summaryrefslogtreecommitdiff
path: root/src/ui/text.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/text.c')
-rw-r--r--src/ui/text.c337
1 files changed, 194 insertions, 143 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index e3f7f7c4..86fdc084 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -123,40 +123,57 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch)
123 123
124static iGlyph *glyph_Font_(iFont *d, iChar ch); 124static iGlyph *glyph_Font_(iFont *d, iChar ch);
125 125
126struct Impl_Font { 126iDeclareType(GlyphTable)
127 iBlock * data; 127
128 enum iTextFont family; 128struct Impl_GlyphTable {
129 stbtt_fontinfo font; 129 iHash glyphs; /* key is glyph index in the font */
130 float xScale, yScale; 130 /* TODO: `glyphs` does not need to be a Hash.
131 int vertOffset; /* offset due to scaling */ 131 We could lazily allocate an array with glyphCount elements instead. */
132 int height;
133 int baseline;
134 iHash glyphs; /* key is glyph index in the font */ /* TODO: does not need to be a Hash */
135 iBool isMonospaced;
136 float emAdvance;
137 enum iFontSize sizeId; /* used to look up different fonts of matching size */
138 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ 132 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */
139#if defined (LAGRANGE_ENABLE_HARFBUZZ)
140 hb_blob_t * hbBlob; /* raw TrueType data */
141 hb_face_t * hbFace;
142 hb_font_t * hbMainFont;
143 hb_font_t * hbFont; /* may be a sub-font with customized font metrics */
144#endif
145}; 133};
146 134
147static iFont *font_Text_(enum iFontId id); 135static void clearGlyphs_Font_(iGlyphTable *d) {
148 136 iForEach(Hash, i, &d->glyphs) {
149#if 0 137 delete_Glyph((iGlyph *) i.value);
150static hb_position_t hbGlyphHKernForNunito_(hb_font_t *font, void *fontData, 138 }
151 hb_codepoint_t firstGlyph, hb_codepoint_t secondGlyph, 139 clear_Hash(&d->glyphs);
152 void *userData) {
153 return 100;
154} 140}
155#endif
156 141
157static void init_Font(iFont *d, const iBlock *data, int height, float scale, 142static void init_GlyphTable(iGlyphTable *d) {
158 enum iFontSize sizeId, iBool isMonospaced) {
159 init_Hash(&d->glyphs); 143 init_Hash(&d->glyphs);
144 memset(d->indexTable, 0xff, sizeof(d->indexTable));
145}
146
147static void deinit_GlyphTable(iGlyphTable *d) {
148 clearGlyphs_Font_(d);
149 deinit_Hash(&d->glyphs);
150}
151
152iDefineTypeConstruction(GlyphTable)
153
154struct Impl_Font {
155 const iFontSpec *fontSpec;
156 const iFontFile *fontFile;
157 int height;
158 int baseline;
159 int vertOffset; /* offset due to glyph scaling */
160 float xScale, yScale;
161 float emAdvance;
162 iGlyphTable * table;
163};
164
165iLocalDef iBool isMonospaced_Font(const iFont *d) {
166 return (d->fontSpec->flags & monospace_FontSpecFlag) != 0;
167}
168
169static iFont *font_Text_(enum iFontId id);
170
171static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *fontFile,
172 enum iFontSize sizeId, int height) {
173 d->fontSpec = fontSpec;
174 d->fontFile = fontFile;
175 /* TODO: Nunito kerning fixes need to be a font parameter of its own. */
176#if 0
160 d->data = NULL; 177 d->data = NULL;
161 d->family = undefined_TextFont; 178 d->family = undefined_TextFont;
162 /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ 179 /* Note: We only use `family` currently for applying a kerning fix to Nunito. */
@@ -177,95 +194,49 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale,
177 data == &fontSmolEmojiRegular_Embedded) { 194 data == &fontSmolEmojiRegular_Embedded) {
178 d->family = emojiAndSymbols_TextFont; 195 d->family = emojiAndSymbols_TextFont;
179 } 196 }
180 d->isMonospaced = isMonospaced; 197#endif
198// d->isMonospaced = (fontSpec->flags & monospace_FontSpecFlag) != 0;
181 d->height = height; 199 d->height = height;
182 iZap(d->font); 200 //iZap(d->font);
183 stbtt_InitFont(&d->font, constData_Block(data), 0); 201// stbtt_InitFont(&d->font, constData_Block(data), 0);
184 int ascent, descent, emAdv; 202// int ascent, descent, emAdv;
185 stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); 203// stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL);
186 stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL); 204// stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL);
187 d->xScale = d->yScale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; 205 const float scale = fontSpec->scaling;
188 if (d->isMonospaced) { 206 d->xScale = d->yScale = scaleForPixelHeight_FontFile(fontFile, height) * scale;
207 if (isMonospaced_Font(d)) {
189 /* It is important that monospaced fonts align 1:1 with the pixel grid so that 208 /* It is important that monospaced fonts align 1:1 with the pixel grid so that
190 box-drawing characters don't have partially occupied edge pixels, leading to seams 209 box-drawing characters don't have partially occupied edge pixels, leading to seams
191 between adjacent glyphs. */ 210 between adjacent glyphs. */
192 const float advance = (float) emAdv * d->xScale; 211 const float advance = (float) fontFile->emAdvance * d->xScale;
193 if (advance > 4) { /* not too tiny */ 212 if (advance > 4) { /* not too tiny */
194 d->xScale *= floorf(advance) / advance; 213 d->xScale *= floorf(advance) / advance;
195 } 214 }
196 } 215 }
197 d->emAdvance = emAdv * d->xScale; 216 d->emAdvance = fontFile->emAdvance * d->xScale;
198 d->baseline = ascent * d->yScale; 217 d->baseline = fontFile->ascent * d->yScale;
199 d->vertOffset = height * (1.0f - scale) / 2; 218 d->vertOffset = height * (1.0f - scale) / 2 * fontSpec->vertOffset;
200 /* Custom tweaks. */ 219 d->table = NULL;
201 if (data == &fontNotoSansSymbolsRegular_Embedded) {
202 d->vertOffset *= 1.2f;
203 }
204 else if (data == &fontNotoSansSymbols2Regular_Embedded) {
205 d->vertOffset /= 2;
206 }
207 else if (data == &fontNotoEmojiRegular_Embedded) {
208 //d->vertOffset -= height / 30;
209 }
210 d->sizeId = sizeId;
211 memset(d->indexTable, 0xff, sizeof(d->indexTable));
212#if defined(LAGRANGE_ENABLE_HARFBUZZ)
213 /* HarfBuzz will read the font data. */ {
214 d->hbBlob = hb_blob_create(constData_Block(data), size_Block(data),
215 HB_MEMORY_MODE_READONLY, NULL, NULL);
216 d->hbFace = hb_face_create(d->hbBlob, 0);
217 d->hbMainFont = hb_font_create(d->hbFace);
218#if 0
219 /* TODO: The custom kerning function doesn't get called?
220 Maybe HarfBuzz needs FreeType to do kerning? */
221 if (d->family == nunito_TextFont) {
222 /* Customize the kerning of Nunito. */
223 d->hbFont = hb_font_create_sub_font(d->hbMainFont);
224 hb_font_funcs_t *ffs = hb_font_funcs_create();
225 hb_font_funcs_set_glyph_h_kerning_func(ffs, hbGlyphHKernForNunito_, d, NULL);
226 hb_font_set_funcs(d->hbFont, ffs, NULL, NULL);
227 hb_font_funcs_destroy(ffs);
228 }
229 else
230#endif
231 {
232 d->hbFont = hb_font_reference(d->hbMainFont);
233 }
234 }
235#endif
236}
237
238static void clearGlyphs_Font_(iFont *d) {
239 iForEach(Hash, i, &d->glyphs) {
240 delete_Glyph((iGlyph *) i.value);
241 }
242 clear_Hash(&d->glyphs);
243} 220}
244 221
245static void deinit_Font(iFont *d) { 222static void deinit_Font(iFont *d) {
246#if defined(LAGRANGE_ENABLE_HARFBUZZ) 223 delete_GlyphTable(d->table);
247 /* HarfBuzz objects. */ {
248 hb_font_destroy(d->hbFont);
249 hb_font_destroy(d->hbMainFont);
250 hb_face_destroy(d->hbFace);
251 hb_blob_destroy(d->hbBlob);
252 }
253#endif
254 clearGlyphs_Font_(d);
255 deinit_Hash(&d->glyphs);
256 delete_Block(d->data);
257} 224}
258 225
259static uint32_t glyphIndex_Font_(iFont *d, iChar ch) { 226static uint32_t glyphIndex_Font_(iFont *d, iChar ch) {
260 /* TODO: Add a small cache of ~5 most recently found indices. */ 227 /* TODO: Add a small cache of ~5 most recently found indices. */
261 const size_t entry = ch - 32; 228 const size_t entry = ch - 32;
262 if (entry < iElemCount(d->indexTable)) { 229 if (!d->table) {
263 if (d->indexTable[entry] == ~0u) { 230 d->table = new_GlyphTable();
264 d->indexTable[entry] = stbtt_FindGlyphIndex(&d->font, ch); 231 }
232 iGlyphTable *table = d->table;
233 if (entry < iElemCount(table->indexTable)) {
234 if (table->indexTable[entry] == ~0u) {
235 table->indexTable[entry] = findGlyphIndex_FontFile(d->fontFile, ch);
265 } 236 }
266 return d->indexTable[entry]; 237 return table->indexTable[entry];
267 } 238 }
268 return stbtt_FindGlyphIndex(&d->font, ch); 239 return findGlyphIndex_FontFile(d->fontFile, ch);
269} 240}
270 241
271/*----------------------------------------------------------------------------------------------*/ 242/*----------------------------------------------------------------------------------------------*/
@@ -282,7 +253,8 @@ struct Impl_Text {
282 enum iTextFont contentFont; 253 enum iTextFont contentFont;
283 enum iTextFont headingFont; 254 enum iTextFont headingFont;
284 float contentFontSize; 255 float contentFontSize;
285 iFont fonts[max_FontId]; 256 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */
257 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */
286 SDL_Renderer * render; 258 SDL_Renderer * render;
287 SDL_Texture * cache; 259 SDL_Texture * cache;
288 iInt2 cacheSize; 260 iInt2 cacheSize;
@@ -296,12 +268,49 @@ struct Impl_Text {
296iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) 268iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render)
297 269
298static iText *activeText_; 270static iText *activeText_;
299static iBlock *userFont_; 271
272static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId) {
273#if defined (iPlatformMobile)
274 const float uiSize = fontSize_UI * 1.1f;
275#else
276 const float uiSize = fontSize_UI;
277#endif
278 const float textSize = fontSize_UI * d->contentFontSize;
279// const float monoSize = textSize * 0.71f;
280// const float smallMonoSize = monoSize * 0.8f;
281 if (spec->flags & override_FontSpecFlag && d->overrideFontId < 0) {
282 /* This is the highest priority override font. */
283 d->overrideFontId = baseId;
284 }
285 for (enum iFontStyle style = 0; style < max_FontStyle; style++) {
286 for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) {
287 init_Font(font_Text_(FONT_ID(baseId, style, sizeId)),
288 spec,
289 spec->styles[style],
290 sizeId,
291 (sizeId < contentRegular_FontSize ? uiSize : textSize) * scale_FontSize(sizeId));
292 }
293 }
294}
295
296iLocalDef iFont *font_Text_(enum iFontId id) {
297 return at_Array(&activeText_->fonts, id & mask_FontId);
298}
299
300static enum iFontId fontId_Text_(const iFont *font) {
301 return (enum iFontId) (font - (const iFont *) constData_Array(&activeText_->fonts));
302}
303
304iLocalDef enum iFontSize sizeId_Text_(const iFont *d) {
305 return fontId_Text_(d) % max_FontSize;
306}
307
308iLocalDef enum iFontStyle styleId_Text_(const iFont *d) {
309 return (fontId_Text_(d) / max_FontSize) % max_FontStyle;
310}
300 311
301static void initFonts_Text_(iText *d) { 312static void initFonts_Text_(iText *d) {
302 const float textSize = fontSize_UI * d->contentFontSize; 313#if 0
303 const float monoSize = textSize * 0.71f;
304 const float smallMonoSize = monoSize * 0.8f;
305 const iBlock *regularFont = &fontNunitoRegular_Embedded; 314 const iBlock *regularFont = &fontNunitoRegular_Embedded;
306 const iBlock *boldFont = &fontNunitoBold_Embedded; 315 const iBlock *boldFont = &fontNunitoBold_Embedded;
307 const iBlock *italicFont = &fontNunitoLightItalic_Embedded; 316 const iBlock *italicFont = &fontNunitoLightItalic_Embedded;
@@ -367,15 +376,10 @@ static void initFonts_Text_(iText *d) {
367 h12Font = &fontIosevkaTermExtended_Embedded; 376 h12Font = &fontIosevkaTermExtended_Embedded;
368 h3Font = &fontIosevkaTermExtended_Embedded; 377 h3Font = &fontIosevkaTermExtended_Embedded;
369 } 378 }
370#if defined (iPlatformMobile)
371 const float uiSize = fontSize_UI * 1.1f;
372#else
373 const float uiSize = fontSize_UI;
374#endif
375 const struct { 379 const struct {
376 const iBlock *ttf; 380 const iFontFile *fontFile;
377 int size; 381 int size; /* pixels */
378 float scaling; 382// float scaling;
379 enum iFontSize sizeId; 383 enum iFontSize sizeId;
380 /* UI sizes: 1.0, 1.125, 1.333, 1.666 */ 384 /* UI sizes: 1.0, 1.125, 1.333, 1.666 */
381 /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */ 385 /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */
@@ -433,8 +437,34 @@ static void initFonts_Text_(iText *d) {
433 DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f), 437 DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f),
434// DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f), 438// DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f),
435 }; 439 };
440#endif
441 /* The `fonts` array has precomputed scaling factors and other parameters in all sizes
442 and styles for each available font. Indices to `fonts` act as font runtime IDs. */
443 /* First the mandatory fonts. */
444 d->overrideFontId = -1;
445 resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */
446 iAssert(auxiliary_FontId == documentHeading_FontId + maxVariants_Fonts);
447 setupFontVariants_Text_(d, findSpec_Fonts("default"), default_FontId);
448 setupFontVariants_Text_(d, findSpec_Fonts("iosevka"), monospace_FontId);
449 setupFontVariants_Text_(d, findSpec_Fonts("default"), documentBody_FontId);
450 setupFontVariants_Text_(d, findSpec_Fonts("default"), documentHeading_FontId);
451 /* Check if there are auxiliary fonts available and set those up, too. */
452 iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) {
453 const iFontSpec *spec = s.ptr;
454 if (spec->flags & auxiliary_FontSpecFlag) {
455 const int fontId = size_Array(&d->fonts);
456 resize_Array(&d->fonts, fontId + maxVariants_Fonts);
457 setupFontVariants_Text_(d, spec, fontId);
458 }
459 }
460 /* test */ {
461 const iFont *h = font_Text_(preformatted_FontId); // FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize));
462 printf("{%s} %d sz:%d st:%d\n", cstr_String(&h->fontSpec->name), h->height, sizeId_Text_(h),
463 styleId_Text_(h));
464 }
465#if 0
436 iForIndices(i, fontData) { 466 iForIndices(i, fontData) {
437 iFont *font = &d->fonts[i]; 467 iFont *font = font_Text_(i);
438 init_Font(font, 468 init_Font(font,
439 fontData[i].ttf, 469 fontData[i].ttf,
440 fontData[i].size, 470 fontData[i].size,
@@ -442,13 +472,15 @@ static void initFonts_Text_(iText *d) {
442 fontData[i].sizeId, 472 fontData[i].sizeId,
443 fontData[i].ttf == &fontIosevkaTermExtended_Embedded); 473 fontData[i].ttf == &fontIosevkaTermExtended_Embedded);
444 } 474 }
475#endif
445 gap_Text = iRound(gap_UI * d->contentFontSize); 476 gap_Text = iRound(gap_UI * d->contentFontSize);
446} 477}
447 478
448static void deinitFonts_Text_(iText *d) { 479static void deinitFonts_Text_(iText *d) {
449 iForIndices(i, d->fonts) { 480 iForEach(Array, i, &d->fonts) {
450 deinit_Font(&d->fonts[i]); 481 deinit_Font(i.value);
451 } 482 }
483 clear_Array(&d->fonts);
452} 484}
453 485
454static int maxGlyphHeight_Text_(const iText *d) { 486static int maxGlyphHeight_Text_(const iText *d) {
@@ -490,6 +522,7 @@ static void deinitCache_Text_(iText *d) {
490 SDL_DestroyTexture(d->cache); 522 SDL_DestroyTexture(d->cache);
491} 523}
492 524
525#if 0
493void loadUserFonts_Text(void) { 526void loadUserFonts_Text(void) {
494 if (userFont_) { 527 if (userFont_) {
495 delete_Block(userFont_); 528 delete_Block(userFont_);
@@ -508,9 +541,12 @@ void loadUserFonts_Text(void) {
508 iRelease(f); 541 iRelease(f);
509 } 542 }
510} 543}
544#endif
511 545
512void init_Text(iText *d, SDL_Renderer *render) { 546void init_Text(iText *d, SDL_Renderer *render) {
513 loadUserFonts_Text(); 547 iText *oldActive = activeText_;
548 activeText_ = d;
549 init_Array(&d->fonts, sizeof(iFont));
514 d->contentFont = nunito_TextFont; 550 d->contentFont = nunito_TextFont;
515 d->headingFont = nunito_TextFont; 551 d->headingFont = nunito_TextFont;
516 d->contentFontSize = contentScale_Text_; 552 d->contentFontSize = contentScale_Text_;
@@ -526,6 +562,7 @@ void init_Text(iText *d, SDL_Renderer *render) {
526 } 562 }
527 initCache_Text_(d); 563 initCache_Text_(d);
528 initFonts_Text_(d); 564 initFonts_Text_(d);
565 activeText_ = oldActive;
529} 566}
530 567
531void deinit_Text(iText *d) { 568void deinit_Text(iText *d) {
@@ -534,6 +571,7 @@ void deinit_Text(iText *d) {
534 deinitCache_Text_(d); 571 deinitCache_Text_(d);
535 d->render = NULL; 572 d->render = NULL;
536 iRelease(d->ansiEscape); 573 iRelease(d->ansiEscape);
574 deinit_Array(&d->fonts);
537} 575}
538 576
539void setCurrent_Text(iText *d) { 577void setCurrent_Text(iText *d) {
@@ -569,8 +607,8 @@ void setContentFontSize_Text(iText *d, float fontSizeFactor) {
569 607
570static void resetCache_Text_(iText *d) { 608static void resetCache_Text_(iText *d) {
571 deinitCache_Text_(d); 609 deinitCache_Text_(d);
572 for (int i = 0; i < max_FontId; i++) { 610 iForEach(Array, i, &d->fonts) {
573 clearGlyphs_Font_(&d->fonts[i]); 611 clearGlyphs_Font_(i.value);
574 } 612 }
575 initCache_Text_(d); 613 initCache_Text_(d);
576} 614}
@@ -582,14 +620,10 @@ void resetFonts_Text(iText *d) {
582 initFonts_Text_(d); 620 initFonts_Text_(d);
583} 621}
584 622
585iLocalDef iFont *font_Text_(enum iFontId id) {
586 return &activeText_->fonts[id & mask_FontId];
587}
588
589static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { 623static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) {
590 int w, h; 624 int w, h;
591 uint8_t *bmp = stbtt_GetGlyphBitmapSubpixel( 625 uint8_t *bmp = rasterizeGlyph_FontFile(d->fontFile, d->xScale, d->yScale, xShift, glyphIndex,
592 &d->font, d->xScale, d->yScale, xShift, 0.0f, glyphIndex, &w, &h, 0, 0); 626 &w, &h);
593 SDL_Surface *surface8 = 627 SDL_Surface *surface8 =
594 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); 628 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8);
595 SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE); 629 SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE);
@@ -636,8 +670,8 @@ static iInt2 assignCachePos_Text_(iText *d, iInt2 size) {
636static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { 670static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
637 iRect *glRect = &glyph->rect[hoff]; 671 iRect *glRect = &glyph->rect[hoff];
638 int x0, y0, x1, y1; 672 int x0, y0, x1, y1;
639 stbtt_GetGlyphBitmapBoxSubpixel( 673 measureGlyph_FontFile(d->fontFile, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f,
640 &d->font, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f, 0.0f, &x0, &y0, &x1, &y1); 674 &x0, &y0, &x1, &y1);
641 glRect->size = init_I2(x1 - x0, y1 - y0); 675 glRect->size = init_I2(x1 - x0, y1 - y0);
642 /* Determine placement in the glyph cache texture, advancing in rows. */ 676 /* Determine placement in the glyph cache texture, advancing in rows. */
643 glRect->pos = assignCachePos_Text_(activeText_, glRect->size); 677 glRect->pos = assignCachePos_Text_(activeText_, glRect->size);
@@ -645,7 +679,7 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
645 glyph->d[hoff].y += d->vertOffset; 679 glyph->d[hoff].y += d->vertOffset;
646 if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */ 680 if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */
647 int adv; 681 int adv;
648 stbtt_GetGlyphHMetrics(&d->font, index_Glyph_(glyph), &adv, NULL); 682 stbtt_GetGlyphHMetrics(&d->fontFile->stbInfo, index_Glyph_(glyph), &adv, NULL);
649 glyph->advance = d->xScale * adv; 683 glyph->advance = d->xScale * adv;
650 } 684 }
651} 685}
@@ -654,13 +688,18 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
654 if (isVariationSelector_Char(ch)) { 688 if (isVariationSelector_Char(ch)) {
655 return d; 689 return d;
656 } 690 }
657 /* Smol Emoji overrides all other fonts. */ 691 const enum iFontStyle styleId = styleId_Text_(d);
658 if (ch != 0x20) { 692 const enum iFontSize sizeId = sizeId_Text_(d);
659 iFont *smol = font_Text_(smolEmoji_FontId + d->sizeId); 693 iFont *overrideFont = NULL;
660 if (smol != d && (*glyphIndex = glyphIndex_Font_(smol, ch)) != 0) { 694 if (ch != 0x20 && activeText_->overrideFontId >= 0) {
661 return smol; 695 /* Override font is checked first. */
696 overrideFont = font_Text_(FONT_ID(activeText_->overrideFontId, styleId, sizeId));
697 if (overrideFont != d && (*glyphIndex = glyphIndex_Font_(overrideFont, ch)) != 0) {
698 return overrideFont;
662 } 699 }
663 } 700 }
701#if 0
702 /* TODO: Put arrows in Smol Emoji. */
664 /* Manual exceptions. */ { 703 /* Manual exceptions. */ {
665 if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { 704 if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) {
666 d = font_Text_(iosevka_FontId + d->sizeId); 705 d = font_Text_(iosevka_FontId + d->sizeId);
@@ -668,9 +707,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
668 return d; 707 return d;
669 } 708 }
670 } 709 }
710#endif
711 /* The font's own version of the glyph. */
671 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { 712 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) {
672 return d; 713 return d;
673 } 714 }
715 /* As a fallback, check all other available fonts of this size. */
716 for (iFont *font = font_Text_(FONT_ID(0, styleId, sizeId));
717 font < (iFont *) end_Array(&activeText_->fonts);
718 font += maxVariants_Fonts) {
719 if (font == d || font == overrideFont) {
720 continue; /* already checked this one */
721 }
722 if ((*glyphIndex = glyphIndex_Font_(font, ch)) != 0) {
723 return font;
724 }
725 }
726#if 0
674 const int fallbacks[] = { 727 const int fallbacks[] = {
675 notoEmoji_FontId, 728 notoEmoji_FontId,
676 symbols2_FontId, 729 symbols2_FontId,
@@ -732,6 +785,7 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
732 if (d != font) { 785 if (d != font) {
733 *glyphIndex = glyphIndex_Font_(font, ch); 786 *glyphIndex = glyphIndex_Font_(font, ch);
734 } 787 }
788#endif // 0
735 if (!*glyphIndex) { 789 if (!*glyphIndex) {
736 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); 790 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr);
737 } 791 }
@@ -739,8 +793,9 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
739} 793}
740 794
741static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { 795static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) {
796 iAssert(d->table);
742 iGlyph* glyph = NULL; 797 iGlyph* glyph = NULL;
743 void * node = value_Hash(&d->glyphs, glyphIndex); 798 void * node = value_Hash(&d->table->glyphs, glyphIndex);
744 if (node) { 799 if (node) {
745 glyph = node; 800 glyph = node;
746 } 801 }
@@ -758,7 +813,7 @@ static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) {
758 and updates the glyph metrics. */ 813 and updates the glyph metrics. */
759 allocate_Font_(d, glyph, 0); 814 allocate_Font_(d, glyph, 0);
760 allocate_Font_(d, glyph, 1); 815 allocate_Font_(d, glyph, 1);
761 insert_Hash(&d->glyphs, &glyph->node); 816 insert_Hash(&d->table->glyphs, &glyph->node);
762 } 817 }
763 return glyph; 818 return glyph;
764} 819}
@@ -866,10 +921,6 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, i
866 run->logical.start = endAt; 921 run->logical.start = endAt;
867} 922}
868 923
869static enum iFontId fontId_Text_(const iFont *font) {
870 return (enum iFontId) (font - activeText_->fonts);
871}
872
873static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { 924static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) {
874 iAssert(isEmpty_Array(&d->runs)); 925 iAssert(isEmpty_Array(&d->runs));
875 size_t length = 0; 926 size_t length = 0;
@@ -997,14 +1048,14 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
997 continue; 1048 continue;
998 } 1049 }
999 if (ch == 0x20) { 1050 if (ch == 0x20) {
1000 if (run.font->family == emojiAndSymbols_TextFont) { 1051 if (run.font->fontSpec->flags & auxiliary_FontSpecFlag) {
1001 finishRun_AttributedText_(d, &run, pos); 1052 finishRun_AttributedText_(d, &run, pos);
1002 run.font = d->font; /* never use space from the symbols font, it's too wide */ 1053 run.font = d->font; /* never use space from the symbols font, it's too wide */
1003 } 1054 }
1004 continue; 1055 continue;
1005 } 1056 }
1006 iFont *currentFont = d->font; 1057 iFont *currentFont = d->font;
1007 if (run.font->family == arabic_TextFont && isPunct_Char(ch)) { 1058 if (run.font->fontSpec->flags & arabic_FontSpecFlag && isPunct_Char(ch)) {
1008 currentFont = run.font; /* remain as Arabic for whitespace */ 1059 currentFont = run.font; /* remain as Arabic for whitespace */
1009 } 1060 }
1010 const iGlyph *glyph = glyph_Font_(currentFont, ch); 1061 const iGlyph *glyph = glyph_Font_(currentFont, ch);
@@ -1283,7 +1334,7 @@ static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int
1283 1334
1284float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) { 1335float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) {
1285#if defined (LAGRANGE_ENABLE_KERNING) 1336#if defined (LAGRANGE_ENABLE_KERNING)
1286 if (!enableKerning_Text || d->family != nunito_TextFont) { 1337 if (!enableKerning_Text || ~d->fontSpec->flags & fixNunitoKerning_FontSpecFlag) {
1287 return 0.0f; 1338 return 0.0f;
1288 } 1339 }
1289 if (glyph1 && glyph2) { 1340 if (glyph1 && glyph2) {
@@ -1335,7 +1386,7 @@ static void deinit_GlyphBuffer_(iGlyphBuffer *d) {
1335 1386
1336static void shape_GlyphBuffer_(iGlyphBuffer *d) { 1387static void shape_GlyphBuffer_(iGlyphBuffer *d) {
1337 if (!d->glyphInfo) { 1388 if (!d->glyphInfo) {
1338 hb_shape(d->font->hbFont, d->hb, NULL, 0); 1389 hb_shape(d->font->fontFile->hbFont, d->hb, NULL, 0);
1339 d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount); 1390 d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount);
1340 d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount); 1391 d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount);
1341 } 1392 }
@@ -1389,7 +1440,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1389 float xCursor = 0.0f; 1440 float xCursor = 0.0f;
1390 float yCursor = 0.0f; 1441 float yCursor = 0.0f;
1391 float xCursorMax = 0.0f; 1442 float xCursorMax = 0.0f;
1392 const iBool isMonospaced = d->isMonospaced; 1443 const iBool isMonospaced = isMonospaced_Font(d);
1393 iWrapText *wrap = args->wrap; 1444 iWrapText *wrap = args->wrap;
1394 iAssert(args->text.end >= args->text.start); 1445 iAssert(args->text.end >= args->text.start);
1395 /* Split the text into a number of attributed runs that specify exactly which 1446 /* Split the text into a number of attributed runs that specify exactly which