summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2022-01-19 15:51:07 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2022-01-19 15:51:07 +0200
commita6146ac91c90a4003efd489ee3ada175203995cc (patch)
tree0ec4545734f49441c2638530ae0c5d617a763399
parentea1b72af1b43c846ada3380ba422fffd400ed5b4 (diff)
Fontpack lookup via missing glyphs
IssueID #435
-rw-r--r--po/en.po27
-rw-r--r--res/lang/cs.binbin32069 -> 32702 bytes
-rw-r--r--res/lang/de.binbin30896 -> 31529 bytes
-rw-r--r--res/lang/en.binbin26965 -> 27598 bytes
-rw-r--r--res/lang/eo.binbin26163 -> 26796 bytes
-rw-r--r--res/lang/es.binbin30784 -> 31417 bytes
-rw-r--r--res/lang/es_MX.binbin28035 -> 28668 bytes
-rw-r--r--res/lang/fi.binbin30606 -> 31239 bytes
-rw-r--r--res/lang/fr.binbin31693 -> 32326 bytes
-rw-r--r--res/lang/gl.binbin29952 -> 30585 bytes
-rw-r--r--res/lang/hu.binbin31718 -> 32351 bytes
-rw-r--r--res/lang/ia.binbin29748 -> 30381 bytes
-rw-r--r--res/lang/ie.binbin29655 -> 30288 bytes
-rw-r--r--res/lang/isv.binbin25686 -> 26319 bytes
-rw-r--r--res/lang/nl.binbin29075 -> 29708 bytes
-rw-r--r--res/lang/pl.binbin30321 -> 30954 bytes
-rw-r--r--res/lang/ru.binbin45641 -> 46274 bytes
-rw-r--r--res/lang/sk.binbin26022 -> 26655 bytes
-rw-r--r--res/lang/sr.binbin44947 -> 45580 bytes
-rw-r--r--res/lang/tok.binbin27801 -> 28434 bytes
-rw-r--r--res/lang/tr.binbin29902 -> 30535 bytes
-rw-r--r--res/lang/uk.binbin45028 -> 45661 bytes
-rw-r--r--res/lang/zh_Hans.binbin25930 -> 26563 bytes
-rw-r--r--res/lang/zh_Hant.binbin26328 -> 26961 bytes
-rw-r--r--src/app.c53
-rw-r--r--src/app.h1
-rw-r--r--src/fontpack.c99
-rw-r--r--src/fontpack.h2
-rw-r--r--src/ui/banner.c3
-rw-r--r--src/ui/documentwidget.c1
-rw-r--r--src/ui/text.c40
-rw-r--r--src/ui/text.h2
-rw-r--r--src/ui/util.c48
-rw-r--r--src/ui/util.h1
34 files changed, 256 insertions, 21 deletions
diff --git a/po/en.po b/po/en.po
index e0e086e3..cc5f2091 100644
--- a/po/en.po
+++ b/po/en.po
@@ -1181,6 +1181,33 @@ msgstr "Russian"
1181msgid "lang.es" 1181msgid "lang.es"
1182msgstr "Spanish" 1182msgstr "Spanish"
1183 1183
1184msgid "heading.glyphfinder"
1185msgstr "Missing Glyphs"
1186
1187msgid "dlg.glyphfinder.missing"
1188msgstr "The following characters could not be shown:"
1189
1190msgid "dlg.glyphfinder.help"
1191msgstr "You can try searching the skyjake.fi Font Library for fonts that provide glyphs for these characters, or manually install new TrueType fonts."
1192
1193msgid "dlg.glyphfinder.help.empty"
1194msgstr "Please reload the page to check again for missing glyphs."
1195
1196msgid "dlg.glyphfinder.disable"
1197msgstr "Disable Warnings"
1198
1199msgid "dlg.glyphfinder.search"
1200msgstr "Search Font Library"
1201
1202msgid "heading.glyphfinder.results"
1203msgstr "Search Results"
1204
1205msgid "glyphfinder.results"
1206msgstr "The following fontpacks provide one or more of the missing glyphs:"
1207
1208msgid "glyphfinder.results.empty"
1209msgstr "Sorry, no matching fontpacks were found."
1210
1184msgid "heading.newident" 1211msgid "heading.newident"
1185msgstr "New Identity" 1212msgstr "New Identity"
1186 1213
diff --git a/res/lang/cs.bin b/res/lang/cs.bin
index eb59f65d..6f1afa85 100644
--- a/res/lang/cs.bin
+++ b/res/lang/cs.bin
Binary files differ
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 9c3d4541..ecb601aa 100644
--- a/res/lang/de.bin
+++ b/res/lang/de.bin
Binary files differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 5f649846..758adbba 100644
--- a/res/lang/en.bin
+++ b/res/lang/en.bin
Binary files differ
diff --git a/res/lang/eo.bin b/res/lang/eo.bin
index ace592df..1de8a7db 100644
--- a/res/lang/eo.bin
+++ b/res/lang/eo.bin
Binary files differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index 14c1b843..a11c6f78 100644
--- a/res/lang/es.bin
+++ b/res/lang/es.bin
Binary files differ
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin
index dfcd6306..40379ee0 100644
--- a/res/lang/es_MX.bin
+++ b/res/lang/es_MX.bin
Binary files differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index 44d2741d..18a4c11d 100644
--- a/res/lang/fi.bin
+++ b/res/lang/fi.bin
Binary files differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index ad16b2e7..ba098242 100644
--- a/res/lang/fr.bin
+++ b/res/lang/fr.bin
Binary files differ
diff --git a/res/lang/gl.bin b/res/lang/gl.bin
index f1501b4a..7727f587 100644
--- a/res/lang/gl.bin
+++ b/res/lang/gl.bin
Binary files differ
diff --git a/res/lang/hu.bin b/res/lang/hu.bin
index c070dd49..94af8aa8 100644
--- a/res/lang/hu.bin
+++ b/res/lang/hu.bin
Binary files differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index 34633dd1..1aac22fe 100644
--- a/res/lang/ia.bin
+++ b/res/lang/ia.bin
Binary files differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index 722ee20d..4d5ac8d9 100644
--- a/res/lang/ie.bin
+++ b/res/lang/ie.bin
Binary files differ
diff --git a/res/lang/isv.bin b/res/lang/isv.bin
index 3be01643..c3931a80 100644
--- a/res/lang/isv.bin
+++ b/res/lang/isv.bin
Binary files differ
diff --git a/res/lang/nl.bin b/res/lang/nl.bin
index dcc9fe97..579f0df8 100644
--- a/res/lang/nl.bin
+++ b/res/lang/nl.bin
Binary files differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index a32e9d10..448be098 100644
--- a/res/lang/pl.bin
+++ b/res/lang/pl.bin
Binary files differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index e434971b..04114010 100644
--- a/res/lang/ru.bin
+++ b/res/lang/ru.bin
Binary files differ
diff --git a/res/lang/sk.bin b/res/lang/sk.bin
index 833872c9..fc984d97 100644
--- a/res/lang/sk.bin
+++ b/res/lang/sk.bin
Binary files differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index 43a6deda..ef949942 100644
--- a/res/lang/sr.bin
+++ b/res/lang/sr.bin
Binary files differ
diff --git a/res/lang/tok.bin b/res/lang/tok.bin
index d17f075e..cfbee57e 100644
--- a/res/lang/tok.bin
+++ b/res/lang/tok.bin
Binary files differ
diff --git a/res/lang/tr.bin b/res/lang/tr.bin
index 06fc22e5..8552574c 100644
--- a/res/lang/tr.bin
+++ b/res/lang/tr.bin
Binary files differ
diff --git a/res/lang/uk.bin b/res/lang/uk.bin
index 23329907..8f39c7fd 100644
--- a/res/lang/uk.bin
+++ b/res/lang/uk.bin
Binary files differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 246b7c42..2f0c03fa 100644
--- a/res/lang/zh_Hans.bin
+++ b/res/lang/zh_Hans.bin
Binary files differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index 0e94f66a..f978013c 100644
--- a/res/lang/zh_Hant.bin
+++ b/res/lang/zh_Hant.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index 4045610e..31f506b5 100644
--- a/src/app.c
+++ b/src/app.c
@@ -2245,6 +2245,13 @@ void resetFonts_App(void) {
2245 } 2245 }
2246} 2246}
2247 2247
2248void availableFontsChanged_App(void) {
2249 iApp *d = &app_;
2250 iConstForEach(PtrArray, win, listWindows_App_(d, collectNew_PtrArray())) {
2251 resetMissing_Text(text_Window(win.ptr));
2252 }
2253}
2254
2248static void invalidateCachedDocuments_App_(void) { 2255static void invalidateCachedDocuments_App_(void) {
2249 iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { 2256 iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) {
2250 invalidateCachedLayout_History(history_DocumentWidget(i.object)); 2257 invalidateCachedLayout_History(history_DocumentWidget(i.object));
@@ -2371,24 +2378,42 @@ iBool handleCommand_App(const char *cmd) {
2371 reload_Fonts(); /* also does font cache reset, window invalidation */ 2378 reload_Fonts(); /* also does font cache reset, window invalidation */
2372 return iTrue; 2379 return iTrue;
2373 } 2380 }
2374#if 0 2381 else if (equal_Command(cmd, "font.find")) {
2375 else if (equal_Command(cmd, "font.user")) { 2382 searchOnlineLibraryForCharacters_Fonts(string_Command(cmd, "chars"));
2376 const char *path = suffixPtr_Command(cmd, "path"); 2383 return iTrue;
2377 if (cmp_String(&d->prefs.symbolFontPath, path)) { 2384 }
2378 if (!isFrozen) { 2385 else if (equal_Command(cmd, "font.found")) {
2379 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); 2386 if (hasLabel_Command(cmd, "error")) {
2380 } 2387 makeSimpleMessage_Widget("${heading.glyphfinder}",
2381 setCStr_String(&d->prefs.symbolFontPath, path); 2388 format_CStr("%d %s",
2382 loadUserFonts_Text(); 2389 argLabel_Command(cmd, "error"),
2383 resetFonts_App(d); 2390 suffixPtr_Command(cmd, "msg")));
2384 if (!isFrozen) { 2391 return iTrue;
2385 postCommand_App("font.changed"); 2392 }
2386 postCommand_App("window.unfreeze"); 2393 iString *src = collectNew_String();
2394 setCStr_String(src, "# ${heading.glyphfinder.results}\n\n");
2395 iRangecc path = iNullRange;
2396 iBool isFirst = iTrue;
2397 while (nextSplit_Rangecc(range_Command(cmd, "packs"), ";", &path)) {
2398 if (isFirst) {
2399 appendCStr_String(src, "${glyphfinder.results}\n\n");
2387 } 2400 }
2401 const char *fp = cstr_Rangecc(path);
2402 appendFormat_String(src, "=> gemini://skyjake.fi/fonts/%s %s\n", fp, fp);
2403 isFirst = iFalse;
2404 }
2405 if (isFirst) {
2406 appendFormat_String(src, "${glyphfinder.results.empty}\n");
2388 } 2407 }
2408 appendCStr_String(src, "\n=> about:fonts ${menu.fonts}");
2409 iDocumentWidget *page = newTab_App(NULL, iTrue);
2410 translate_Lang(src);
2411 setUrlAndSource_DocumentWidget(page,
2412 collectNewCStr_String(""),
2413 collectNewCStr_String("text/gemini"),
2414 utf8_String(src));
2389 return iTrue; 2415 return iTrue;
2390 } 2416 }
2391#endif
2392 else if (equal_Command(cmd, "font.set")) { 2417 else if (equal_Command(cmd, "font.set")) {
2393 if (!isFrozen) { 2418 if (!isFrozen) {
2394 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); 2419 setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
diff --git a/src/app.h b/src/app.h
index 5968de0d..1a4aa556 100644
--- a/src/app.h
+++ b/src/app.h
@@ -142,6 +142,7 @@ iDocumentWidget * document_Command (const char *cmd);
142void openInDefaultBrowser_App(const iString *url); 142void openInDefaultBrowser_App(const iString *url);
143void revealPath_App (const iString *path); 143void revealPath_App (const iString *path);
144void resetFonts_App (void); 144void resetFonts_App (void);
145void availableFontsChanged_App(void);
145 146
146iMainWindow * mainWindow_App (void); 147iMainWindow * mainWindow_App (void);
147void closePopups_App (void); 148void closePopups_App (void);
diff --git a/src/fontpack.c b/src/fontpack.c
index a440234e..3faf0e92 100644
--- a/src/fontpack.c
+++ b/src/fontpack.c
@@ -23,6 +23,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
23#include "fontpack.h" 23#include "fontpack.h"
24#include "resources.h" 24#include "resources.h"
25#include "ui/window.h" 25#include "ui/window.h"
26#include "gmrequest.h"
26#include "app.h" 27#include "app.h"
27 28
28#include <the_Foundation/archive.h> 29#include <the_Foundation/archive.h>
@@ -1018,6 +1019,7 @@ void install_Fonts(const iString *packId, const iBlock *data) {
1018 iRelease(f); 1019 iRelease(f);
1019 /* Newly installed fontpacks may have a higher priority that overrides other fonts. */ 1020 /* Newly installed fontpacks may have a higher priority that overrides other fonts. */
1020 reload_Fonts(); 1021 reload_Fonts();
1022 availableFontsChanged_App();
1021} 1023}
1022 1024
1023void installFontFile_Fonts(const iString *fileName, const iBlock *data) { 1025void installFontFile_Fonts(const iString *fileName, const iBlock *data) {
@@ -1028,6 +1030,7 @@ void installFontFile_Fonts(const iString *fileName, const iBlock *data) {
1028 } 1030 }
1029 iRelease(f); 1031 iRelease(f);
1030 reload_Fonts(); 1032 reload_Fonts();
1033 availableFontsChanged_App();
1031} 1034}
1032 1035
1033void enablePack_Fonts(const iString *packId, iBool enable) { 1036void enablePack_Fonts(const iString *packId, iBool enable) {
@@ -1040,6 +1043,7 @@ void enablePack_Fonts(const iString *packId, iBool enable) {
1040 } 1043 }
1041 updateActive_Fonts(); 1044 updateActive_Fonts();
1042 resetFonts_App(); 1045 resetFonts_App();
1046 availableFontsChanged_App();
1043 invalidate_Window(get_MainWindow()); 1047 invalidate_Window(get_MainWindow());
1044} 1048}
1045 1049
@@ -1047,5 +1051,98 @@ void updateActive_Fonts(void) {
1047 sortSpecs_Fonts_(&fonts_); 1051 sortSpecs_Fonts_(&fonts_);
1048} 1052}
1049 1053
1050iDefineClass(FontFile) 1054static void findCharactersInCMap_(iGmRequest *d, iGmRequest *req) {
1055 /* Note: Called in background thread. */
1056 iUnused(req);
1057 const iString *missingChars = userData_Object(d);
1058 if (isSuccess_GmStatusCode(status_GmRequest(d))) {
1059 iStringList *matchingPacks = new_StringList();
1060 iChar needed[20];
1061 iChar minChar = UINT32_MAX, maxChar = 0;
1062 size_t numNeeded = 0;
1063 iConstForEach(String, ch, missingChars) {
1064 needed[numNeeded++] = ch.value;
1065 minChar = iMin(minChar, ch.value);
1066 maxChar = iMax(maxChar, ch.value);
1067 if (numNeeded == iElemCount(needed)) {
1068 /* Shouldn't be that many. */
1069 break;
1070 }
1071 }
1072 iBlock *data = decompressGzip_Block(body_GmRequest(d));
1073 iRangecc line = iNullRange;
1074 while (nextSplit_Rangecc(range_Block(data), "\n", &line)) {
1075 iRangecc fontpackPath = iNullRange;
1076 for (const char *pos = line.start; pos < line.end; pos++) {
1077 if (*pos == ':') {
1078 fontpackPath.start = line.start;
1079 fontpackPath.end = pos;
1080 line.start = pos + 1;
1081 trimStart_Rangecc(&line);
1082 break;
1083 }
1084 }
1085 if (fontpackPath.start) {
1086 /* Parse the character ranges and see if any match what we need. */
1087 const char *pos = line.start;
1088 while (pos < line.end) {
1089 char *endp;
1090 uint32_t first = strtoul(pos, &endp, 10);
1091 uint32_t last = first;
1092 if (*endp == '-') {
1093 last = strtoul(endp + 1, &endp, 10);
1094 }
1095 if (maxChar < first) {
1096 break; /* The rest are even higher. */
1097 }
1098 if (minChar <= last) {
1099 for (size_t i = 0; i < numNeeded; i++) {
1100 if (needed[i] >= first && needed[i] <= last) {
1101 /* Got it. */
1102 pushBackRange_StringList(matchingPacks, fontpackPath);
1103 break;
1104 }
1105 }
1106 }
1107 pos = endp + 1;
1108 }
1109 }
1110 }
1111 delete_Block(data);
1112 iString result;
1113 init_String(&result);
1114 format_String(&result, "font.found chars:%s packs:", cstr_String(missingChars));
1115 iConstForEach(StringList, s, matchingPacks) {
1116 if (s.pos != 0) {
1117 appendCStr_String(&result, ";");
1118 }
1119 append_String(&result, s.value);
1120 }
1121 postCommandString_Root(NULL, &result);
1122 deinit_String(&result);
1123 iRelease(matchingPacks);
1124 }
1125 else {
1126 /* Report error. */
1127 postCommandf_Root(NULL,
1128 "font.found chars:%s error:%d msg:\x1b[1m%s\x1b[0m\n%s",
1129 cstr_String(missingChars),
1130 status_GmRequest(d),
1131 cstr_String(meta_GmRequest(d)),
1132 cstr_String(url_GmRequest(d)));
1133 }
1134 fflush(stdout);
1135 delete_String(userData_Object(d));
1136 iReleaseLater(d);
1137}
1051 1138
1139void searchOnlineLibraryForCharacters_Fonts(const iString *chars) {
1140 /* Fetch the character map from skyjake.fi. */
1141 iGmRequest *req = new_GmRequest(certs_App());
1142 setUrl_GmRequest(req, collectNewCStr_String("gemini://skyjake.fi/fonts/cmap.txt.gz"));
1143 setUserData_Object(req, copy_String(chars));
1144 iConnect(GmRequest, req, finished, req, findCharactersInCMap_);
1145 submit_GmRequest(req);
1146}
1147
1148iDefineClass(FontFile)
diff --git a/src/fontpack.h b/src/fontpack.h
index aa6f2b9f..27ecd087 100644
--- a/src/fontpack.h
+++ b/src/fontpack.h
@@ -186,3 +186,5 @@ void reload_Fonts (void);
186iLocalDef iBool isInstalled_Fonts(const char *packId) { 186iLocalDef iBool isInstalled_Fonts(const char *packId) {
187 return pack_Fonts(packId) != NULL; 187 return pack_Fonts(packId) != NULL;
188} 188}
189
190void searchOnlineLibraryForCharacters_Fonts (const iString *chars);
diff --git a/src/ui/banner.c b/src/ui/banner.c
index 11ae1574..79d70039 100644
--- a/src/ui/banner.c
+++ b/src/ui/banner.c
@@ -327,7 +327,8 @@ iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) {
327 else { 327 else {
328 switch (item->code) { 328 switch (item->code) {
329 case missingGlyphs_GmStatusCode: 329 case missingGlyphs_GmStatusCode:
330 postCommandf_App("open newtab:1 url:about:fonts"); 330 //postCommandf_App("open newtab:1 url:about:fonts");
331 makeGlyphFinder_Widget();
331 break; 332 break;
332 case ansiEscapes_GmStatusCode: 333 case ansiEscapes_GmStatusCode:
333 makeQuestion_Widget( 334 makeQuestion_Widget(
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 6a535882..fdb55232 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -2756,6 +2756,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d,
2756 } 2756 }
2757 d->flags |= drawDownloadCounter_DocumentWidgetFlag; 2757 d->flags |= drawDownloadCounter_DocumentWidgetFlag;
2758 clear_PtrSet(d->view.invalidRuns); 2758 clear_PtrSet(d->view.invalidRuns);
2759 documentRunsInvalidated_DocumentWidget_(d);
2759 deinit_String(&str); 2760 deinit_String(&str);
2760 return; 2761 return;
2761 } 2762 }
diff --git a/src/ui/text.c b/src/ui/text.c
index 7bb418eb..86ac709b 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -247,8 +247,6 @@ struct Impl_CacheRow {
247}; 247};
248 248
249struct Impl_Text { 249struct Impl_Text {
250// enum iTextFont contentFont;
251// enum iTextFont headingFont;
252 float contentFontSize; 250 float contentFontSize;
253 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ 251 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */
254 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ 252 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */
@@ -264,7 +262,8 @@ struct Impl_Text {
264 int ansiFlags; 262 int ansiFlags;
265 int baseFontId; /* base attributes (for restoring via escapes) */ 263 int baseFontId; /* base attributes (for restoring via escapes) */
266 int baseFgColorId; 264 int baseFgColorId;
267 iBool missingGlyphs; /* true if a glyph couldn't be found */ 265 iBool missingGlyphs; /* true if a glyph couldn't be found */
266 iChar missingChars[20]; /* rotating buffer of the latest missing characters */
268}; 267};
269 268
270iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) 269iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render)
@@ -341,6 +340,8 @@ static void initFonts_Text_(iText *d) {
341 printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); 340 printf("[Text] %zu font variants ready\n", size_Array(&d->fonts));
342#endif 341#endif
343 gap_Text = iRound(gap_UI * d->contentFontSize); 342 gap_Text = iRound(gap_UI * d->contentFontSize);
343// d->missingGlyphs = iFalse;
344// iZap(d->missingChars);
344} 345}
345 346
346static void deinitFonts_Text_(iText *d) { 347static void deinitFonts_Text_(iText *d) {
@@ -403,6 +404,7 @@ void init_Text(iText *d, SDL_Renderer *render) {
403 d->baseFontId = -1; 404 d->baseFontId = -1;
404 d->baseFgColorId = -1; 405 d->baseFgColorId = -1;
405 d->missingGlyphs = iFalse; 406 d->missingGlyphs = iFalse;
407 iZap(d->missingChars);
406 d->render = render; 408 d->render = render;
407 /* A grayscale palette for rasterized glyphs. */ { 409 /* A grayscale palette for rasterized glyphs. */ {
408 SDL_Color colors[256]; 410 SDL_Color colors[256];
@@ -589,8 +591,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
589 } 591 }
590 } 592 }
591 if (!*glyphIndex) { 593 if (!*glyphIndex) {
592 activeText_->missingGlyphs = iTrue; 594 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int) ch); fflush(stderr);
593 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); 595 iText *tx = activeText_;
596 tx->missingGlyphs = iTrue;
597 /* Remember a few of the latest missing characters. */
598 iBool gotIt = iFalse;
599 for (size_t i = 0; i < iElemCount(tx->missingChars); i++) {
600 if (tx->missingChars[i] == ch) {
601 gotIt = iTrue;
602 break;
603 }
604 }
605 if (!gotIt) {
606 memmove(tx->missingChars + 1,
607 tx->missingChars,
608 sizeof(tx->missingChars) - sizeof(tx->missingChars[0]));
609 tx->missingChars[0] = ch;
610 }
594 } 611 }
595 return d; 612 return d;
596} 613}
@@ -2199,6 +2216,19 @@ iBool checkMissing_Text(void) {
2199 return missing; 2216 return missing;
2200} 2217}
2201 2218
2219iChar missing_Text(size_t index) {
2220 const iText *d = activeText_;
2221 if (index >= iElemCount(d->missingChars)) {
2222 return 0;
2223 }
2224 return d->missingChars[index];
2225}
2226
2227void resetMissing_Text(iText *d) {
2228 d->missingGlyphs = iFalse;
2229 iZap(d->missingChars);
2230}
2231
2202SDL_Texture *glyphCache_Text(void) { 2232SDL_Texture *glyphCache_Text(void) {
2203 return activeText_->cache; 2233 return activeText_->cache;
2204} 2234}
diff --git a/src/ui/text.h b/src/ui/text.h
index c8bb6f85..a34cc9bd 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -227,6 +227,8 @@ struct Impl_WrapText {
227iTextMetrics measure_WrapText (iWrapText *, int fontId); 227iTextMetrics measure_WrapText (iWrapText *, int fontId);
228iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); 228iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color);
229 229
230iChar missing_Text (size_t index);
231void resetMissing_Text (iText *);
230iBool checkMissing_Text (void); /* returns the flag, and clears it */ 232iBool checkMissing_Text (void); /* returns the flag, and clears it */
231SDL_Texture * glyphCache_Text (void); 233SDL_Texture * glyphCache_Text (void);
232 234
diff --git a/src/ui/util.c b/src/ui/util.c
index 31907721..8e71dcec 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -3451,6 +3451,54 @@ iWidget *makeTranslation_Widget(iWidget *parent) {
3451 return dlg; 3451 return dlg;
3452} 3452}
3453 3453
3454iWidget *makeGlyphFinder_Widget(void) {
3455 iString msg;
3456 iString command;
3457 init_String(&msg);
3458 initCStr_String(&command, "!font.find chars:");
3459 for (size_t i = 0; ; i++) {
3460 iChar ch = missing_Text(i);
3461 if (!ch) break;
3462 appendFormat_String(&msg, " U+%04X", ch);
3463 appendChar_String(&command, ch);
3464 }
3465 iArray items;
3466 init_Array(&items, sizeof(iMenuItem));
3467 if (!isEmpty_String(&msg)) {
3468 prependCStr_String(&msg, "${dlg.glyphfinder.missing} ");
3469 appendCStr_String(&msg, "\n\n${dlg.glyphfinder.help}");
3470 pushBackN_Array(
3471 &items,
3472 (iMenuItem[]){
3473 { "${menu.fonts}", 0, 0, "!open newtab:1 url:about:fonts" },
3474 { "${dlg.glyphfinder.disable}", 0, 0, "prefs.font.warnmissing.changed arg:0" },
3475 { "---" },
3476 { uiTextCaution_ColorEscape magnifyingGlass_Icon " ${dlg.glyphfinder.search}",
3477 0,
3478 0,
3479 cstr_String(&command) },
3480 { "${close}", 0, 0, "cancel" } },
3481 5);
3482 }
3483 else {
3484 setCStr_String(&msg, "${dlg.glyphfinder.help.empty}");
3485 pushBackN_Array(&items,
3486 (iMenuItem[]){ { "${menu.reload}", 0, 0, "navigate.reload" },
3487 { "${close}", 0, 0, "cancel" } },
3488 2);
3489 }
3490 iWidget *dlg = makeQuestion_Widget("${heading.glyphfinder}", cstr_String(&msg),
3491 constData_Array(&items),
3492 size_Array(&items));
3493 arrange_Widget(dlg);
3494 deinit_Array(&items);
3495 deinit_String(&command);
3496 deinit_String(&msg);
3497 return dlg;
3498}
3499
3500/*----------------------------------------------------------------------------------------------*/
3501
3454void init_PerfTimer(iPerfTimer *d) { 3502void init_PerfTimer(iPerfTimer *d) {
3455 d->ticks = SDL_GetPerformanceCounter(); 3503 d->ticks = SDL_GetPerformanceCounter();
3456} 3504}
diff --git a/src/ui/util.h b/src/ui/util.h
index 98ce784c..0289d579 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -342,6 +342,7 @@ iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *titl
342iWidget * makeIdentityCreation_Widget (void); 342iWidget * makeIdentityCreation_Widget (void);
343iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); 343iWidget * makeFeedSettings_Widget (uint32_t bookmarkId);
344iWidget * makeTranslation_Widget (iWidget *parent); 344iWidget * makeTranslation_Widget (iWidget *parent);
345iWidget * makeGlyphFinder_Widget (void);
345 346
346const char * languageId_String (const iString *menuItemLabel); 347const char * languageId_String (const iString *menuItemLabel);
347int languageIndex_CStr (const char *langId); 348int languageIndex_CStr (const char *langId);