From a82083c2de03cedafd48f86f99b127483b2d899a Mon Sep 17 00:00:00 2001 From: Krister Wicksell Date: Wed, 12 May 2021 14:55:30 +0200 Subject: [PATCH 1/3] When a font is missing a glyph we try to fallback to using a question mark As it is now MapServer will fail to render if a layer is using characters that's not available in the font. This commit resolves this by trying to fallback to using a question mark in these situations. --- fontcache.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/fontcache.c b/fontcache.c index 8bf9e97490..119b210d86 100644 --- a/fontcache.c +++ b/fontcache.c @@ -287,6 +287,12 @@ glyph_element* msGetGlyphByIndex(face_element *face, unsigned int size, unsigned FT_Set_Pixel_Sizes(face->face,0,MS_NINT(size * 96/72.0)); } error = FT_Load_Glyph(face->face,key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + if (error) { + msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", key.codepoint, face->font); + // If we can't find a glyph then try to fallback to a question mark. + unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); + error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + } if(error) { msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphByIndex()",key.codepoint, face->font); free(gc); @@ -335,8 +341,14 @@ outline_element* msGetGlyphOutline(face_element *face, glyph_element *glyph) { pen.x = pen.y = 0; FT_Set_Transform(face->face, &matrix, &pen); error = FT_Load_Glyph(face->face,glyph->key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + if (error) { + msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", glyph->key.codepoint, face->font); + // If we can't find a glyph then try to fallback to a question mark. + unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); + error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); + } if(error) { - msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphByIndex()",glyph->key.codepoint, face->font); + msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphOutline()",glyph->key.codepoint, face->font); #ifdef USE_THREAD if (use_global_ft_cache) msReleaseLock(TLOCK_TTF); From fd3658095614c18577cda4ddab765f522def8d2d Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 23 May 2021 15:23:50 +0200 Subject: [PATCH 2/3] msGetGlyphByIndex() / msGetGlyphOutline(): tweak debug/error messages --- fontcache.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fontcache.c b/fontcache.c index 119b210d86..e730dfd046 100644 --- a/fontcache.c +++ b/fontcache.c @@ -288,13 +288,13 @@ glyph_element* msGetGlyphByIndex(face_element *face, unsigned int size, unsigned } error = FT_Load_Glyph(face->face,key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); if (error) { - msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", key.codepoint, face->font); + msDebug("Unable to load glyph %u for font \"%s\". Using ? as fallback.\n", key.codepoint, face->font); // If we can't find a glyph then try to fallback to a question mark. unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); } if(error) { - msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphByIndex()",key.codepoint, face->font); + msSetError(MS_MISCERR, "unable to load glyph %u for font \"%s\"", "msGetGlyphByIndex()",key.codepoint, face->font); free(gc); #ifdef USE_THREAD if (use_global_ft_cache) @@ -342,13 +342,13 @@ outline_element* msGetGlyphOutline(face_element *face, glyph_element *glyph) { FT_Set_Transform(face->face, &matrix, &pen); error = FT_Load_Glyph(face->face,glyph->key.codepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); if (error) { - msDebug("Unable to load glyph %ud for font \"%s\". Using ? as fallback.", glyph->key.codepoint, face->font); + msDebug("Unable to load glyph %u for font \"%s\". Using ? as fallback.\n", glyph->key.codepoint, face->font); // If we can't find a glyph then try to fallback to a question mark. unsigned int fallbackCodepoint = msGetGlyphIndex(face, 0x3F); error = FT_Load_Glyph(face->face,fallbackCodepoint,FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP/*|FT_LOAD_IGNORE_TRANSFORM*/|FT_LOAD_NO_HINTING|FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH); } if(error) { - msSetError(MS_MISCERR, "unable to load glyph %ud for font \"%s\"", "msGetGlyphOutline()",glyph->key.codepoint, face->font); + msSetError(MS_MISCERR, "unable to load glyph %u for font \"%s\"", "msGetGlyphOutline()",glyph->key.codepoint, face->font); #ifdef USE_THREAD if (use_global_ft_cache) msReleaseLock(TLOCK_TTF); From a72d95c2199aa12ac43281e4e605a1e208d2c4b4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 23 May 2021 15:03:35 +0200 Subject: [PATCH 3/3] check_single_font(): use quotation mark when msGetGlyphIndex() fails to retrieve the glyph --- .../expected/font-fail-missing-glyph.png | Bin 0 -> 2307 bytes msautotest/renderers/font-fail.map | 18 ++++++++++++++++++ textlayout.c | 13 ++++++++++--- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 msautotest/renderers/expected/font-fail-missing-glyph.png diff --git a/msautotest/renderers/expected/font-fail-missing-glyph.png b/msautotest/renderers/expected/font-fail-missing-glyph.png new file mode 100644 index 0000000000000000000000000000000000000000..611bdedc0e74767789f1c98f1f7fa5176e6ba179 GIT binary patch literal 2307 zcmd^B_dDBd7pHF?n>JFTlvd2Dnze!`qGF{K#e*V76@7>q^C60d)Cj8f=s=AUtr^6u zR-@V)jS-3%ZDQ44?fZRyeE){$hx&pG!=u|k>fa*1%Uu(0r&A`!O% z`wm#S9A|+2e3uo9g@t3t6k%YCEn3U7tFki}9vmq=D-Y!mtWuJf=fa5vG*f9*7dH<# z_Eef!)u2aQC=%@(yDdz+pBd{BOfjoW}gH1#y!-h zv2OiFpgAE@xylCr4kIy(b|Hn;Sr%%<9Pg#I{k^?ByuC6~c{;5bFw3NozEQ+Io9!x`pwz{+5}UnT5s4$)Q?!7?V1GcZdES^24*%lMb|Eh@^FDVH&Ixm zVhwov)tL-&c=YNIH4x}OXIT2?Ma$u#GyKT_A?vf9$D8wgrSO>42(kFI7a4;p7~FAZ zZCi`^^>obL@@`6xYfn{NN+n_ukd(Qa=d zH^FDstQ4+i!&qP+dufyWa_LH_+!sS2#om&Gf*0mb?l!o^EAd@|12KD<|Fgio6I)VLcg$EDc0B}L@>K*2IjlH7kXQZKFFCeIx)2GIGnAhSB z-Tnm~fTfR()9s;Gjp7`ZK0LV>K^$xJ9v-quDl^6l`V$DNW4H5l0My-w4{x6HdHd*s z0_K%tt=CYFL>%}eYG)PPg?Ds6Ko!Hr+d~f|N>Pu$< zR)_J_180J?JrQjT+7)2q73r5fhSt0Q^bgXor$dB6WO=MLpfFCiF8JJ+dRKi{&j@8E zkIPVr!@0`32JH{3hzBK4MGO`a_?5B%B-eV`5#tnGZkVXyPwoCgJFUj%DS}fW zSeREEipBIIuP?TrL?4K?yb|N|IlC;Ni;IZ^gzZCgtlNpm5Y6);14Sf>V6Z>Z+yT)2qjs;_8fUT zu6P;C0>lu%hT$U&T0%P3lafd1`AO{QkzCFS=M{tHU?HPvR<#O_jaJF@NI(f8DLM|1 zuh>+WJ(d^K#eN#+9553=3q)lrWnF%)c?g{X+ulB%rxHE-eEKU1ZimoWpg?3Ppnsa^ z54w=mD8#-oTfC*RKiTtrmj6_h|_fl!4{)diG3n8VNJbRH=E zllwCKmX)oGcxDDO!TmG^BHI{x1l4A7YEa6GMCGeW*WSW%FG3P5I&Li*#p+=A{kr2= zW67nkQ2Fn5q*s47$+uk-dS;R#Gn|TmaJNVXl7W(PmwW_YsYmWKy#1`oe=2J{+Y!+R z14;Cw>(tiUf>+YNvl-wZ`6O?dYP*_c+*9*d)1;Ya3j=tPbERP-7rY*=a@o%!0i>HH z@9Oz`H$O literal 0 HcmV?d00001 diff --git a/msautotest/renderers/font-fail.map b/msautotest/renderers/font-fail.map index 9271e3dfe8..0f86e9850f 100644 --- a/msautotest/renderers/font-fail.map +++ b/msautotest/renderers/font-fail.map @@ -1,5 +1,6 @@ # RUN_PARMS: font-fail-key.txt [MAPSERV] QUERY_STRING="map=[MAPFILE]&mode=map&layer=l1" > [RESULT_DEVERSION] # RUN_PARMS: font-fail-file.txt [MAPSERV] QUERY_STRING="map=[MAPFILE]&mode=map&layer=l2" > [RESULT_DEVERSION] +# RUN_PARMS: font-fail-missing-glyph.png [SHP2IMG] -m [MAPFILE] -l missing_glyph -i png -o [RESULT] MAP @@ -41,4 +42,21 @@ LAYER FEATURE POINTS 50 50 END END END +LAYER + NAME "missing_glyph" + STATUS ON + TYPE POLYGON + FEATURE + POINTS 0 0 400 0 400 300 0 300 0 0 END + END + CLASS + LABEL + TEXT 'this is a test: это проверка' + TYPE truetype + SIZE 8 + FONT "default" + END + END +END + END diff --git a/textlayout.c b/textlayout.c index f6826b1d12..b16bda5a4c 100644 --- a/textlayout.c +++ b/textlayout.c @@ -242,7 +242,7 @@ static hb_position_t _ms_get_glyph_v_advance_func (hb_font_t *font, void *font_d } #endif -int WARN_UNUSED check_single_font(fontSetObj *fontset, char *fontkey, text_run *run, TextInfo *glyphs, int ignore_missing) { +static int check_single_font(fontSetObj *fontset, char *fontkey, text_run *run, TextInfo *glyphs, int ignore_missing) { int i; face_element *fcache = NULL; if(fontset && fontkey) { @@ -256,16 +256,23 @@ int WARN_UNUSED check_single_font(fontSetObj *fontset, char *fontkey, text_run * run->face = fcache; if(MS_UNLIKELY(!fcache)) return MS_FAILURE; for(i=0; ilength; i++) { - int codepoint = msGetGlyphIndex(fcache, glyphs->unicodes[run->offset+i]); + unsigned int codepoint = msGetGlyphIndex(fcache, glyphs->unicodes[run->offset+i]); if(codepoint || ignore_missing) + { + if( codepoint == 0 ) + { + msDebug("Unable to find glyph for codepoint %u. Using ? as fallback.\n", glyphs->unicodes[run->offset+i]); + codepoint = msGetGlyphIndex(fcache, '?'); + } glyphs->codepoints[run->offset+i] = codepoint; + } else return MS_FAILURE; } return MS_SUCCESS; } -int WARN_UNUSED get_face_for_run(fontSetObj *fontset, char *fontlist, text_run *run, TextInfo *glyphs) { +static int get_face_for_run(fontSetObj *fontset, char *fontlist, text_run *run, TextInfo *glyphs) { char *startfont, *endfont; #if defined(USE_HARFBUZZ) && defined(USE_FRIBIDI) const char *prefix = NULL;