bd85159b36db7ff8c02b32eb18d96ed6066feb8f braney Wed Aug 5 13:59:46 2026 -0700 hgTracks: measure text with the same font engine that will draw it, refs #38027 Pack mode works out how many rows a track needs by measuring its item labels, and mgFontStringWidth answers from whichever text engine is loaded at the time. FreeType was only loaded when an image was created, so the measuring could happen on the bitmap engine while the drawing happened on FreeType -- and which one you got depended on whether the ideogram image had been built first. Load the engine in initTl(), alongside the rest of the font setup, before anything measures a string. maybeNewFonts() and the new initFontEngine() share chosenFreeTypeFont(), and mgLoadFontEngine() loads an engine without attaching it to an image. ftInitialize() now keeps the face it already has when asked for the same font file, so the glyph cache hanging off that face survives the extra call. Co-Authored-By: Claude Opus 5 (1M context) diff --git src/hg/hgTracks/config.c src/hg/hgTracks/config.c index 28257e86458..2dacb531f32 100644 --- src/hg/hgTracks/config.c +++ src/hg/hgTracks/config.c @@ -140,52 +140,74 @@ 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). */ +static char *chosenFreeTypeFont(char **retFontName) +/* Return the file holding the FreeType font the user has picked, and its name in + * retFontName. NULL means stay on the bitmap engine. */ { if (!freeTypeOn()) - return; + return NULL; if (sameString(tl.textFont, "Bitmap")) - return; - -char *fontDir = cfgOptionDefault("freeTypeDir", "../htdocs/urw-fonts"); -char buffer[4096]; + return NULL; int ii; for(ii=0; ii < ArraySize(freeTypeFonts); ii++) if (sameString(freeTypeFonts[ii].name, tl.textFont)) break; 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 ); + return NULL; // not a font we know about; leave the bitmap engine in place + +static char buffer[PATH_LEN]; +char *fontDir = cfgOptionDefault("freeTypeDir", "../htdocs/urw-fonts"); +safef(buffer, sizeof buffer, "%s/%s", fontDir, freeTypeFonts[ii].file); +*retFontName = freeTypeFonts[ii].name; +return buffer; +} + +void initFontEngine() +/* Load the text engine the user has picked, before anything measures a string. + * Pack mode reserves room for an item by measuring its label, and + * mgFontStringWidth answers from whichever engine is loaded at the time. Doing + * this up front keeps the packing from depending on whether some earlier image + * on the page -- the ideogram -- happened to load FreeType first. */ +{ +char *fontName = NULL; +char *fontFile = chosenFreeTypeFont(&fontName); +if (fontFile != NULL) + mgLoadFontEngine(FONT_METHOD_FREETYPE, fontFile); +} + +void maybeNewFonts(struct hvGfx *hvg) +/* Check to see if we want to use the alternate font engine (FreeType2). */ +{ +char *fontName = NULL; +char *fontFile = chosenFreeTypeFont(&fontName); +if (fontFile != NULL) + hvGfxSetFontMethod(hvg, FONT_METHOD_FREETYPE, fontName, fontFile); } 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[ArraySize(freeTypeFonts) + 1]; // +1 for the "Bitmap" entry below int ii;