07e5a2c08f148d8bf31219af8c2218cd2cc893cf
braney
Wed Jun 3 09:16:15 2026 -0700
hgTracks config: pair FreeType font names with their files in one table
The configure-page font list lived in two parallel arrays, freeTypeFontNames[]
and freeTypeFontFiles[], matched only by position. Nothing tied a name to its
file, and maybeNewFonts() looked a name up in the first array and then indexed
the second with no bounds check -- so any drift in order or count between the
two, or a selected name that matched nothing, silently rendered the wrong font
or read past the end of the array.
Merge the two arrays into a single struct freeTypeFont[] of {name, file} rows so
the two can no longer get out of sync, and make maybeNewFonts() fall back to the
bitmap engine when the selected font isn't one we know instead of indexing out
of bounds. Same fonts and same name-to-file mapping as before. Also fix
faceNames[] to size by element count (ArraySize) rather than byte count
(sizeof).
refs #37698
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
diff --git src/hg/hgTracks/config.c src/hg/hgTracks/config.c
index 34d377022ab..086369c4a7d 100644
--- src/hg/hgTracks/config.c
+++ src/hg/hgTracks/config.c
@@ -46,153 +46,138 @@
char* name = el->name;
name = chopPrefix(name); // chop off first three words
name = chopPrefix(name);
name = chopPrefix(name);
replaceChar(name, '_', ' ');
labels[i] = name;
i++;
}
char* currentTheme = cartOptionalString(cart, "theme");
hDropList("theme", labels, i, currentTheme);
slFreeList(themes);
hPrintf("</TD>");
}
-char *freeTypeFontNames[] = {
-"AvantGarde-Book",
-"AvantGarde-Demi",
-"AvantGarde-BookOblique",
-"AvantGarde-DemiOblique",
-"Helvetica",
-"Helvetica-Bold",
-"Helvetica-Oblique",
-"Helvetica-BoldOblique",
-"Helvetica-Narrow",
-"Helvetica-Narrow-Bold",
-"Helvetica-Narrow-Oblique",
-"Helvetica-Narrow-BoldOblique",
-"Times-Roman",
-"Times-Bold",
-"Times-Italic",
-"Times-BoldItalic",
-"Courier",
-"Courier-Bold",
-"Courier-Oblique",
-"Courier-BoldOblique",
-"ZapfChancery-MediumItalic",
-"Atkinson",
-"Atkinson-Bold",
-"Atkinson-Oblique",
-"Atkinson-BoldOblique",
-"Lexend",
-"Lexend-Bold",
+struct freeTypeFont
+/* A font offered to the FreeType text engine. The name and its file live on
+ * one row so the two can never drift out of sync (this used to be two parallel
+ * arrays indexed by position, which silently rendered the wrong font when they
+ * disagreed). */
+ {
+ char *name; /* Label shown in the configure-page dropdown. A normal-weight
+ * font is just the face ("Lexend"); a variant is "Face-Style"
+ * ("Lexend-Bold"). The dropdown splits this on the first '-'. */
+ char *file; /* Font file (.pfb Type-1 or .ttf TrueType) found under
+ * freeTypeDir (hg.conf freeTypeDir, default ../htdocs/urw-fonts). */
};
-char *freeTypeFontFiles[] = {
-"a010013l.pfb",
-"a010015l.pfb",
-"a010033l.pfb",
-"a010035l.pfb",
-"n019003l.pfb",
-"n019004l.pfb",
-"n019023l.pfb",
-"n019024l.pfb",
-"n019043l.pfb",
-"n019044l.pfb",
-"n019063l.pfb",
-"n019064l.pfb",
-"n021003l.pfb",
-"n021004l.pfb",
-"n021023l.pfb",
-"n021024l.pfb",
-"n022003l.pfb",
-"n022004l.pfb",
-"n022023l.pfb",
-"n022024l.pfb",
-"z003034l.pfb",
-"AtkinsonHyperlegible-Regular.ttf",
-"AtkinsonHyperlegible-Bold.ttf",
-"AtkinsonHyperlegible-Italic.ttf",
-"AtkinsonHyperlegible-BoldItalic.ttf",
-"Lexend-Regular.ttf",
-"Lexend-Bold.ttf",
+struct freeTypeFont freeTypeFonts[] = {
+{"AvantGarde-Book", "a010013l.pfb"},
+{"AvantGarde-Demi", "a010015l.pfb"},
+{"AvantGarde-BookOblique", "a010033l.pfb"},
+{"AvantGarde-DemiOblique", "a010035l.pfb"},
+{"Helvetica", "n019003l.pfb"},
+{"Helvetica-Bold", "n019004l.pfb"},
+{"Helvetica-Oblique", "n019023l.pfb"},
+{"Helvetica-BoldOblique", "n019024l.pfb"},
+{"Helvetica-Narrow", "n019043l.pfb"},
+{"Helvetica-Narrow-Bold", "n019044l.pfb"},
+{"Helvetica-Narrow-Oblique", "n019063l.pfb"},
+{"Helvetica-Narrow-BoldOblique", "n019064l.pfb"},
+{"Times-Roman", "n021003l.pfb"},
+{"Times-Bold", "n021004l.pfb"},
+{"Times-Italic", "n021023l.pfb"},
+{"Times-BoldItalic", "n021024l.pfb"},
+{"Courier", "n022003l.pfb"},
+{"Courier-Bold", "n022004l.pfb"},
+{"Courier-Oblique", "n022023l.pfb"},
+{"Courier-BoldOblique", "n022024l.pfb"},
+{"ZapfChancery-MediumItalic", "z003034l.pfb"},
+{"Atkinson", "AtkinsonHyperlegible-Regular.ttf"},
+{"Atkinson-Bold", "AtkinsonHyperlegible-Bold.ttf"},
+{"Atkinson-Oblique", "AtkinsonHyperlegible-Italic.ttf"},
+{"Atkinson-BoldOblique", "AtkinsonHyperlegible-BoldItalic.ttf"},
+{"Lexend", "Lexend-Regular.ttf"},
+{"Lexend-Bold", "Lexend-Bold.ttf"},
};
char *emptyStyles[] = {
"Normal"
};
static boolean freeTypeOn()
{
#ifdef USE_FREETYPE
char *defaultState = "on";
#else // USE_FREETYPE
char *defaultState = "off";
#endif // USE_FREETYPE
return sameString(cfgOptionDefault("freeType", defaultState), "on");
}
void maybeNewFonts(struct hvGfx *hvg)
/* Check to see if we want to use the alternate font engine (FreeType2). */
{
if (!freeTypeOn())
return;
if (sameString(tl.textFont, "Bitmap"))
return;
char *fontDir = cfgOptionDefault("freeTypeDir", "../htdocs/urw-fonts");
char buffer[4096];
int ii;
-for(ii=0; ii < ArraySize(freeTypeFontNames); ii++)
- if (sameString(freeTypeFontNames[ii], tl.textFont))
+for(ii=0; ii < ArraySize(freeTypeFonts); ii++)
+ if (sameString(freeTypeFonts[ii].name, tl.textFont))
break;
-char *fontFile = freeTypeFontFiles[ii];
-char *fontName = freeTypeFontNames[ii];
+if (ii == ArraySize(freeTypeFonts))
+ return; // not a font we know about; leave the bitmap engine in place
+char *fontFile = freeTypeFonts[ii].file;
+char *fontName = freeTypeFonts[ii].name;
safef(buffer, sizeof buffer, "%s/%s", fontDir, fontFile);
hvGfxSetFontMethod(hvg, FONT_METHOD_FREETYPE, fontName, buffer );
}
static void textFontDropDown()
/* Create drop down for font size. */
{
/* get current values for font and style */
char *currentFontName = cloneString(tl.textFont);
char *currentStyle = strchr(currentFontName, '-');
if (currentStyle)
*currentStyle++ = 0;
else
currentStyle = "Normal";
-char *faceNames[sizeof(freeTypeFontNames)];
+char *faceNames[ArraySize(freeTypeFonts) + 1]; // +1 for the "Bitmap" entry below
int ii;
int numFonts = 0;
struct dyString *dy = dyStringNew(1024);
dyStringPrintf(dy, " fontStyles = [];\n");
int numStyle = 0;
char *lastName = NULL;
faceNames[numFonts++] = "Bitmap";
dyStringPrintf(dy, " fontStyles['Bitmap'] = ['Normal'];");
-for (ii=0; ii < ArraySize(freeTypeFontNames); ii++)
+for (ii=0; ii < ArraySize(freeTypeFonts); ii++)
{
- char *fontName = cloneString(freeTypeFontNames[ii]);
+ char *fontName = cloneString(freeTypeFonts[ii].name);
char *style = strchr(fontName, '-');
if (style)
*style++ = 0;
if ((lastName == NULL) || differentString(lastName, fontName))
{
faceNames[numFonts] = fontName;
if (lastName != NULL)
dyStringPrintf(dy, " ];\n");
dyStringPrintf(dy, " fontStyles['%s'] = [", fontName);
numStyle = 0;
numFonts++;
}