Known Issues ------------ - No printing support. - No typing support using the OS virtual keyboard. iOS needs to be signalled to bring up the virtual keyboard when the text tool is active. We also may need to do some finagling to make IM work with the virtual keyboard. - OS language detection doesn't work yet. - Quitting doesn't close the app. It just displays a black screen until it is force-closed. - Need to include cross-compilation instructions. Possible Issues --------------- - No text display. This is likely an issue with how pango and related libraries were cross-compiled rather than an issue with Tux Paint code. From the error output it appears to be a font rendering issue. - SVG integration couldn't be tested because RSVG library has not yet be cross-compiled successfully. - Only tested under the iOS Simulator (and not on an actual iOS device yet.)
1680 lines
39 KiB
C
1680 lines
39 KiB
C
/*
|
|
fonts.c
|
|
|
|
Copyright (c) 2009-2020
|
|
http://www.tuxpaint.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
(See COPYING.txt)
|
|
|
|
$Id$
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#ifndef __USE_GNU
|
|
#define __USE_GNU /* for strcasestr() */
|
|
#endif
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <locale.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <libintl.h>
|
|
#ifndef gettext_noop
|
|
#define gettext_noop(String) String
|
|
#endif
|
|
|
|
/*
|
|
The following section renames global variables defined in SDL_Pango.h to avoid errors during linking.
|
|
It is okay to rename these variables because they are constants.
|
|
SDL_Pang.h is included by fonts.h.
|
|
*/
|
|
#define _MATRIX_WHITE_BACK _MATRIX_WHITE_BACK2
|
|
#define MATRIX_WHITE_BACK MATRIX_WHITE_BACK2
|
|
#define _MATRIX_BLACK_BACK _MATRIX_BLACK_BACK2
|
|
#define MATRIX_BLACK_BACK MATRIX_BLACK_BACK2
|
|
#define _MATRIX_TRANSPARENT_BACK_BLACK_LETTER _MATRIX_TRANSPARENT_BACK_BLACK_LETTER2
|
|
#define MATRIX_TRANSPARENT_BACK_BLACK_LETTER MATRIX_TRANSPARENT_BACK_BLACK_LETTER2
|
|
#define _MATRIX_TRANSPARENT_BACK_WHITE_LETTER _MATRIX_TRANSPARENT_BACK_WHITE_LETTER2
|
|
#define MATRIX_TRANSPARENT_BACK_WHITE_LETTER MATRIX_TRANSPARENT_BACK_WHITE_LETTER2
|
|
#define _MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER _MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER2
|
|
#define MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER MATRIX_TRANSPARENT_BACK_TRANSPARENT_LETTER2
|
|
/*
|
|
The renaming ends here.
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#include "fonts.h"
|
|
#include "i18n.h"
|
|
#include "progressbar.h"
|
|
#include "dirwalk.h"
|
|
#include "get_fname.h"
|
|
#include "debug.h"
|
|
|
|
#ifdef WIN32
|
|
#include "win32_print.h"
|
|
#endif
|
|
|
|
#if defined(__MACOS__)
|
|
#include "macos.h"
|
|
#elif defined(__IOS__)
|
|
#include "ios.h"
|
|
#endif
|
|
|
|
#ifdef __HAIKU__
|
|
#include <FindDirectory.h>
|
|
#include <fs_info.h>
|
|
#endif
|
|
|
|
/* system fonts that cause TTF_OpenFont to crash */
|
|
static const char *problemFonts[] = {
|
|
"/Library/Fonts//AppleMyungjo.ttf",
|
|
NULL
|
|
};
|
|
|
|
/* font types that cause TTF_OpenFont to crash */
|
|
static const char *problemFontExtensions[] = {
|
|
".pfb", /* Ubuntu 14.04 (libsdl-ttf2.0-0 2.0.11-3, libfreetype6 2.5.2-1ubuntu2) -bjk 2014.04.19 */
|
|
NULL
|
|
};
|
|
|
|
#ifdef FORKED_FONTS
|
|
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/wait.h>
|
|
|
|
#ifdef _POSIX_PRIORITY_SCHEDULING
|
|
#include <sched.h>
|
|
#else
|
|
#define sched_yield()
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
#include <sys/prctl.h>
|
|
#else
|
|
#define prctl(o,a1)
|
|
#define PR_SET_PDEATHSIG 0
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifndef FORKED_FONTS
|
|
SDL_Thread *font_thread;
|
|
#endif
|
|
|
|
#ifndef NO_SDLPANGO
|
|
|
|
#include "SDL_Pango.h"
|
|
#if !defined(SDL_PANGO_H)
|
|
#error "---------------------------------------------------"
|
|
#error "If you installed SDL_Pango from a package, be sure"
|
|
#error "to get the development package, as well!"
|
|
#error "(e.g., 'libsdl-pango1-dev.rpm')"
|
|
#error "---------------------------------------------------"
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef FORKED_FONTS
|
|
int no_system_fonts;
|
|
#else
|
|
int no_system_fonts = 1;
|
|
#endif
|
|
int all_locale_fonts;
|
|
volatile long font_thread_done;
|
|
volatile long font_thread_aborted;
|
|
volatile long waiting_for_fonts;
|
|
static int font_scanner_pid;
|
|
int font_socket_fd;
|
|
|
|
TuxPaint_Font *medium_font, *small_font, *large_font, *locale_font;
|
|
|
|
family_info **user_font_families;
|
|
int num_font_families;
|
|
static int num_font_families_max;
|
|
|
|
style_info **user_font_styles;
|
|
int num_font_styles;
|
|
int num_font_styles_max;
|
|
|
|
int text_state;
|
|
unsigned text_size = 4; // initial text size
|
|
|
|
int button_label_y_nudge;
|
|
|
|
#ifdef FORKED_FONTS
|
|
static void reliable_read(int fd, void *buf, size_t count);
|
|
#endif
|
|
|
|
|
|
#ifdef NO_SDLPANGO
|
|
TuxPaint_Font *load_locale_font(TuxPaint_Font * fallback, int size)
|
|
{
|
|
TuxPaint_Font *ret = NULL;
|
|
|
|
if (!need_own_font)
|
|
{
|
|
return fallback;
|
|
}
|
|
else
|
|
{
|
|
char str[128];
|
|
|
|
snprintf(str, sizeof(str), "%sfonts/locale/%s.ttf", DATA_PREFIX, lang_prefix);
|
|
|
|
ret = TuxPaint_Font_OpenFont("", str, size);
|
|
|
|
#ifdef __APPLE__
|
|
if (!ret)
|
|
{
|
|
snprintf(str, sizeof(str), "%sfonts/%s.ttf", DATA_PREFIX, lang_prefix);
|
|
ret = TuxPaint_Font_OpenFont("", str, size);
|
|
}
|
|
|
|
if (!ret)
|
|
{
|
|
snprintf(str, sizeof(str), "/Library/Fonts/%s.ttf", lang_prefix);
|
|
ret = TuxPaint_Font_OpenFont("", str, size);
|
|
}
|
|
|
|
if (!ret)
|
|
{
|
|
snprintf(str, sizeof(str), "%s/%s.ttf", apple_fontsPath(), lang_prefix);
|
|
ret = TuxPaint_Font_OpenFont("", str, size);
|
|
}
|
|
#endif
|
|
|
|
/* Not sure why this is like this; we're in a function that's not
|
|
even defined, unless NO_SDLPANGO is set, so this can't happen
|
|
-bjk 2017.10.15 */
|
|
/*
|
|
#ifndef NO_SDLPANGO
|
|
if (!ret)
|
|
{
|
|
ret = try_alternate_font(size);
|
|
if (!ret)
|
|
{
|
|
fprintf(stderr,
|
|
"\nWarning: Can't load font for this locale:\n"
|
|
"%s\n"
|
|
"The Simple DirectMedia Layer error that occurred was:\n"
|
|
"%s\n\n" "Will use default (American English) instead.\n\n", str, SDL_GetError());
|
|
button_label_y_nudge = smash_i18n();
|
|
}
|
|
}
|
|
#endif
|
|
*/
|
|
return ret ? ret : fallback;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void TuxPaint_Font_CloseFont(TuxPaint_Font * tpf)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_CloseFont step 1 (%p)\n", tpf); //EP
|
|
#endif
|
|
if (!tpf)
|
|
return; //EP
|
|
|
|
#ifndef NO_SDLPANGO
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_CloseFont step 2 (%p, %d)\n", tpf->pango_context, tpf->typ); //EP
|
|
#endif
|
|
if (tpf->typ == FONT_TYPE_PANGO)
|
|
if (tpf->pango_context) //EP
|
|
{
|
|
#ifndef __APPLE__ //EP added ifdef because SDLPango_FreeContext sometimes crashed with "pointer being freed was not allocated"
|
|
SDLPango_FreeContext(tpf->pango_context);
|
|
#endif
|
|
tpf->pango_context = NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_CloseFont step 3 (%p, %d)\n", tpf->ttf_font, tpf->typ); //EP
|
|
fflush(stdout);
|
|
#endif
|
|
if (tpf->typ == FONT_TYPE_TTF)
|
|
if (tpf->ttf_font) //EP
|
|
{
|
|
TTF_CloseFont(tpf->ttf_font);
|
|
tpf->ttf_font = NULL;
|
|
}
|
|
|
|
if (tpf->desc != NULL)
|
|
{
|
|
free(tpf->desc);
|
|
tpf->desc = NULL;
|
|
}
|
|
|
|
free(tpf);
|
|
}
|
|
|
|
TuxPaint_Font *TuxPaint_Font_OpenFont(const char *pangodesc, const char *ttffilename, int size)
|
|
{
|
|
TuxPaint_Font *tpf = NULL;
|
|
int i;
|
|
|
|
#ifndef NO_SDLPANGO
|
|
char desc[1024];
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
printf("OpenFont(pango:\"%s\", ttf:\"%s\")\n", pangodesc, ttffilename);
|
|
#endif
|
|
|
|
#ifndef NO_SDLPANGO
|
|
|
|
if (pangodesc != NULL && pangodesc[0] != '\0')
|
|
{
|
|
tpf = (TuxPaint_Font *) malloc(sizeof(TuxPaint_Font));
|
|
tpf->typ = FONT_TYPE_PANGO;
|
|
snprintf(desc, sizeof(desc), "%s %d", pangodesc, (size * 3) / 4);
|
|
tpf->desc = strdup(desc);
|
|
|
|
#ifdef DEBUG
|
|
printf("Creating context: \"%s\"\n", desc);
|
|
#endif
|
|
|
|
tpf->pango_context = SDLPango_CreateContext_GivenFontDesc(desc);
|
|
if (tpf->pango_context == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("Failed to load %s\n", desc);
|
|
#endif
|
|
free(tpf);
|
|
tpf = NULL;
|
|
}
|
|
else
|
|
tpf->height = size; /* FIXME: Is this accurate!? -bjk 2007.07.12 */
|
|
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_OpenFont() done\n");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
return (tpf);
|
|
}
|
|
#endif
|
|
|
|
if (ttffilename != NULL && ttffilename[0] != '\0')
|
|
{
|
|
#ifdef DEBUG
|
|
printf("Opening TTF\n");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
i = 0;
|
|
while (problemFonts[i] != NULL)
|
|
{
|
|
if (!strcmp(ttffilename, problemFonts[i++]))
|
|
return NULL; /* bail on known problematic fonts that cause TTF_OpenFont to crash */
|
|
}
|
|
|
|
i = 0;
|
|
while (problemFontExtensions[i] != NULL)
|
|
{
|
|
if (strstr(ttffilename, problemFontExtensions[i++]))
|
|
return NULL; /* bail on known problematic font types that cause TTF_OpenFont to crash */
|
|
}
|
|
|
|
tpf = (TuxPaint_Font *) malloc(sizeof(TuxPaint_Font));
|
|
tpf->typ = FONT_TYPE_TTF;
|
|
tpf->ttf_font = TTF_OpenFont(ttffilename, size);
|
|
tpf->desc = strdup(ttffilename);
|
|
|
|
#ifdef DEBUG
|
|
printf("Loaded %s: %d->%d\n", ttffilename, tpf, tpf->ttf_font);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
if (tpf->ttf_font == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("Failed to load %s: %s\n", ttffilename, SDL_GetError());
|
|
#endif
|
|
free(tpf);
|
|
tpf = NULL;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
printf("Succeeded loading %s\n", ttffilename);
|
|
#endif
|
|
tpf->height = TTF_FontHeight(tpf->ttf_font);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_OpenFont() done\n");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
return (tpf);
|
|
}
|
|
|
|
|
|
#ifdef FORKED_FONTS
|
|
|
|
void reliable_write(int fd, const void *buf, size_t count)
|
|
{
|
|
struct pollfd p;
|
|
|
|
do
|
|
{
|
|
ssize_t rc = write(fd, buf, count);
|
|
|
|
if (rc == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
default:
|
|
return;
|
|
case EAGAIN:
|
|
case ENOSPC:
|
|
; // satisfy a C syntax abomination
|
|
p = (struct pollfd)
|
|
{
|
|
fd, POLLOUT, 0};
|
|
poll(&p, 1, -1); // try not to burn CPU time
|
|
// FALL THROUGH
|
|
case EINTR:
|
|
continue;
|
|
}
|
|
}
|
|
buf += rc;
|
|
count -= rc;
|
|
}
|
|
while (count);
|
|
}
|
|
|
|
|
|
static void reliable_read(int fd, void *buf, size_t count)
|
|
{
|
|
struct pollfd p;
|
|
|
|
do
|
|
{
|
|
ssize_t rc = read(fd, buf, count);
|
|
|
|
if (rc == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
default:
|
|
return;
|
|
case EAGAIN:
|
|
; // satisfy a C syntax abomination
|
|
p = (struct pollfd)
|
|
{
|
|
fd, POLLIN, 0};
|
|
poll(&p, 1, -1); // try not to burn CPU time
|
|
// FALL THROUGH
|
|
case EINTR:
|
|
continue;
|
|
}
|
|
}
|
|
if (rc == 0)
|
|
break; // EOF. Better not happen before the end!
|
|
buf += rc;
|
|
count -= rc;
|
|
}
|
|
while (count);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void groupfonts_range(style_info ** base, int count)
|
|
{
|
|
int boldcounts[4] = { 0, 0, 0, 0 };
|
|
int boldmap[4] = { -1, -1, -1, -1 };
|
|
int i;
|
|
int boldmax;
|
|
int boldmin;
|
|
int bolduse;
|
|
int spot;
|
|
family_info *fi;
|
|
|
|
#if 0
|
|
// THREADED_FONTS
|
|
if (count < 1 || count > 4)
|
|
{
|
|
printf("\n::::::: %d styles in %s:\n", count, base[0]->family);
|
|
i = count;
|
|
while (i--)
|
|
{
|
|
printf(" %s\n", base[i]->style);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
i = count;
|
|
while (i--)
|
|
boldcounts[base[i]->boldness]++;
|
|
|
|
boldmax = base[0]->boldness;
|
|
boldmin = base[0]->boldness;
|
|
bolduse = 0;
|
|
|
|
i = 4;
|
|
while (i--)
|
|
{
|
|
if (!boldcounts[i])
|
|
continue;
|
|
if (i > boldmax)
|
|
boldmax = i;
|
|
if (i < boldmin)
|
|
boldmin = i;
|
|
bolduse++;
|
|
}
|
|
if (likely(bolduse <= 2))
|
|
{
|
|
// in case they are same, we want non-bold,
|
|
// so that setting goes second
|
|
boldmap[boldmax] = 1;
|
|
boldmap[boldmin] = 0;
|
|
}
|
|
else if (count == 3)
|
|
{
|
|
int boldmid;
|
|
int zmin = 0, zmid = 0, zmax = 0;
|
|
|
|
boldmap[boldmax] = 1;
|
|
boldmap[boldmin] = 0;
|
|
boldmid = boldcounts[boldmin + 1] ? boldmin + 1 : boldmin + 2;
|
|
|
|
i = 3;
|
|
while (i--)
|
|
{
|
|
if (base[i]->boldness == boldmin)
|
|
zmin = base[i]->italic;
|
|
if (base[i]->boldness == boldmid)
|
|
zmid = base[i]->italic;
|
|
if (base[i]->boldness == boldmax)
|
|
zmax = base[i]->italic;
|
|
}
|
|
if (zmin != zmid)
|
|
boldmap[boldmid] = 0;
|
|
else if (zmid != zmax)
|
|
boldmap[boldmid] = 1;
|
|
else if (boldmin == 0 && boldmid == 1)
|
|
{
|
|
boldmap[0] = -1;
|
|
boldmap[1] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int claimed_bold = boldcounts[3];
|
|
int claimed_norm = boldcounts[1];
|
|
|
|
// 3 or 4 boldness levels, 4 or more styles!
|
|
// This is going to be random hacks and hopes.
|
|
|
|
// bold is bold
|
|
boldmap[3] = 1;
|
|
|
|
// norm is norm
|
|
boldmap[1] = 0;
|
|
|
|
// classify demi-bold or medium
|
|
if (claimed_bold < 2)
|
|
{
|
|
boldmap[2] = 1;
|
|
claimed_bold += boldcounts[2];
|
|
}
|
|
else if (claimed_norm < 2)
|
|
{
|
|
boldmap[2] = 0;
|
|
claimed_norm += boldcounts[2];
|
|
}
|
|
|
|
// classify lightface
|
|
if (claimed_norm < 2)
|
|
{
|
|
boldmap[0] = 0;
|
|
//claimed_norm += boldcounts[0];
|
|
}
|
|
}
|
|
|
|
if (num_font_families == num_font_families_max)
|
|
{
|
|
num_font_families_max = num_font_families_max * 5 / 4 + 30;
|
|
user_font_families = realloc(user_font_families, num_font_families_max * sizeof *user_font_families);
|
|
}
|
|
|
|
fi = calloc(1, sizeof *fi);
|
|
user_font_families[num_font_families++] = fi;
|
|
fi->directory = strdup(base[0]->directory);
|
|
fi->family = strdup(base[0]->family);
|
|
fi->score = base[0]->truetype + base[0]->score;
|
|
i = count;
|
|
while (i--)
|
|
{
|
|
int b = boldmap[base[i]->boldness];
|
|
|
|
if (b == -1)
|
|
{
|
|
#if 0
|
|
// THREADED_FONTS
|
|
printf("too many boldness levels, discarding: %s, %s\n", base[i]->family, base[i]->style);
|
|
#endif
|
|
continue;
|
|
}
|
|
spot = b ? TTF_STYLE_BOLD : 0;
|
|
spot += base[i]->italic ? TTF_STYLE_ITALIC : 0;
|
|
if (fi->filename[spot])
|
|
{
|
|
#if 0
|
|
// THREADED_FONTS
|
|
printf("duplicates, discarding: %s, %s\n", base[i]->family, base[i]->style);
|
|
printf("b %d, spot %d\n", b, spot);
|
|
printf("occupancy %p %p %p %p\n", fi->filename[0], fi->filename[1], fi->filename[2], fi->filename[3]);
|
|
#endif
|
|
continue;
|
|
}
|
|
fi->filename[spot] = strdup(base[i]->filename);
|
|
fi->score += 2;
|
|
}
|
|
if (!fi->filename[0] && !fi->filename[1])
|
|
{
|
|
fi->filename[0] = fi->filename[2];
|
|
fi->filename[2] = NULL;
|
|
fi->filename[1] = fi->filename[3];
|
|
fi->filename[3] = NULL;
|
|
}
|
|
if (!fi->filename[0] && !fi->filename[2])
|
|
{
|
|
fi->filename[0] = fi->filename[1];
|
|
fi->filename[1] = NULL;
|
|
fi->filename[2] = fi->filename[3];
|
|
fi->filename[3] = NULL;
|
|
}
|
|
if (!fi->filename[0])
|
|
{
|
|
fi->filename[0] = strdup(fi->filename[TTF_STYLE_BOLD]);
|
|
}
|
|
}
|
|
|
|
|
|
// void qsort(void *base, size_t nmemb, size_t size,
|
|
// int(*compar)(const void *, const void *));
|
|
|
|
|
|
// For qsort() and other use, to see if font files are groupable
|
|
static int compar_fontgroup(const void *v1, const void *v2)
|
|
{
|
|
const style_info *s1 = *(style_info **) v1;
|
|
const style_info *s2 = *(style_info **) v2;
|
|
int rc;
|
|
|
|
rc = strcmp(s1->directory, s2->directory);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = s1->truetype - s2->truetype;
|
|
if (rc)
|
|
return rc;
|
|
|
|
return strcmp(s1->family, s2->family);
|
|
}
|
|
|
|
|
|
// For qsort() and other use, to see if font files are duplicates
|
|
static int compar_fontkiller(const void *v1, const void *v2)
|
|
{
|
|
const family_info *f1 = *(family_info **) v1;
|
|
const family_info *f2 = *(family_info **) v2;
|
|
int rc;
|
|
|
|
rc = strcmp(f1->family, f2->family);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return f1->score - f2->score;
|
|
}
|
|
|
|
|
|
// For qsort() and other use, to order the worst ones last
|
|
static int compar_fontscore(const void *v1, const void *v2)
|
|
{
|
|
const family_info *f1 = *(family_info **) v1;
|
|
const family_info *f2 = *(family_info **) v2;
|
|
|
|
return f2->score - f1->score;
|
|
}
|
|
|
|
|
|
// Font style names are a mess that we must try to make
|
|
// sense of. For example...
|
|
//
|
|
// Cooper: Light, Medium, Light Bold, Black
|
|
// HoeflerText: (nil), Black
|
|
static void parse_font_style(style_info * si)
|
|
{
|
|
int have_light = 0;
|
|
int have_demi = 0;
|
|
int have_bold = 0;
|
|
int have_medium = 0;
|
|
int have_black = 0;
|
|
int have_heavy = 0;
|
|
|
|
int stumped = 0;
|
|
char *sp = si->style;
|
|
|
|
si->italic = 0;
|
|
|
|
|
|
while (*sp)
|
|
{
|
|
if (*sp == ' ')
|
|
{
|
|
sp++;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Bold", strlen("Bold")))
|
|
{
|
|
sp += strlen("Bold");
|
|
have_bold = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Regular", strlen("Regular")))
|
|
{
|
|
sp += strlen("Regular");
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Italic", strlen("Italic")))
|
|
{
|
|
sp += strlen("Italic");
|
|
si->italic = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Oblique", strlen("Oblique")))
|
|
{
|
|
sp += strlen("Oblique");
|
|
si->italic = 1;
|
|
continue;
|
|
}
|
|
// move " Condensed" from style to family
|
|
if (!strncasecmp(sp, "Condensed", strlen("Condensed")))
|
|
{
|
|
size_t len = strlen(si->family);
|
|
char *name = malloc(len + strlen(" Condensed") + 1);
|
|
|
|
sp += strlen("Condensed");
|
|
memcpy(name, si->family, len);
|
|
strcpy(name + len, " Condensed");
|
|
free(si->family);
|
|
si->family = name;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Light", strlen("Light")))
|
|
{
|
|
sp += strlen("Light");
|
|
have_light = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Medium", strlen("Medium")))
|
|
{
|
|
sp += strlen("Medium");
|
|
have_medium = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Demi", strlen("Demi")))
|
|
{
|
|
sp += strlen("Demi");
|
|
have_demi = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Heavy", strlen("Heavy")))
|
|
{
|
|
sp += strlen("Heavy");
|
|
have_heavy = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Normal", strlen("Normal")))
|
|
{
|
|
sp += strlen("Normal");
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Black", strlen("Black")))
|
|
{
|
|
sp += strlen("Black");
|
|
have_black = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Roman", strlen("Roman")))
|
|
{
|
|
sp += strlen("Roman");
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Book", strlen("Book")))
|
|
{
|
|
sp += strlen("Book");
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Chancery", strlen("Chancery")))
|
|
{
|
|
sp += strlen("Chancery");
|
|
si->italic = 1;
|
|
continue;
|
|
}
|
|
if (!strncasecmp(sp, "Thin", strlen("Thin")))
|
|
{
|
|
sp += strlen("Thin");
|
|
have_light = 1;
|
|
continue;
|
|
}
|
|
if (!strncmp(sp, "LR", strlen("LR")))
|
|
{
|
|
sp += strlen("LR");
|
|
continue;
|
|
}
|
|
|
|
if (!stumped)
|
|
{
|
|
stumped = 1;
|
|
#if 0
|
|
// THREADED_FONTS
|
|
printf("Font style parser stumped by \"%s\".\n", si->style);
|
|
#endif
|
|
}
|
|
sp++; // bad: an unknown character
|
|
}
|
|
|
|
|
|
if (have_demi || have_medium)
|
|
si->boldness = 2;
|
|
else if (have_bold || have_black || have_heavy) // TODO: split these
|
|
si->boldness = 3;
|
|
else if (have_light)
|
|
si->boldness = 0;
|
|
else
|
|
si->boldness = 1;
|
|
|
|
// we'll count both TrueType and OpenType
|
|
si->truetype = ! !strcasestr(si->filename, ".ttf") || ! !strcasestr(si->filename, ".otf");
|
|
}
|
|
|
|
|
|
static void dupe_markdown_range(family_info ** base, int count)
|
|
{
|
|
int bestscore = -999;
|
|
int bestslot = 0;
|
|
int i = count;
|
|
|
|
while (i--)
|
|
{
|
|
int score = base[i]->score;
|
|
|
|
if (score <= bestscore)
|
|
continue;
|
|
bestscore = score;
|
|
bestslot = i;
|
|
}
|
|
i = count;
|
|
while (i--)
|
|
{
|
|
if (i == bestslot)
|
|
continue;
|
|
base[i]->score = -999;
|
|
}
|
|
}
|
|
|
|
|
|
static void groupfonts(void)
|
|
{
|
|
char **cpp;
|
|
int i = num_font_styles;
|
|
int low = 0;
|
|
|
|
while (i--)
|
|
parse_font_style(user_font_styles[i]);
|
|
|
|
qsort(user_font_styles, num_font_styles, sizeof user_font_styles[0], compar_fontgroup);
|
|
//printf("groupfonts() qsort(user_font_styles...)\n");
|
|
//fflush(stdout);
|
|
|
|
for (;;)
|
|
{
|
|
int high = low;
|
|
|
|
if (low >= num_font_styles)
|
|
break;
|
|
for (;;)
|
|
{
|
|
if (++high >= num_font_styles)
|
|
break;
|
|
if (compar_fontgroup(user_font_styles + low, user_font_styles + high))
|
|
break;
|
|
}
|
|
groupfonts_range(user_font_styles + low, high - low);
|
|
low = high;
|
|
}
|
|
|
|
i = num_font_styles;
|
|
while (i--)
|
|
{
|
|
free(user_font_styles[i]->filename);
|
|
free(user_font_styles[i]->directory);
|
|
free(user_font_styles[i]->family);
|
|
free(user_font_styles[i]->style);
|
|
free(user_font_styles[i]);
|
|
}
|
|
free(user_font_styles);
|
|
user_font_styles = NULL; // just to catch bugs
|
|
|
|
qsort(user_font_families, num_font_families, sizeof user_font_families[0], compar_fontkiller);
|
|
low = 0;
|
|
for (;;)
|
|
{
|
|
int high = low;
|
|
|
|
if (low >= num_font_families)
|
|
break;
|
|
for (;;)
|
|
{
|
|
if (++high >= num_font_families)
|
|
break;
|
|
if (strcmp(user_font_families[low]->family, user_font_families[high]->family))
|
|
break;
|
|
}
|
|
dupe_markdown_range(user_font_families + low, high - low);
|
|
low = high;
|
|
}
|
|
qsort(user_font_families, num_font_families, sizeof user_font_families[0], compar_fontscore);
|
|
//printf("groupfonts() qsort(user_font_families 2...)\n");
|
|
//fflush(stdout);
|
|
if (num_font_families > 0 && user_font_families[0]->score < 0)
|
|
fprintf(stderr, "sorted the wrong way, or all fonts were unusable\n");
|
|
#if 0
|
|
// THREADED_FONTS
|
|
printf("Trim starting with %d families\n", num_font_families);
|
|
#endif
|
|
while (num_font_families > 1 && user_font_families[num_font_families - 1]->score < 0)
|
|
{
|
|
i = --num_font_families;
|
|
free(user_font_families[i]->directory);
|
|
free(user_font_families[i]->family);
|
|
cpp = user_font_families[i]->filename;
|
|
if (cpp[0])
|
|
free(cpp[0]);
|
|
if (cpp[1])
|
|
free(cpp[1]);
|
|
if (cpp[2])
|
|
free(cpp[2]);
|
|
if (cpp[3])
|
|
free(cpp[3]);
|
|
free(user_font_families[i]);
|
|
user_font_families[i] = NULL;
|
|
}
|
|
#if 0
|
|
// THREADED_FONTS
|
|
printf("Trim ending with %d families\n", num_font_families);
|
|
#endif
|
|
}
|
|
|
|
|
|
static void loadfonts_locale_filter(SDL_Surface * screen, const char *const dir, const char *restrict const locale)
|
|
{
|
|
char buf[TP_FTW_PATHSIZE];
|
|
unsigned dirlen = strlen(dir);
|
|
|
|
memcpy(buf, dir, dirlen);
|
|
tp_ftw(screen, buf, dirlen, 1, loadfont_callback, locale);
|
|
}
|
|
|
|
static void loadfonts(SDL_Surface * screen, const char *const dir)
|
|
{
|
|
loadfonts_locale_filter(screen, dir, NULL);
|
|
}
|
|
|
|
|
|
/* static */ int load_user_fonts(SDL_Surface * screen, void *vp, const char *restrict const locale)
|
|
{
|
|
char *homedirdir;
|
|
|
|
(void)vp; // junk passed by threading library
|
|
|
|
loadfonts_locale_filter(screen, DATA_PREFIX "fonts", locale);
|
|
|
|
if (!no_system_fonts)
|
|
{
|
|
#ifdef WIN32
|
|
homedirdir = GetSystemFontDir();
|
|
loadfonts(screen, homedirdir);
|
|
free(homedirdir);
|
|
#elif defined(__BEOS__)
|
|
loadfonts(screen, "/boot/home/config/font/ttffonts");
|
|
loadfonts(screen, "/usr/share/fonts");
|
|
loadfonts(screen, "/usr/X11R6/lib/X11/fonts");
|
|
#elif defined(__HAIKU__)
|
|
dev_t volume = dev_for_path("/boot");
|
|
char buffer[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
|
|
status_t result;
|
|
|
|
result = find_directory(B_SYSTEM_FONTS_DIRECTORY, volume, false, buffer, sizeof(buffer));
|
|
loadfonts(screen, buffer);
|
|
result = find_directory(B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY, volume, false, buffer, sizeof(buffer));
|
|
loadfonts(screen, buffer);
|
|
result = find_directory(B_USER_FONTS_DIRECTORY, volume, false, buffer, sizeof(buffer));
|
|
loadfonts(screen, buffer);
|
|
result = find_directory(B_USER_NONPACKAGED_FONTS_DIRECTORY, volume, false, buffer, sizeof(buffer));
|
|
loadfonts(screen, buffer);
|
|
#elif defined(__APPLE__)
|
|
loadfonts(screen, "/System/Library/Fonts");
|
|
loadfonts(screen, "/Library/Fonts");
|
|
loadfonts(screen, apple_fontsPath());
|
|
loadfonts(screen, "/usr/share/fonts");
|
|
loadfonts(screen, "/usr/X11R6/lib/X11/fonts");
|
|
#elif defined(__sun__)
|
|
loadfonts(screen, "/usr/openwin/lib/X11/fonts");
|
|
loadfonts(screen, "/usr/share/fonts");
|
|
loadfonts(screen, "/usr/X11R6/lib/X11/fonts");
|
|
#else
|
|
loadfonts(screen, "/usr/share/feh/fonts");
|
|
loadfonts(screen, "/usr/share/fonts");
|
|
loadfonts(screen, "/usr/X11R6/lib/X11/fonts");
|
|
loadfonts(screen, "/usr/share/texmf/fonts");
|
|
loadfonts(screen, "/usr/share/grace/fonts/type1");
|
|
loadfonts(screen, "/usr/share/hatman/fonts");
|
|
loadfonts(screen, "/usr/share/icewm/themes/jim-mac");
|
|
loadfonts(screen, "/usr/share/vlc/skins2/fonts");
|
|
loadfonts(screen, "/usr/share/xplanet/fonts");
|
|
#endif
|
|
}
|
|
|
|
homedirdir = get_fname("fonts", DIR_DATA);
|
|
loadfonts(screen, homedirdir);
|
|
free(homedirdir);
|
|
|
|
#ifdef WIN32
|
|
homedirdir = get_fname("data/fonts", DIR_DATA);
|
|
loadfonts(screen, homedirdir);
|
|
free(homedirdir);
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
printf("Grouping fonts...\n");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
groupfonts();
|
|
|
|
#ifdef DEBUG
|
|
printf("Finished loading the fonts\n");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
font_thread_done = 1;
|
|
waiting_for_fonts = 0;
|
|
// FIXME: need a memory barrier here
|
|
return 0; // useless, wanted by threading library
|
|
}
|
|
|
|
|
|
#ifdef FORKED_FONTS
|
|
|
|
void run_font_scanner(SDL_Surface * screen, const char *restrict const locale)
|
|
{
|
|
int sv[2];
|
|
int size, i;
|
|
char *buf, *walk;
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
|
|
exit(42);
|
|
font_scanner_pid = fork();
|
|
if (font_scanner_pid)
|
|
{
|
|
// parent (or error -- but we're screwed in that case)
|
|
font_socket_fd = sv[0];
|
|
close(sv[1]);
|
|
return;
|
|
}
|
|
#ifndef __HAIKU__
|
|
/* be nice, letting the main thread get the CPU */
|
|
if (nice(42) == -1)
|
|
{
|
|
fprintf(stderr, "Font scanner thread can't nice() itself\n");
|
|
}
|
|
#endif
|
|
sched_yield(); // try to let the parent run right now
|
|
prctl(PR_SET_PDEATHSIG, 9); // get killed if parent exits
|
|
if (getppid() == 1)
|
|
_exit(99); // parent is already init, and won't be dying :-)
|
|
font_socket_fd = sv[1];
|
|
close(sv[0]);
|
|
progress_bar_disabled = 1;
|
|
reliable_read(font_socket_fd, &no_system_fonts, sizeof no_system_fonts);
|
|
sched_yield(); // try to let the parent run right now
|
|
SDL_Init(SDL_INIT_NOPARACHUTE);
|
|
TTF_Init();
|
|
load_user_fonts(screen, NULL, locale);
|
|
|
|
size = 0;
|
|
i = num_font_families;
|
|
while (i--)
|
|
{
|
|
char *s;
|
|
|
|
s = user_font_families[i]->directory;
|
|
if (s)
|
|
size += strlen(s);
|
|
s = user_font_families[i]->family;
|
|
if (s)
|
|
size += strlen(s);
|
|
s = user_font_families[i]->filename[0];
|
|
if (s)
|
|
size += strlen(s);
|
|
s = user_font_families[i]->filename[1];
|
|
if (s)
|
|
size += strlen(s);
|
|
s = user_font_families[i]->filename[2];
|
|
if (s)
|
|
size += strlen(s);
|
|
s = user_font_families[i]->filename[3];
|
|
if (s)
|
|
size += strlen(s);
|
|
size += 6; // for '\0' on each of the above
|
|
}
|
|
size += 2; // for 2-byte font count
|
|
buf = malloc(size);
|
|
walk = buf;
|
|
#ifdef DEBUG
|
|
printf("Sending %u bytes with %u families.\n", size, num_font_families);
|
|
#endif
|
|
*walk++ = num_font_families & 0xffu;
|
|
*walk++ = num_font_families >> 8u;
|
|
i = num_font_families;
|
|
while (i--)
|
|
{
|
|
int len;
|
|
char *s;
|
|
|
|
s = user_font_families[i]->directory;
|
|
if (s)
|
|
{
|
|
len = strlen(s);
|
|
memcpy(walk, s, len);
|
|
walk += len;
|
|
}
|
|
*walk++ = '\0';
|
|
|
|
s = user_font_families[i]->family;
|
|
if (s)
|
|
{
|
|
len = strlen(s);
|
|
memcpy(walk, s, len);
|
|
walk += len;
|
|
}
|
|
*walk++ = '\0';
|
|
|
|
s = user_font_families[i]->filename[0];
|
|
if (s)
|
|
{
|
|
len = strlen(s);
|
|
memcpy(walk, s, len);
|
|
walk += len;
|
|
}
|
|
*walk++ = '\0';
|
|
|
|
s = user_font_families[i]->filename[1];
|
|
if (s)
|
|
{
|
|
len = strlen(s);
|
|
memcpy(walk, s, len);
|
|
walk += len;
|
|
}
|
|
*walk++ = '\0';
|
|
|
|
s = user_font_families[i]->filename[2];
|
|
if (s)
|
|
{
|
|
len = strlen(s);
|
|
memcpy(walk, s, len);
|
|
walk += len;
|
|
}
|
|
*walk++ = '\0';
|
|
|
|
s = user_font_families[i]->filename[3];
|
|
if (s)
|
|
{
|
|
len = strlen(s);
|
|
memcpy(walk, s, len);
|
|
walk += len;
|
|
}
|
|
*walk++ = '\0';
|
|
}
|
|
reliable_write(font_socket_fd, buf, size);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
void receive_some_font_info(SDL_Surface * screen)
|
|
{
|
|
char *buf = NULL;
|
|
unsigned buf_size = 0;
|
|
unsigned buf_fill = 0;
|
|
ssize_t rc;
|
|
struct pollfd p;
|
|
int status;
|
|
/* unsigned */ char *walk;
|
|
unsigned i;
|
|
family_info *fip;
|
|
|
|
fcntl(font_socket_fd, F_SETFL, O_NONBLOCK);
|
|
for (;;)
|
|
{
|
|
if (buf_size <= buf_fill * 9 / 8 + 128)
|
|
{
|
|
buf_size = buf_size * 5 / 4 + 256;
|
|
|
|
// FIXME: Valgrind says this leaks -bjk 2007.07.19
|
|
buf = realloc(buf, buf_size);
|
|
}
|
|
rc = read(font_socket_fd, buf + buf_fill, buf_size - buf_fill);
|
|
#ifdef DEBUG
|
|
printf("read: fd=%d buf_fill=%u buf_size=%u rc=%ld\n", font_socket_fd, buf_fill, buf_size, (long int)rc);
|
|
#endif
|
|
|
|
if (rc == -1)
|
|
{
|
|
switch (errno)
|
|
{
|
|
default:
|
|
return;
|
|
case EAGAIN:
|
|
; // satisfy a C syntax abomination
|
|
p = (struct pollfd)
|
|
{
|
|
font_socket_fd, POLLIN, 0};
|
|
show_progress_bar(screen);
|
|
poll(&p, 1, 29); // try not to burn CPU time
|
|
continue;
|
|
case EINTR:
|
|
continue;
|
|
}
|
|
}
|
|
buf_fill += rc;
|
|
if (!rc || font_thread_aborted)
|
|
break;
|
|
}
|
|
close(font_socket_fd);
|
|
|
|
waitpid(font_scanner_pid, &status, 0);
|
|
if (WIFSIGNALED(status) || font_thread_aborted)
|
|
{
|
|
fprintf(stderr, "child killed by signal %u\n", WTERMSIG(status));
|
|
user_font_families = NULL;
|
|
num_font_families = 0;
|
|
font_thread_done = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
show_progress_bar(screen);
|
|
walk = buf;
|
|
num_font_families = *(unsigned char *)walk++;
|
|
num_font_families += *(unsigned char *)walk++ << 8u;
|
|
#ifdef DEBUG
|
|
printf("Got %u bytes with %u families.\n", buf_fill, num_font_families);
|
|
#endif
|
|
user_font_families = malloc(num_font_families * sizeof *user_font_families);
|
|
|
|
// FIXME: Valgrind says this malloc() is leaked -bjk 2007.07.19
|
|
fip = malloc(num_font_families * sizeof **user_font_families);
|
|
|
|
i = num_font_families;
|
|
while (i--)
|
|
{
|
|
unsigned len;
|
|
|
|
user_font_families[i] = fip + i;
|
|
|
|
len = strlen(walk);
|
|
user_font_families[i]->directory = len ? walk : NULL;
|
|
walk += len + 1;
|
|
|
|
len = strlen(walk);
|
|
user_font_families[i]->family = len ? walk : NULL;
|
|
walk += len + 1;
|
|
|
|
len = strlen(walk);
|
|
user_font_families[i]->filename[0] = len ? walk : NULL;
|
|
walk += len + 1;
|
|
|
|
len = strlen(walk);
|
|
user_font_families[i]->filename[1] = len ? walk : NULL;
|
|
walk += len + 1;
|
|
|
|
len = strlen(walk);
|
|
user_font_families[i]->filename[2] = len ? walk : NULL;
|
|
walk += len + 1;
|
|
|
|
len = strlen(walk);
|
|
user_font_families[i]->filename[3] = len ? walk : NULL;
|
|
walk += len + 1;
|
|
|
|
user_font_families[i]->handle = NULL;
|
|
|
|
// score left uninitialized
|
|
}
|
|
font_thread_done = 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
TuxPaint_Font *getfonthandle(int desire)
|
|
{
|
|
int missing = 0;
|
|
family_info *fi = user_font_families[desire];
|
|
char *name;
|
|
char *pathname;
|
|
char description[1024];
|
|
|
|
#ifdef DEBUG
|
|
printf("\ngetfonthandle(%d)...\n", desire);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
if (fi == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("getfonthandle(%d) points to a NULL family\n", desire);
|
|
fflush(stdout);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
if (fi->filename != NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("Setting 'name' to fi->filename[%d (0x%x)]\n", (int)text_state, (int)text_state);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
name = fi->filename[text_state];
|
|
|
|
#ifdef DEBUG
|
|
printf("Which is: %s\n", name);
|
|
fflush(stdout);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG //EP fixed typo: replaced DBEUG with DEBUG
|
|
printf("fi->filename is NULL\n");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
name = NULL;
|
|
}
|
|
|
|
if (fi->handle)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("fi->handle was set (0x%x)\n", (int)(intptr_t) fi->handle); //EP added (intptr_t) to avoid warning on x64
|
|
|
|
fflush(stdout);
|
|
#endif
|
|
return fi->handle;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("fi->handle was not yet set\n");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
|
|
/* FIXME: Doesn't make sense; fi->handle is NULL! -bjk 2007.07.17
|
|
|
|
#ifndef NO_SDLPANGO
|
|
|
|
if (fi->handle->typ == FONT_TYPE_PANGO)
|
|
{
|
|
snprintf(description, sizeof(description), "%s%s%s", fi->family,
|
|
(text_state ^ TTF_STYLE_ITALIC ? " italic" : ""),
|
|
(text_state ^ TTF_STYLE_BOLD ? " bold" : ""));
|
|
|
|
pathname = (char *) "";
|
|
|
|
#ifdef DEBUG
|
|
printf("getfonthandle(%d) asking SDL_Pango for %s\n", desire, description);
|
|
#endif
|
|
}
|
|
#endif
|
|
*/
|
|
|
|
/* FIXME: Doesn't make sense; fi->handle is NULL! -bjk 2007.07.17
|
|
if (fi->handle->typ == FONT_TYPE_TTF)
|
|
*/
|
|
{
|
|
if (!name)
|
|
{
|
|
name = fi->filename[text_state ^ TTF_STYLE_ITALIC];
|
|
missing = text_state & TTF_STYLE_ITALIC;
|
|
}
|
|
if (!name)
|
|
{
|
|
name = fi->filename[text_state ^ TTF_STYLE_BOLD];
|
|
missing = text_state & TTF_STYLE_BOLD;
|
|
}
|
|
if (!name)
|
|
{
|
|
name = fi->filename[text_state ^ (TTF_STYLE_ITALIC | TTF_STYLE_BOLD)];
|
|
missing = text_state & (TTF_STYLE_ITALIC | TTF_STYLE_BOLD);
|
|
}
|
|
if (!name)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("name is still NULL\n");
|
|
fflush(stdout);
|
|
#endif
|
|
return (NULL);
|
|
}
|
|
|
|
pathname = alloca(strlen(fi->directory) + 1 + strlen(name) + 1);
|
|
sprintf(pathname, "%s/%s", fi->directory, name);
|
|
|
|
strcpy(description, "");
|
|
}
|
|
|
|
fi->handle = TuxPaint_Font_OpenFont(description, pathname, text_sizes[text_size]);
|
|
// if the font doesn't load, we die -- it did load OK before though
|
|
|
|
if (fi->handle == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("fi->handle is NULL!\n");
|
|
fflush(stdout);
|
|
#endif
|
|
return (NULL);
|
|
}
|
|
|
|
if (fi->handle->typ == FONT_TYPE_TTF)
|
|
{
|
|
if (fi->handle->ttf_font == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("fi->handle->ttf_font is NULL!\n");
|
|
fflush(stdout);
|
|
#endif
|
|
return (NULL);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("calling TTF_SetFontStyle(0x%x)\n", missing);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
TTF_SetFontStyle(fi->handle->ttf_font, missing);
|
|
}
|
|
|
|
#ifndef NO_SDLPANGO
|
|
if (fi->handle->typ == FONT_TYPE_PANGO)
|
|
printf("It's a Pango context...\n");
|
|
#endif
|
|
|
|
return fi->handle;
|
|
}
|
|
|
|
|
|
// backdoor into qsort operations, so we don't have to do work again
|
|
static int was_bad_font;
|
|
|
|
// see if two font surfaces are the same
|
|
static int do_surfcmp(const SDL_Surface * const *const v1, const SDL_Surface * const *const v2)
|
|
{
|
|
const SDL_Surface *const s1 = *v1;
|
|
const SDL_Surface *const s2 = *v2;
|
|
int width;
|
|
int cmp;
|
|
int i;
|
|
|
|
if (s1 == s2)
|
|
{
|
|
fprintf(stderr, "s1==s2?\n");
|
|
return 0;
|
|
}
|
|
if (!s1 || !s2 || !s1->w || !s2->w || !s1->h || !s2->h || !s1->format || !s2->format)
|
|
{
|
|
was_bad_font = 1;
|
|
return 0;
|
|
}
|
|
if (s1->format->BytesPerPixel != s2->format->BytesPerPixel)
|
|
{
|
|
// something really strange and bad happened
|
|
was_bad_font = 1;
|
|
return s1->format->BytesPerPixel - s2->format->BytesPerPixel;
|
|
}
|
|
|
|
|
|
if (s1->w != s2->w)
|
|
return s1->w - s2->w;
|
|
if (s1->h != s2->h)
|
|
return s1->h - s2->h;
|
|
|
|
{
|
|
const char *const c1 = (char *const)s1->pixels;
|
|
const char *const c2 = (char *const)s2->pixels;
|
|
|
|
width = s1->format->BytesPerPixel * s1->w;
|
|
if (width == s1->pitch)
|
|
return memcmp(c1, c2, width * s1->h);
|
|
cmp = 0;
|
|
i = s1->h;
|
|
while (i--)
|
|
{
|
|
cmp = memcmp(c1 + i * s1->pitch, c2 + i * s2->pitch, width);
|
|
if (cmp)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
|
|
// see if two font surfaces are the same
|
|
static int surfcmp(const void *s1, const void *s2)
|
|
{
|
|
int diff = do_surfcmp(s1, s2);
|
|
|
|
if (!diff)
|
|
was_bad_font = 1;
|
|
return diff;
|
|
}
|
|
|
|
// check if the characters will render distinctly
|
|
int charset_works(TuxPaint_Font * font, const char *s)
|
|
{
|
|
SDL_Color black = { 0, 0, 0, 0 };
|
|
#ifndef NO_SDLPANGO
|
|
SDLPango_Matrix pango_color;
|
|
#endif
|
|
SDL_Surface **surfs = malloc(strlen(s) * sizeof surfs[0]);
|
|
unsigned count = 0;
|
|
int ret = 0;
|
|
|
|
while (*s)
|
|
{
|
|
char c[8];
|
|
unsigned offset = 0;
|
|
SDL_Surface *tmp_surf = NULL;
|
|
|
|
do
|
|
c[offset++] = *s++;
|
|
while ((*s & 0xc0u) == 0x80u); // assume safe input
|
|
c[offset++] = '\0';
|
|
|
|
#ifndef NO_SDLPANGO
|
|
if (font->typ == FONT_TYPE_PANGO)
|
|
{
|
|
sdl_color_to_pango_color(black, &pango_color);
|
|
SDLPango_SetDefaultColor(font->pango_context, &pango_color);
|
|
SDLPango_SetText(font->pango_context, c, -1);
|
|
tmp_surf = SDLPango_CreateSurfaceDraw(font->pango_context);
|
|
}
|
|
#endif
|
|
|
|
/* FIXME: Should the following be in an "#else" block!? -bjk 2009.06.01 */
|
|
if (font->typ == FONT_TYPE_TTF)
|
|
{
|
|
tmp_surf = TTF_RenderUTF8_Blended(font->ttf_font, c, black);
|
|
}
|
|
|
|
if (!tmp_surf)
|
|
{
|
|
#if 0
|
|
// THREADED_FONTS
|
|
printf("could not render \"%s\" font\n", TTF_FontFaceFamilyName(font));
|
|
#endif
|
|
goto out;
|
|
}
|
|
surfs[count++] = tmp_surf;
|
|
}
|
|
was_bad_font = 0;
|
|
qsort(surfs, count, sizeof surfs[0], surfcmp);
|
|
ret = !was_bad_font;
|
|
out:
|
|
while (count--)
|
|
{
|
|
if (surfs[count] == NULL)
|
|
fprintf(stderr, "TRYING TO RE-FREE!");
|
|
else
|
|
{
|
|
SDL_FreeSurface(surfs[count]);
|
|
surfs[count] = NULL;
|
|
}
|
|
}
|
|
free(surfs);
|
|
return ret;
|
|
}
|
|
|
|
int TuxPaint_Font_FontHeight(TuxPaint_Font * tpf)
|
|
{
|
|
if (tpf == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_FontHeight() received NULL\n");
|
|
fflush(stdout);
|
|
#endif
|
|
return (1);
|
|
}
|
|
|
|
return (tpf->height);
|
|
}
|
|
|
|
const char *TuxPaint_Font_FontFaceFamilyName(TuxPaint_Font * tpf)
|
|
{
|
|
if (tpf == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_FontFaceFamilyName() received NULL\n");
|
|
fflush(stdout);
|
|
#endif
|
|
return ("");
|
|
}
|
|
|
|
#ifndef NO_SDLPANGO
|
|
if (tpf->typ == FONT_TYPE_PANGO)
|
|
{
|
|
(void)(tpf);
|
|
/* FIXME */
|
|
|
|
return ("");
|
|
}
|
|
#endif
|
|
|
|
if (tpf->typ == FONT_TYPE_TTF)
|
|
return (TTF_FontFaceFamilyName(tpf->ttf_font));
|
|
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_FontFaceFamilyName() is confused\n");
|
|
#endif
|
|
|
|
return ("");
|
|
}
|
|
|
|
const char *TuxPaint_Font_FontFaceStyleName(TuxPaint_Font * tpf)
|
|
{
|
|
if (tpf == NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_FontFaceStyleName() received NULL\n");
|
|
fflush(stdout);
|
|
#endif
|
|
return ("");
|
|
}
|
|
|
|
#ifndef NO_SDLPANGO
|
|
if (tpf->typ == FONT_TYPE_PANGO)
|
|
{
|
|
(void)(tpf);
|
|
/* FIXME */
|
|
|
|
return ("");
|
|
}
|
|
#endif
|
|
|
|
if (tpf->typ == FONT_TYPE_TTF)
|
|
return (TTF_FontFaceStyleName(tpf->ttf_font));
|
|
|
|
#ifdef DEBUG
|
|
printf("TuxPaint_Font_FontFaceStyleName() is confused\n");
|
|
#endif
|
|
|
|
return ("");
|
|
}
|
|
|
|
|
|
#ifndef NO_SDLPANGO
|
|
|
|
void sdl_color_to_pango_color(SDL_Color sdl_color, SDLPango_Matrix * pango_color)
|
|
{
|
|
Uint8 pc[4][4];
|
|
|
|
pc[0][0] = 0;
|
|
pc[1][0] = 0;
|
|
pc[2][0] = 0;
|
|
pc[3][0] = 0;
|
|
|
|
pc[0][1] = sdl_color.r;
|
|
pc[1][1] = sdl_color.g;
|
|
pc[2][1] = sdl_color.b;
|
|
pc[3][1] = 255;
|
|
|
|
pc[0][2] = 0;
|
|
pc[1][2] = 0;
|
|
pc[2][2] = 0;
|
|
pc[3][2] = 0;
|
|
|
|
pc[0][3] = 0;
|
|
pc[1][3] = 0;
|
|
pc[2][3] = 0;
|
|
pc[3][3] = 0;
|
|
|
|
memcpy(pango_color, pc, 16);
|
|
}
|
|
|
|
#endif
|