racket/src/wxcommon/FontDirectory.cxx
Eli Barzilay 8a2753efb8 2008 -> 2009
svn: r13001
2009-01-04 15:34:50 +00:00

826 lines
18 KiB
C++

/* -*- C++ -*-
*
* Purpose: wxWindows font name handling
*
* Authors: Markus Holzem, Julian Smart, and Matthew Flatt
*
* Copyright: (C) 2004-2009 PLT Scheme Inc.
* Copyright: (C) 1995, AIAI, University of Edinburgh (Julian)
* Copyright: (C) 1995, GNU (Markus)
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA.
*/
#pragma implementation
#ifdef wx_xt
# define Uses_wxApp
# define Uses_wxFontNameDirectory
# include "wx.h"
# include <string.h>
#endif
char *font_defaults[] = {
"PostScriptMediumStraight", "",
"PostScriptMediumItalic", "-Oblique",
"PostScriptMediumSlant", "-Oblique",
"PostScriptLightStraight", "",
"PostScriptLightItalic", "-Oblique",
"PostScriptLightSlant", "-Oblique",
"PostScriptBoldStraight", "-Bold",
"PostScriptBoldItalic", "-BoldOblique",
"PostScriptBoldSlant", "-BoldOblique",
"PostScript___", "${PostScript$[family],$[weight],$[style]}",
"PostScriptSystem__", "${PostScriptTimes,$[weight],$[style]}",
"PostScriptRoman__", "${PostScriptTimes,$[weight],$[style]}",
"PostScriptScript__", "ZapfChancery-MediumItalic",
"PostScriptTimesMedium", "",
"PostScriptTimesLight", "",
"PostScriptTimesBold", "Bold",
"PostScriptTimes__", "Times${PostScript$[weight]$[style]}",
"PostScriptTimesMediumStraight", "Times-Roman",
"PostScriptTimesLightStraight", "Times-Roman",
"PostScriptTimes_Slant", "Times-${PostScriptTimes$[weight]}Italic",
"PostScriptTimes_Italic", "Times-${PostScriptTimes$[weight]}Italic",
"PostScriptDefault__", "Helvetica${PostScript$[weight]$[style]}",
"PostScriptSwiss__", "Helvetica${PostScript$[weight]$[style]}",
"PostScriptDecorative__", "Helvetica${PostScript$[weight]$[style]}",
"PostScriptModern__", "Courier${PostScript$[weight]$[style]}",
"PostScriptSymbol__", "Symbol",
"PostScriptTeletype__", "${PostScriptModern,$[weight],$[style]}",
#ifdef wx_x
"ScreenMedium", "medium",
"ScreenBold", "bold",
"ScreenLight", "light",
"ScreenStraight", "r",
"ScreenItalic", "i",
"ScreenSlant", "o",
"ScreenSystemBase", "*-lucida",
"ScreenDefaultBase", "*-lucida",
"ScreenRomanBase", "*-times",
"ScreenDecorativeBase", "*-helvetica",
"ScreenModernBase", "*-courier",
"ScreenTeletypeBase", "*-lucidatypewriter",
"ScreenSwissBase", "*-lucida",
"ScreenScriptBase", "*-zapf chancery",
"ScreenSymbolBase", "*-symbol",
"ScreenStdSuffix", "-${Screen$[weight]}-${Screen$[style]}"
"-normal-*-*-%d-*-*-*-*-*-*",
"ScreenSystem__",
"+-${ScreenSystemBase}${ScreenStdSuffix}",
"ScreenDefault__",
"+-${ScreenDefaultBase}${ScreenStdSuffix}",
"ScreenRoman__",
"+-${ScreenRomanBase}${ScreenStdSuffix}",
"ScreenDecorative__",
"+-${ScreenDecorativeBase}${ScreenStdSuffix}",
"ScreenModern__",
"+-${ScreenModernBase}${ScreenStdSuffix}",
"ScreenTeletype__",
"+-${ScreenTeletypeBase}${ScreenStdSuffix}",
"ScreenSwiss__",
"+-${ScreenSwissBase}${ScreenStdSuffix}",
"ScreenScript__",
"+-${ScreenScriptBase}-medium-i-normal-*-*-%d-*-*-*-*-*-*",
"ScreenSymbol__",
"+-${ScreenSymbolBase}-medium-r-normal-*-*-%d-*-*-*-*-*-*",
#endif
#ifdef wx_msw
"ScreenSystem__", "MS Sans Serif", /* maybe changed by Adjust */
"ScreenDefault__", "MS Sans Serif", /* maybe changed by Adjust */
"ScreenRoman__", "Times New Roman",
"ScreenDecorative__", "Arial",
"ScreenModern__", "Courier New",
"ScreenTeletype__", "${ScreenModern$[weight];$[style]}",
"ScreenSwiss__", "Arial",
"ScreenScript__", "Arial",
"ScreenSymbol__", "Symbol",
#endif
#ifdef wx_mac
# ifdef OS_X
"ScreenDefault__", "Lucida Grande",
"ScreenSystem__", "Lucida Grande",
"ScreenDecorative__", "Arial", /* "Geneva" looks bad at 12pt in Quartz with QD spacing */
"ScreenModern__", "Courier New", /* "Courier" is worse without Quartz */
"ScreenScript__", "Apple Chancery",
# else
"ScreenDefault__", "applicationfont",
"ScreenSystem__", "systemfont",
"ScreenDecorative__", "Geneva",
"ScreenModern__", "Monaco", /* "courier" is also good */
"ScreenScript__", "Zapf Chancery",
# endif
"ScreenRoman__", "Times",
"ScreenTeletype__", "${ScreenModern,$[weight],$[style]}",
"ScreenSwiss__", "Helvetica",
"ScreenSymbol__", "Symbol",
#endif
NULL
};
#ifdef wx_msw
static int CALLBACK check_font_here(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme,
DWORD FontType, LPARAM lParam)
{
*(int *)lParam = 1;
return 0;
}
int check_avail(char *name)
{
int present = 0;
LOGFONT lf;
HDC dc;
lf.lfCharSet = DEFAULT_CHARSET;
strcpy(lf.lfFaceName, name);
lf.lfPitchAndFamily = 0;
dc = GetDC(NULL);
EnumFontFamiliesEx(dc, &lf, (FONTENUMPROC)check_font_here, (LPARAM)&present, 0);
ReleaseDC(NULL, dc);
return present;
}
#endif
#ifdef WX_USE_XFT
extern int wxXRenderHere(void);
#endif
static void AdjustFontDefaults(void)
{
#ifdef wx_xt
# ifdef WX_USE_XFT
if (wxXRenderHere()) {
int i;
for (i = 0; font_defaults[i]; i += 2) {
if (!strcmp(font_defaults[i], "ScreenSystem__")) {
font_defaults[i + 1] = " Sans";
} else if (!strcmp(font_defaults[i], "ScreenDefault__")) {
font_defaults[i + 1] = " Sans";
} else if (!strcmp(font_defaults[i], "ScreenRoman__")) {
font_defaults[i + 1] = " Serif";
} else if (!strcmp(font_defaults[i], "ScreenDecorative__")) {
font_defaults[i + 1] = " Nimbus Sans L";
} else if (!strcmp(font_defaults[i], "ScreenModern__")) {
font_defaults[i + 1] = " Monospace";
} else if (!strcmp(font_defaults[i], "ScreenTeletype__")) {
font_defaults[i + 1] = " Monospace";
} else if (!strcmp(font_defaults[i], "ScreenSwiss__")) {
font_defaults[i + 1] = " Nimbus Sans L";
} else if (!strcmp(font_defaults[i], "ScreenScript__")) {
font_defaults[i + 1] = " URW Chancery L";
} else if (!strcmp(font_defaults[i], "ScreenSymbol__")) {
font_defaults[i + 1] = " Standard Symbols L,Nimbus Sans L";
}
}
}
# endif
#endif
#ifdef wx_msw
int i;
for (i = 0; font_defaults[i]; i += 2) {
if (!strcmp(font_defaults[i], "ScreenDefault__")) {
/* We'd prefer to use "Microsoft Sans Serif", instead,
in XP, because that's an outline font. */
if (check_avail("Microsoft Sans Serif")) {
font_defaults[i + 1] = "Microsoft Sans Serif";
}
} else if (!strcmp(font_defaults[i], "ScreenSystem__")) {
/* 2000 and XP use Tahoma by default for controls */
static int known = 0, present = 0;
if (check_avail("Tahoma")) {
font_defaults[i+1] = "Tahoma";
}
}
}
#endif
}
wxFontNameDirectory *wxTheFontNameDirectory;
enum {
wxWEIGHT_NORMAL,
wxWEIGHT_BOLD,
wxWEIGHT_LIGHT,
wxNUM_WEIGHTS
};
enum {
wxSTYLE_NORMAL,
wxSTYLE_ITALIC,
wxSTYLE_SLANT,
wxNUM_STYLES
};
class wxSuffixMap {
public:
char *map[wxNUM_WEIGHTS][wxNUM_STYLES];
void Initialize(const char *, const char *, int weight, int style, int fam);
wxSuffixMap();
#ifdef MZ_PRECISE_GC
void gcMark();
void gcFixup();
#endif
};
#ifdef MZ_PRECISE_GC
START_XFORM_SKIP;
void wxSuffixMap::gcMark() {
int i, j;
for (i = 0; i < wxNUM_WEIGHTS; i++)
for (j = 0; j < wxNUM_STYLES; j++) {
gcMARK_TYPED(char *, map[i][j]);
}
}
void wxSuffixMap::gcFixup() {
int i, j;
for (i = 0; i < wxNUM_WEIGHTS; i++)
for (j = 0; j < wxNUM_STYLES; j++) {
gcFIXUP_TYPED(char *, map[i][j]);
}
}
END_XFORM_SKIP;
#endif
wxSuffixMap::wxSuffixMap() {
int i, j;
for (i = 0; i < wxNUM_WEIGHTS; i++) {
for (j = 0; j < wxNUM_STYLES; j++) {
map[i][j] = NULL;
}
}
}
class wxFontNameItem : public wxObject
{
public:
int id;
int family;
char *name;
wxSuffixMap *screen, *printing;
Bool isfamily;
wxFontNameItem();
};
wxFontNameItem::wxFontNameItem()
: wxObject(WXGC_NO_CLEANUP)
{
screen = new WXGC_PTRS wxSuffixMap;
printing = new WXGC_PTRS wxSuffixMap;
}
static int WCoordinate(int w)
{
switch (w) {
case wxBOLD:
return wxWEIGHT_BOLD;
case wxLIGHT:
return wxWEIGHT_LIGHT;
case wxNORMAL:
default:
return wxWEIGHT_NORMAL;
}
}
static int SCoordinate(int s)
{
switch (s) {
case wxITALIC:
return wxSTYLE_ITALIC;
case wxSLANT:
return wxSTYLE_SLANT;
case wxNORMAL:
default:
return wxSTYLE_NORMAL;
}
}
wxFontNameDirectory::wxFontNameDirectory(void)
{
wxHashTable *ht;
ht = new WXGC_PTRS wxHashTable(wxKEY_INTEGER, 20);
table = ht;
nextFontId = 100; /* Larger than all family ids */
}
wxFontNameDirectory::~wxFontNameDirectory()
{
DELETE_OBJ table;
}
int wxFontNameDirectory::GetNewFontId(void)
{
return (nextFontId++);
}
int wxGetPreference(const char *name, char *res, long len);
static char pref_buf[1024];
static void SearchResource(const char *prefix, const char **names, int count, char **v)
{
int k, i, j;
char resource[1024], *internal;
GC_CAN_IGNORE char **defaults;
k = 1 << count;
*v = NULL;
internal = NULL;
for (i = 0; i < k; i++) {
strcpy(resource, prefix);
for (j = 0; j < count; j++) {
if (!(i & (1 << j)))
strcat(resource, names[j]);
else
strcat(resource, "_");
}
if (wxGetPreference((char *)resource, pref_buf, 1024) && *pref_buf) {
*v = pref_buf;
return;
}
if (!internal) {
defaults = font_defaults;
while (*defaults) {
if (!strcmp(*defaults, resource)) {
internal = defaults[1];
break;
}
defaults += 2;
}
}
}
if (internal) {
char *s;
s = copystring(internal);
*v = s;
}
}
void wxInitializeFontNameDirectory(void)
{
AdjustFontDefaults();
wxREGGLOB(wxTheFontNameDirectory);
wxTheFontNameDirectory = new WXGC_PTRS wxFontNameDirectory;
wxTheFontNameDirectory->Initialize(wxSYSTEM, wxSYSTEM, "System");
wxTheFontNameDirectory->Initialize(wxDEFAULT, wxDEFAULT, "Default");
wxTheFontNameDirectory->Initialize(wxDECORATIVE, wxDECORATIVE, "Decorative");
wxTheFontNameDirectory->Initialize(wxROMAN, wxROMAN, "Roman");
wxTheFontNameDirectory->Initialize(wxMODERN, wxMODERN, "Modern");
wxTheFontNameDirectory->Initialize(wxTELETYPE, wxTELETYPE, "Teletype");
wxTheFontNameDirectory->Initialize(wxSWISS, wxSWISS, "Swiss");
wxTheFontNameDirectory->Initialize(wxSCRIPT, wxSCRIPT, "Script");
wxTheFontNameDirectory->Initialize(wxSYMBOL, wxSYMBOL, "Symbol");
}
void wxSuffixMap::Initialize(const char *resname, const char *devresname,
int wt, int st, int fam)
{
const char *weight, *style;
char *v = NULL;
int i, drn;
{
switch (wt) {
case wxWEIGHT_NORMAL:
weight = "Medium";
break;
case wxWEIGHT_LIGHT:
weight = "Light";
break;
case wxWEIGHT_BOLD:
default:
weight = "Bold";
}
{
int len, closer = 0, startpos = 0;
const char *rnames[3];
switch (st) {
case wxSTYLE_NORMAL:
style = "Straight";
break;
case wxSTYLE_ITALIC:
style = "Italic";
break;
case wxSTYLE_SLANT:
default:
style = "Slant";
}
rnames[0] = resname;
rnames[1] = weight;
rnames[2] = style;
SearchResource(devresname, rnames, 3, &v);
/* Expand macros in the found string: */
found:
len = (v ? strlen(v) : 0);
for (i = 0; i < len; i++) {
if (v[i] == '$' && ((v[i+1] == '[') || (v[i+1] == '{'))) {
startpos = i;
if (v[i+1] == '[')
closer = ']';
else
closer = '}';
i++;
} else if (v[i] == closer) {
int newstrlen, noff;
const char *r = NULL;
char *naya, *name;
noff = startpos + 2;
name = v;
v[i] = 0;
if (closer == '}') {
int i, count, len;
char **names;
for (i = 0, count = 1; name[i + noff]; i++) {
if (name[i + noff] == ',') {
count++;
name[i + noff] = 0;
}
}
len = i;
names = new WXGC_PTRS char*[count];
{
char *cs;
cs = COPYSTRING_TO_ALIGNED(name, noff);
names[0] = cs;
}
for (i = 0, count = 1; i < len; i++) {
if (!name[i + noff]) {
{
char *cs;
cs = COPYSTRING_TO_ALIGNED(name, i + 1 + noff);
names[count++] = cs;
}
}
}
SearchResource("", (const char **)names, count, (char **)&r);
if (!r) {
for (i = 0; i < len; i++) {
if (!name[i + noff])
name[i + noff] = ',';
}
r = "";
printf("Bad resource name \"%s\" in font lookup\n", name + noff);
}
} else if (!strcmp(name + noff, "weight")) {
r = weight;
} else if (!strcmp(name + noff, "style")) {
r = style;
} else if (!strcmp(name + noff, "family")) {
switch (fam) {
case wxSYSTEM:
r = "System";
break;
case wxDECORATIVE:
r = "Decorative";
break;
case wxROMAN:
r = "Roman";
break;
case wxMODERN:
r = "Modern";
break;
case wxTELETYPE:
r = "Teletype";
break;
case wxSWISS:
r = "Swiss";
break;
case wxSCRIPT:
r = "Script";
break;
case wxSYMBOL:
r = "Symbol";
break;
default:
r = "Default";
}
} else {
r = "";
printf("Bad font macro name \"%s\"\n", name + noff);
}
newstrlen = strlen(r);
naya = new WXGC_ATOMIC char[len + newstrlen + 1];
memcpy(naya, v, startpos);
memcpy(naya + startpos, r, newstrlen);
memcpy(naya + startpos + newstrlen, v + i + 1, len - i + 1);
v = naya;
goto found;
}
}
drn = ((resname[0] == '@') ? 1 : 0);
#if defined(wx_msw) || defined(wx_mac)
if (!v)
v = copystring(resname + drn);
#endif
#ifdef wx_x
if (!strcmp(devresname, "Screen")) {
if (v && (v[0] == '+')) {
memmove(v, v + 1, strlen(v));
} else {
int len, ds;
char *src;
char *normalcy;
/* Build name using special heuristics:
-([^-]*) => -*-\1-<weight>-<style>-normal-*-*-%d-*-*-*-*-*-*
-([^-]*)-(.*) => -\1-\2-<weight>-<style>-normal-*-*-%d-*-*-*-*-*-*
([^-].*[^-]) => \1
*/
if (v) {
src = (char *)v;
ds = 0;
} else {
src = (char *)resname;
ds = drn;
}
len = strlen(src XFORM_OK_PLUS ds);
if (src[ds] == '-') {
char *prefix;
int c = 0;
for (i = 0; i < len; i++) {
if (src[ds + i] == '-')
c++;
}
v = new WXGC_ATOMIC char[len + 40];
if (c < 2)
prefix = "-*";
else
prefix = "";
if (c < 3) {
switch (wt) {
case wxWEIGHT_NORMAL:
weight = "-medium";
break;
case wxWEIGHT_LIGHT:
weight = "-light";
break;
case wxWEIGHT_BOLD:
default:
weight = "-bold";
}
} else
weight = "";
if (c < 4) {
switch (st) {
case wxSTYLE_NORMAL:
style = "-r";
break;
case wxSTYLE_ITALIC:
style = "-i";
break;
case wxSTYLE_SLANT:
default:
style = "-o";
}
} else
style = "";
if (c < 5)
normalcy = "-normal";
else
normalcy = "";
sprintf(v, "%s%s%s%s%s-*-*-%%d-*-*-*-*-*-*",
prefix, src + ds, weight, style, normalcy);
} else
v = COPYSTRING_TO_ALIGNED(src, ds);
}
}
#endif
/* We have a final value: */
map[wt][st] = v;
}
}
}
void wxFontNameDirectory::Initialize(int fontid, int family, const char *resname)
{
wxFontNameItem *item;
item = new WXGC_PTRS wxFontNameItem;
item->id = fontid;
item->family = family;
item->isfamily = (resname[0] != '@');
item->name = copystring(resname);
table->Put(fontid, item);
}
int wxFontNameDirectory::FindOrCreateFontId(const char *name, int family)
{
int id;
char *s;
if ((id = GetFontId(name, family)))
return id;
id = GetNewFontId();
s = new WXGC_ATOMIC char[strlen(name) + 2];
strcpy(s + 1, name);
s[0] = '@';
Initialize(id, family, s);
return id;
}
char *wxFontNameDirectory::GetScreenName(int fontid, int weight, int style)
{
int wt, st;
wxFontNameItem *item;
item = (wxFontNameItem *)table->Get(fontid);
if (!item)
return NULL;
wt = WCoordinate(weight);
st = SCoordinate(style);
/* Check for init */
if (!item->screen->map[wt][st])
item->screen->Initialize(item->name, "Screen", wt, st, item->family);
return item->screen->map[wt][st];
}
void wxFontNameDirectory::SetScreenName(int fontid, int weight, int style, char *s)
{
int wt, st;
wxFontNameItem *item;
item = (wxFontNameItem *)table->Get(fontid);
if (!item)
return;
wt = WCoordinate(weight);
st = SCoordinate(style);
#ifdef wx_x
{
/* Safety: name must be less than 500 chars, and must not contain %
except maybe one instance of %d. */
int i, found_d = 0;
for (i = 0; s[i]; i++) {
if (i > 500) {
s = NULL;
break;
}
if (s[i] == '%') {
if (found_d || (s[i+1] != 'd')) {
s = NULL;
break;
} else
found_d = 1;
}
}
}
if (!s)
return;
#endif
item->screen->map[wt][st] = s;
}
char *wxFontNameDirectory::GetPostScriptName(int fontid, int weight, int style)
{
int wt, st;
wxFontNameItem *item;
item = (wxFontNameItem *)table->Get(fontid);
if (!item)
return NULL;
wt = WCoordinate(weight);
st = SCoordinate(style);
/* Check for init */
if (!item->printing->map[wt][st])
item->printing->Initialize(item->name, "PostScript", wt, st, item->family);
return item->printing->map[wt][st];
}
void wxFontNameDirectory::SetPostScriptName(int fontid, int weight, int style, char *s)
{
int wt, st;
wxFontNameItem *item;
item = (wxFontNameItem *)table->Get(fontid);
if (!item)
return;
wt = WCoordinate(weight);
st = SCoordinate(style);
item->printing->map[wt][st] = s;
}
char *wxFontNameDirectory::GetFontName(int fontid)
{
wxFontNameItem *item;
item = (wxFontNameItem *)table->Get(fontid);
if (!item)
return NULL;
if (item->isfamily)
return NULL;
return item->name + 1;
}
int wxFontNameDirectory::GetFontId(const char *name, int family)
{
wxNode *node;
table->BeginFind();
while ((node = table->Next())) {
wxFontNameItem *item;
item = (wxFontNameItem *)node->Data();
if (!item->isfamily
&& !strcmp(name, item->name+1)
&& item->family == family)
return item->id;
}
return 0;
}
int wxFontNameDirectory::GetFamily(int fontid)
{
wxFontNameItem *item;
item = (wxFontNameItem *)table->Get(fontid);
if (!item)
return wxDEFAULT;
return item->family;
}