# 57-libxft's report: reads the libxft-probe transcript and emits every BST # line. Run by tests/57-libxft.test, which hands in the pinned values with -v. function emit(id, ok, detail) { if (ok) printf "BST ok %s\n", id else printf "BST not ok %s : %s\n", id, substr(detail, 1, 100) } # A line of a section, by exact prefix; "" when the section did not carry one. function line(sec, pfx, xa, na, i) { na = split(SEC[sec], xa, "\n") for (i = 1; i <= na; i++) if (index(xa[i], pfx) == 1) return substr(xa[i], length(pfx) + 1) return "" } function has(sec, s) { return index(SEC[sec], s) > 0 } function count(sec, pfx, xa, na, i, n) { na = split(SEC[sec], xa, "\n"); n = 0 for (i = 1; i <= na; i++) if (index(xa[i], pfx) == 1) n++ return n } # FreeType fixed-point, exactly as ftcalc.c and ftobjs.c define them. floor # division throughout: FT_MulFix ends in an arithmetic right shift and # FT_PIX_FLOOR masks, and both round toward negative infinity -- which is what # makes the descender, the only negative operand here, the case that would # expose an int() truncation used in their place. function fl(a, b, q) { q = a / b; if (q < 0 && q != int(q)) return int(q) - 1; return int(q) } function mulfix(a, b, ab) { ab = a * b; return fl(ab + 32768 - (ab < 0 ? 1 : 0), 65536) } function pix_floor(x) { return fl(x, 64) * 64 } function pix_ceil(x) { return pix_floor(x + 63) } function pix_round(x) { return pix_floor(x + 32) } { if (index($0, "==XFT-BEGIN ") == 1) { s = substr($0, 13, length($0) - 14); next } if (index($0, "==XFT-END ") == 1) { s = ""; next } if ($0 == "==XFT-DONE==") { done = 1; next } if (index($0, "XFT-TOTAL ") == 1) { total = substr($0, 11) + 0; next } if (s != "") SEC[s] = SEC[s] $0 "\n" } END { # A probe that died half way leaves a truncated transcript whose sections all # read as absent, and an absent section is indistinguishable from a section # that matched nothing in a suite that only ever looks for what it expects. # So the end marker is checked before anything below is believed, and the # case count is floored as well -- the marker alone would not catch a probe # that reached its end having emitted half of its sections empty. emit("libxft/probe-complete", done, "no ==XFT-DONE== in the transcript") emit("libxft/probe-total", total >= min_total, "the probe emitted " total " measurements, below the floor of " min_total) n = split("version abi nodpy xlfd name subst info font config", S, " ") missing = "" for (i = 1; i <= n; i++) if (SEC[S[i]] == "") missing = missing " " S[i] emit("libxft/probe-sections", missing == "", "empty in the transcript:" missing) # ---------------- § 3 version ---------------- # XftGetVersion is compiled from XftVersion, which configure derives from the # tarball version -- so a mismatch between it and the header means the two # halves of the install came from different builds. v = line("version", "VER getversion ") emit("libxft/version", v + 0 == want_ver, "want " want_ver " got <" v ">") h = line("version", "VER header ") emit("libxft/version-header", h == "2.3.8 = " want_ver, "got <" h ">") pv = line("version", "VER recipe-pv ") emit("libxft/version-recipe-pv", pv == "2.3.8", "recipe PV is <" pv ">") # XftInit is FcInit and XftInitFtLibrary is FT_Init_FreeType. Both are # prerequisites for everything below, so a red here explains every later red # and nothing else does. emit("libxft/init-fontconfig", line("version", "VER init ") == "true", "XftInit(NULL) returned false -- FcInit failed") emit("libxft/init-freetype", line("version", "VER initftlibrary ") == "true", "XftInitFtLibrary returned false -- FT_Init_FreeType failed") # ---------------- § 4 abi ---------------- # Anchored to wasm32 rather than compared: FT_Fixed and FT_F26Dot6 are # "signed long", and a build that answered 8 here would have every # fixed-point figure below computed with headroom this target does not have. # a = line("abi", "ABI sizeof long ") emit("libxft/abi-ilp32", a == "4 pointer 4", "want <4 pointer 4> got <" a ">") a = line("abi", "ABI sizeof FT_Fixed ") emit("libxft/abi-ft-types", a == "4 FT_F26Dot6 4 FT_Int 4", "got <" a ">") # The load-flag constants the § 6 anchors are stated in terms of. Pinned from # FreeType 2.13.2s ftimage.h: a header whose values moved would make every # anchor below assert the wrong bit while still reading as an assertion. a = line("abi", "ABI FT_LOAD bits ") emit("libxft/abi-load-bits", a == "nobitmap 0x8 nohinting 0x2 color 0x100000 forceautohint 0x20 verticallayout 0x10 ignoreglobaladvance 0x200", "got <" a ">") a = line("abi", "ABI FT_LOAD targets ") emit("libxft/abi-load-targets", a == "mono 0x20000 light 0x10000 lcd 0x30000 lcdv 0x40000", "got <" a ">") # ---------------- § 5 xlfd ---------------- # XftXlfdParse is a pure decoder: no Display, no config, no fontconfig # matching. Every answer below is a property of the input string, so it is # stated outright rather than diffed against anything. # # Fixture 0 is the ordinary case: foundry, family, the medium weight, the # roman slant, pixel 16 and point 160 -- FC_SIZE is point/10 and FC_PIXEL_SIZE # is the pixel field, and XFT_XLFD carries the original string back. emit("libxft/xlfd-parsed", line("xlfd", "XLFD 0 parsed ") == "yes", "a complete XLFD did not parse") emit("libxft/xlfd-family", line("xlfd", "XLFD 0 family string ") == "\"bitstream vera sans\"", "got <" line("xlfd", "XLFD 0 family string ") ">") emit("libxft/xlfd-foundry", line("xlfd", "XLFD 0 foundry string ") == "\"bitstream\"", "got <" line("xlfd", "XLFD 0 foundry string ") ">") emit("libxft/xlfd-size", line("xlfd", "XLFD 0 size double ") == "16", "point 160 must give FC_SIZE 16, got <" line("xlfd", "XLFD 0 size double ") ">") emit("libxft/xlfd-pixelsize", line("xlfd", "XLFD 0 pixelsize double ") == "16", "got <" line("xlfd", "XLFD 0 pixelsize double ") ">") emit("libxft/xlfd-roundtrip", index(line("xlfd", "XLFD 0 xlfd string "), "-bitstream-bitstream vera sans-") == 2, "XFT_XLFD did not carry the original string: <" line("xlfd", "XLFD 0 xlfd string ") ">") # The symbolic tables, one fixture each. _XftMatchSymbolic is case-insensitive # and falls back to medium/roman for a name it does not know -- the fallback is # what fixture 5 is for, and it is the arm a decoder that had stopped matching # at all would ALSO pass, which is why the four known names are checked beside it. split("1:weight:50 2:weight:180 3:weight:210 4:weight:100 5:weight:100", W, " ") split("first light demibold black regular unknown", WN, " ") for (i = 1; i <= 5; i++) { split(W[i], kv, ":") g = line("xlfd", "XLFD " kv[1] " " kv[2] " int ") emit("libxft/xlfd-weight-" WN[i + 1], g == kv[3], "want " kv[3] " got <" g ">") } emit("libxft/xlfd-slant-italic", line("xlfd", "XLFD 1 slant int ") == "100", "got <" line("xlfd", "XLFD 1 slant int ") ">") emit("libxft/xlfd-slant-oblique", line("xlfd", "XLFD 2 slant int ") == "110", "got <" line("xlfd", "XLFD 2 slant int ") ">") emit("libxft/xlfd-slant-unknown", line("xlfd", "XLFD 5 slant int ") == "0", "an unknown slant must fall back to roman, got <" line("xlfd", "XLFD 5 slant int ") ">") # THE ONE PIECE OF ARITHMETIC IN THE DECODER. With the pixel field `*` and # point 140 at resy 100, XftXlfdParse derives pixelsize as # point * resy / 720.0 = 14000/720 = 19.4444... in double # (xftxlfd.c:154). Six significant figures, which is what the probe prints. g = line("xlfd", "XLFD 6 pixelsize double ") emit("libxft/xlfd-derived-pixelsize", g == "19.4444", "point 140 at resy 100 must give 19.4444 px, got <" g ">") emit("libxft/xlfd-derived-size", line("xlfd", "XLFD 6 size double ") == "14", "got <" line("xlfd", "XLFD 6 size double ") ">") # Point absent: no FC_SIZE at all, and pixelsize straight off the pixel field. emit("libxft/xlfd-nopoint-size", line("xlfd", "XLFD 7 size ") == "absent", "a `*` point field must add no FC_SIZE, got <" line("xlfd", "XLFD 7 size ") ">") emit("libxft/xlfd-nopoint-pixelsize", line("xlfd", "XLFD 7 pixelsize double ") == "18", "got <" line("xlfd", "XLFD 7 pixelsize double ") ">") # A wildcard foundry or family is DROPPED, not stored as the literal "*". # A decoder that stored it would hand the matcher a family nothing can match, # and the failure would surface as "no font found" three layers away. emit("libxft/xlfd-wildcard-family", line("xlfd", "XLFD 8 family ") == "absent", "got <" line("xlfd", "XLFD 8 family ") ">") emit("libxft/xlfd-wildcard-foundry", line("xlfd", "XLFD 8 foundry ") == "absent", "got <" line("xlfd", "XLFD 8 foundry ") ">") # The four documented refusals. A decoder that accepted a truncated XLFD # would build a pattern out of whatever followed it in memory. split("9:no-leading-dash 10:too-few-fields 11:extra-field 12:zero-pixel", R, " ") for (i = 1; i <= 4; i++) { split(R[i], kv, ":") g = line("xlfd", "XLFD " kv[1] " parsed ") emit("libxft/xlfd-refuse-" kv[2], g == "no", "was accepted (parsed <" g ">)") } # ---------------- § 6 name ---------------- # FcNameParse/FcNameUnparse with an Xft signature. The parse side is # fontconfig s; what libXft owns is the UNPARSE buffer contract, and it has # three outcomes rather than two. emit("libxft/name-parsed", line("name", "NAME 0 parsed ") == "yes", "a simple name did not parse") emit("libxft/name-family", line("name", "NAME 0 family string ") == "\"Bitstream Vera Sans\"", "got <" line("name", "NAME 0 family string ") ">") emit("libxft/name-size", line("name", "NAME 0 size double ") == "12", "got <" line("name", "NAME 0 size double ") ">") emit("libxft/name-antialias", line("name", "NAME 2 antialias bool ") == "false", "antialias=false did not survive the parse") emit("libxft/name-hintstyle", line("name", "NAME 2 hintstyle int ") == "1", "hintstyle=hintslight must be 1, got <" line("name", "NAME 2 hintstyle int ") ">") emit("libxft/name-pixelsize", line("name", "NAME 4 pixelsize double ") == "13.5", "got <" line("name", "NAME 4 pixelsize double ") ">") emit("libxft/name-dpi", line("name", "NAME 4 dpi double ") == "96", "got <" line("name", "NAME 4 dpi double ") ">") # A 4096-byte buffer takes every fixture whole; a 1-byte one cannot even hold # the terminator. Both must be reported truthfully -- the second is the arm # that returns false AFTER writing, and a caller that trusted the return # value would otherwise read a truncated family as a whole one. nb = 0; nb_ok = 0; nb_short = 0; nb_short_ok = 0 for (k = 0; k <= 5; k++) { g = line("name", "NAME " k " unparse len 4096 rc ") if (g != "") { nb++; if (index(g, "true out ") == 1) nb_ok++ } g = line("name", "NAME " k " unparse len 1 rc ") if (g != "") { nb_short++; if (index(g, "false out \"\"") == 1) nb_short_ok++ } } emit("libxft/name-unparse-full", nb > 0 && nb_ok == nb, nb_ok " of " nb " fixtures unparsed into a 4096-byte buffer") emit("libxft/name-unparse-len1", nb_short > 0 && nb_short_ok == nb_short, nb_short_ok " of " nb_short " fixtures reported false and an empty string at len 1") # The round trip: unparse, re-parse, unparse again. Two passes agreeing is # what says the two halves are inverse on these patterns rather than merely # both running -- an encoder that dropped an attribute would agree with # itself on the second pass and disagree here on the first. nrt = count("name", "NAME 0 roundtrip") + count("name", "NAME 1 roundtrip") \ + count("name", "NAME 2 roundtrip") + count("name", "NAME 3 roundtrip") \ + count("name", "NAME 4 roundtrip") + count("name", "NAME 5 roundtrip") emit("libxft/name-roundtrip", nrt == 6 && !has("name", "roundtrip DIFFERS") \ && !has("name", "roundtrip parse-failed") \ && !has("name", "roundtrip unparse-failed"), nrt " of 6 round trips reported; a DIFFERS or a failure is present") # ---------------- § 7 subst ---------------- # XftDefaultSubstitute, three arms. Every value below is a literal in # xftdpy.c except FC_DPI, which is computed from the screen -- and the probe # fabricates that screen at 1000 px over 254 mm precisely so the answer is # exactly 100 and not a number nobody can check. emit("libxft/subst-screen", line("subst", "SUBST screen ") == "1000px 254mm", "the probe did not fabricate the screen this section is read against") emit("libxft/subst-hasrender", line("subst", "SUBST empty hasrender ") == "false", "XftDefaultHasRender must be false on an image with no server") emit("libxft/subst-defaultset", line("subst", "SUBST empty defaultset ") == "true", "XftDefaultSet refused") # Arm 1: an EMPTY defaults pattern, so every attribute takes its hard-coded # default. This is the arm that says the twelve branches ran at all. split("render:bool false" "\036" "antialias:bool true" "\036" "embolden:bool false" \ "\036" "hinting:bool true" "\036" "hintstyle:int 3" "\036" "autohint:bool false" \ "\036" "rgba:int 0" "\036" "lcdfilter:int 1" "\036" "minspace:bool false" \ "\036" "dpi:double 100" "\036" "scale:double 1" \ "\036" "maxglyphmemory:int 1048576", D, "\036") for (i = 1; i <= 12; i++) { j = index(D[i], ":"); o = substr(D[i], 1, j - 1); w = substr(D[i], j + 1) g = line("subst", "SUBST empty " o " ") emit("libxft/subst-default-" o, g == w, "want <" w "> got <" g ">") } # Arm 2: the defaults pattern supplies six of them, and each must WIN over # the compiled-in constant. Without this arm a library that ignored its # resources entirely would pass arm 1 exactly. split("antialias:bool false" "\036" "embolden:bool true" "\036" "hintstyle:int 1" \ "\036" "rgba:int 4" "\036" "lcdfilter:int 2" "\036" "dpi:double 96" \ "\036" "scale:double 2" "\036" "maxglyphmemory:int 4096", E, "\036") for (i = 1; i <= 8; i++) { j = index(E[i], ":"); o = substr(E[i], 1, j - 1); w = substr(E[i], j + 1) g = line("subst", "SUBST resources " o " ") emit("libxft/subst-resource-" o, g == w, "want <" w "> got <" g ">") } # ...and one it does NOT supply still takes the default, so arm 2 is not # simply echoing the defaults pattern back. emit("libxft/subst-resource-untouched", line("subst", "SUBST resources hinting bool ") == "true", "got <" line("subst", "SUBST resources hinting bool ") ">") # Arm 3: the INPUT pattern already carries them, and every branch of # XftDefaultSubstitute is `if (FcPatternGet (...) == FcResultNoMatch)`, so # the input must win over both the resources and the constants. A # substitution pass that overwrote a caller s explicit antialias=false is a # real and much-reported class of Xft bug. split("antialias:bool true" "\036" "embolden:bool false" "\036" "hintstyle:int 0" \ "\036" "rgba:int 1" "\036" "dpi:double 300" "\036" "render:bool true", F, "\036") for (i = 1; i <= 6; i++) { j = index(F[i], ":"); o = substr(F[i], 1, j - 1); w = substr(F[i], j + 1) g = line("subst", "SUBST preset " o " ") emit("libxft/subst-preset-" o, g == w, "want <" w "> got <" g ">") } # FcDefaultSubstitute runs last and is what turns FC_SIZE into FC_PIXEL_SIZE # using the dpi and scale just substituted. At 12 pt, 100 dpi and scale 1 # that is 12 * 100/72 = 16.6667 px. It is fontconfig s arithmetic, not # libXft s, but it is reached ONLY through this call -- so a port whose # XftDefaultSubstitute stopped supplying FC_DPI would present here. g = line("subst", "SUBST empty pixelsize double ") emit("libxft/subst-pixelsize-from-dpi", g == "16.6667", "12pt at 100dpi must give 16.6667px, got <" g ">") # ---------------- § 8 info ---------------- # XftFontInfoFill: pattern -> FreeType load flags. Twenty-three arms, each # differing from `plain` in exactly one attribute, so the anchors below name # the bit that attribute is supposed to set and nothing else. plain = line("info", "INFO plain load_flags ") emit("libxft/info-plain-flags", plain == "0x100008", "the default arm must be FT_LOAD_COLOR|FT_LOAD_NO_BITMAP (antialias on," \ " no embedded bitmaps), got <" plain ">") emit("libxft/info-plain-size", line("info", "INFO plain xsize ") == "1024 ysize 1024", "16 px must be 1024 in 26.6, got <" line("info", "INFO plain xsize ") ">") emit("libxft/info-plain-render", line("info", "INFO plain render ") == "false", "fi->render must be false with no Render extension -- it is what keeps" \ " the whole glyph path client-side") emit("libxft/info-plain-transform", line("info", "INFO plain matrix ") == "65536 0 0 65536 transform false", "got <" line("info", "INFO plain matrix ") ">") # Each arm names the bit it must set relative to `plain`, computed rather # than typed, so a change to plain does not silently invalidate the rest. # FT_LOAD_TARGET_MONO replaces the antialias arms target rather than adding # to it, so noaa is stated whole. emit("libxft/info-noaa", line("info", "INFO noaa load_flags ") == "0x120000", "antialias=false must be FT_LOAD_COLOR|FT_LOAD_TARGET_MONO and NOT" \ " FT_LOAD_NO_BITMAP, got <" line("info", "INFO noaa load_flags ") ">") emit("libxft/info-noaa-antialias", line("info", "INFO noaa antialias ") == "false embolden false color false", "got <" line("info", "INFO noaa antialias ") ">") emit("libxft/info-nohint", line("info", "INFO nohint load_flags ") == "0x10000a", "hinting=false must add FT_LOAD_NO_HINTING, got <" line("info", "INFO nohint load_flags ") ">") emit("libxft/info-hintnone", line("info", "INFO hintnone load_flags ") == "0x10000a", "hintstyle=none must add FT_LOAD_NO_HINTING, got <" line("info", "INFO hintnone load_flags ") ">") emit("libxft/info-hintslight", line("info", "INFO hintslight load_flags ") == "0x110008", "hintstyle=slight must select FT_LOAD_TARGET_LIGHT, got <" line("info", "INFO hintslight load_flags ") ">") emit("libxft/info-hintmedium", line("info", "INFO hintmedium load_flags ") == "0x110008", "hintstyle=medium must select FT_LOAD_TARGET_LIGHT, got <" line("info", "INFO hintmedium load_flags ") ">") emit("libxft/info-autohint", line("info", "INFO autohint load_flags ") == "0x100028", "autohint must add FT_LOAD_FORCE_AUTOHINT, got <" line("info", "INFO autohint load_flags ") ">") emit("libxft/info-vertical", line("info", "INFO vertical load_flags ") == "0x100018", "verticallayout must add FT_LOAD_VERTICAL_LAYOUT, got <" line("info", "INFO vertical load_flags ") ">") emit("libxft/info-noglobal", line("info", "INFO noglobal load_flags ") == "0x100208", "globaladvance=false must add FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, got <" line("info", "INFO noglobal load_flags ") ">") emit("libxft/info-embbitmap", line("info", "INFO embbitmap load_flags ") == "0x100000", "embeddedbitmap=true must CLEAR FT_LOAD_NO_BITMAP, got <" line("info", "INFO embbitmap load_flags ") ">") # The subpixel arms. rgba is what selects an LCD target, and the horizontal # and vertical orders select DIFFERENT ones -- a port that collapsed them # would render vertically striped panels with horizontal filtering. emit("libxft/info-rgba-rgb", line("info", "INFO rgba-rgb load_flags ") == "0x130008", "rgba=rgb must select FT_LOAD_TARGET_LCD, got <" line("info", "INFO rgba-rgb load_flags ") ">") emit("libxft/info-rgba-vrgb", line("info", "INFO rgba-vrgb load_flags ") == "0x140008", "rgba=vrgb must select FT_LOAD_TARGET_LCD_V, got <" line("info", "INFO rgba-vrgb load_flags ") ">") emit("libxft/info-rgba-none", line("info", "INFO rgba-none load_flags ") == "0x100008", "rgba=none must select no LCD target, got <" line("info", "INFO rgba-none load_flags ") ">") emit("libxft/info-rgba-value", line("info", "INFO rgba-vrgb rgba ") == "3 lcd_filter 1", "got <" line("info", "INFO rgba-vrgb rgba ") ">") emit("libxft/info-lcdfilter", line("info", "INFO lcdfilter rgba ") == "0 lcd_filter 3", "lcdfilter must reach fi->lcd_filter untouched, got <" line("info", "INFO lcdfilter rgba ") ">") # embolden, spacing, minspace and charwidth are carried rather than folded # into load_flags -- except charwidth, which also forces FC_MONO spacing # (xftfreetype.c:700). emit("libxft/info-embolden", line("info", "INFO embolden antialias ") == "true embolden true color false", "got <" line("info", "INFO embolden antialias ") ">") emit("libxft/info-spacing", line("info", "INFO spacing spacing ") == "100 minspace false char_width 0", "got <" line("info", "INFO spacing spacing ") ">") emit("libxft/info-minspace", line("info", "INFO minspace spacing ") == "0 minspace true char_width 0", "got <" line("info", "INFO minspace spacing ") ">") emit("libxft/info-charwidth", line("info", "INFO charwidth spacing ") == "100 minspace false char_width 9", "a non-zero charwidth must also force FC_MONO spacing, got <" line("info", "INFO charwidth spacing ") ">") # aspect scales the X size only: xsize = pixelsize * aspect * 64. At 16 px # and aspect 0.5 that is 512 against a ysize of 1024. It is the only place # the two sizes are allowed to differ, and a port that had wired them # together would be invisible everywhere else. emit("libxft/info-aspect", line("info", "INFO aspect xsize ") == "512 ysize 1024", "aspect 0.5 at 16px must give xsize 512 ysize 1024, got <" line("info", "INFO aspect xsize ") ">") # The matrix arms. fi->transform is set iff the matrix is not the identity, # and it forces FT_LOAD_NO_BITMAP regardless of antialias -- so the identity # arm and the scale arm differing in `transform` is the wiring being checked. emit("libxft/info-matrix-identity", line("info", "INFO matrix-id matrix ") == "65536 0 0 65536 transform false", "an identity FC_MATRIX must not set transform, got <" line("info", "INFO matrix-id matrix ") ">") emit("libxft/info-matrix-scale", line("info", "INFO matrix-scale matrix ") == "131072 0 0 32768 transform true", "xx=2 yy=0.5 must be 131072/32768 in 16.16, got <" line("info", "INFO matrix-scale matrix ") ">") emit("libxft/info-matrix-shear", line("info", "INFO matrix-shear matrix ") == "65536 16384 -8192 65536 transform true", "xy=0.25 yx=-0.125 must be 16384/-8192 in 16.16, and the pair must not" \ " be transposed, got <" line("info", "INFO matrix-shear matrix ") ">") # THE CHAINED MATRIX IS THE ARITHMETIC THIS ARM IS MOST EXPOSED ON. A second # FC_MATRIX value is multiplied into the first through DOUBLE and back to # 16.16 (xftfreetype.c:495-508) -- the only place in the library where a # fixed-point value makes a round trip through floating point, and therefore # the one a 32-bit FT_Fixed could truncate in. # A = [2 0.25; 0 1] (the first value), B = [1 0; -0.125 0.5] (the second) # xftfreetype.c calls FcMatrixMultiply(&fm1, B, &fm1), so the result is B*A: # xx=2, xy=0.25, yx=-0.25, yy=0.46875 -- 131072, 16384, -16384, 30720. emit("libxft/info-matrix-chain", line("info", "INFO matrix-chain matrix ") == "131072 16384 -16384 30720 transform true", "the second FC_MATRIX must be multiplied into the first, got <" line("info", "INFO matrix-chain matrix ") ">") # The hash contract. Never the VALUE -- the struct s second word is a heap # pointer, so the number is a fact about malloc rather than about this port. # What the hash is FOR is the cache bucket XftFontOpenInfo walks, and these # four properties are the whole of that contract. emit("libxft/info-hash-equal", line("info", "INFO hash equal-patterns ") == "agree", "two fills of the same pattern must hash equal") emit("libxft/info-hash-differs", line("info", "INFO hash differing-size ") == "differ", "a differing pixel size must not hash equal -- a cache keyed on this" \ " would return the wrong size s font") emit("libxft/info-equal-same", line("info", "INFO equal same ") == "true", "XftFontInfoEqual said two identical fills differ") emit("libxft/info-equal-differs", line("info", "INFO equal differing-size ") == "false", "XftFontInfoEqual said two different sizes are equal") # ---------------- § 9 font ---------------- # The face, the scaler and the rasteriser. This is the section that says the # port is usable rather than merely present. # # The DESIGN metrics are anchored against 57-freetype, which pins this exact # file on this exact image. A red in BOTH suites is a FreeType problem; a red # here alone is libXft reaching the wrong file or the wrong face index. g = line("font", "FONT 16 face upem ") emit("libxft/font-face-upem", g == upem " numglyphs " numglyphs, "want <" upem " numglyphs " numglyphs "> got <" g "> (57-freetype pins both)") g = line("font", "FONT 16 face design ") emit("libxft/font-face-design", g == "ascender " d_asc " descender " d_desc " height " d_height " maxadvw " d_maxadv, "got <" g "> (57-freetype pins these)") emit("libxft/font-face-scalable", line("font", "FONT 16 face scalable ") == "true", "Vera is a scalable TrueType face; a false here means a bitmap strike" \ " was selected instead") # THE SCALER, COMPUTED RATHER THAN PINNED. FT_Set_Char_Size(face, xsize, # ysize, 0, 0) with zero resolution makes FT_Request_Metrics take the 26.6 # size as the scaled height, so y_scale = FT_DivFix(ysize, upem) and # ft_recompute_scaled_metrics grid-fits each metric with its own rounding: # ceil for the ascender, floor for the descender, round for the height and # the max advance (ftobjs.c:3180-3191, GRID_FIT_METRICS being defined # unconditionally at ftobjs.c:102). # # Deriving it here rather than writing the numbers down is what keeps this # honest: the answer is recomputed from the design metrics the same run # reported, so it cannot be a transcription of what a passing build once # printed. for (k = 1; k <= 3; k++) { px = (k == 1 ? 8 : (k == 2 ? 16 : 32)) ys = px * 64 yscale = int((ys * 65536) / upem) want_asc = pix_ceil(mulfix(d_asc, yscale)) want_desc = pix_floor(mulfix(d_desc, yscale)) want_hgt = pix_round(mulfix(d_height, yscale)) want_adv = pix_round(mulfix(d_maxadv, yscale)) g = line("font", "FONT " px " face scaled ") w = "ascender " want_asc " descender " want_desc " height " want_hgt \ " maxadvance " want_adv emit("libxft/font-scaled-" px, g == w, "want <" w "> got <" g ">") # ...and libXft s own arithmetic over those: ascent is the ascender # shifted down six, descent is the NEGATED descender, height and # max_advance_width are shifted (xftfreetype.c:939-961). This is the # port s code, and the shift of a negative is where a compiler that # emitted a logical shift instead of an arithmetic one would show. # The prefix carries `ascent` deliberately: `line()` returns the FIRST # line with the prefix, and "FONT 16 " alone would match the `opened` # line two above -- a green that measured nothing. w = fl(want_asc, 64) " descent " (-fl(want_desc, 64)) \ " height " fl(want_hgt, 64) " maxadvance " fl(want_adv, 64) g = line("font", "FONT " px " ascent ") emit("libxft/font-metrics-" px, g == w, "want got ") emit("libxft/font-opened-" px, line("font", "FONT " px " opened ") == "yes", "XftFontOpenPattern returned NULL at " px " px") } # The glyph index path, out of libXft s own Unicode hash table rather than # out of FreeType directly. Asking twice must give the same answer -- the # first call populates the table and the second reads it, so a disagreement # is a cache that stores something other than what it computed. emit("libxft/font-charindex-cached", !has("font", "cached DIFFERS"), "a second XftCharIndex disagreed with the first") # ASCII must be present and must not be glyph 0 (.notdef); the CJK and the # astral codepoint must be absent from a Latin font, which is what says the # charset is real rather than a blanket yes. g = line("font", "FONT 16 charindex U+0041 ") emit("libxft/font-charindex-A", index(g, "glyph 0 ") != 1 && index(g, "exists true") > 0, "U+0041 must map to a real glyph in Vera, got <" g ">") g = line("font", "FONT 16 charindex U+4E2D ") emit("libxft/font-charindex-absent", index(g, "exists false") > 0, "U+4E2D must be absent from a Latin face, got <" g ">") g = line("font", "FONT 16 charindex U+0020 ") emit("libxft/font-charindex-space", index(g, "exists true") > 0, "U+0020 must be present, got <" g ">") # THE RASTERISER. XftGlyphExtents -> XftFontCheckGlyph -> XftFontLoadGlyphs, # all client-side because font->format is NULL. A capital A at 16 px must # have a non-zero box and advance; the SPACE glyph must have an advance and # NO box, which is the pair that separates "the rasteriser ran" from "the # metrics were copied from somewhere". g = line("font", "FONT 16 glyphextents U+0041 ") split(g, GA, " ") emit("libxft/font-glyph-A-box", GA[2] + 0 > 0 && GA[4] + 0 > 0, "U+0041 rasterised to an empty box: <" g ">") emit("libxft/font-glyph-A-advance", GA[10] + 0 > 0, "U+0041 has no advance: <" g ">") g = line("font", "FONT 16 glyphextents U+0020 ") split(g, GS, " ") emit("libxft/font-glyph-space", GS[2] + 0 == 0 && GS[4] + 0 == 0 && GS[10] + 0 > 0, "the space glyph must advance and rasterise nothing: <" g ">") # A four-glyph run must advance further than any one of its glyphs, and its # box must be at least as wide. A run accumulator that reset per glyph would # pass every single-glyph case above. g = line("font", "FONT 16 glyphextents run4 ") split(g, GR, " ") emit("libxft/font-glyph-run", GR[10] + 0 > GA[10] + 0 && GR[2] + 0 > 0, "a four-glyph run did not advance further than one glyph: <" g ">") # The four public text-extent entry points over ONE string in four # encodings. They must agree on the advance: the same glyphs advance the # same distance however the caller spelled them, and a UTF-8 decoder that # had gone wrong on this arm is exactly what breaks that agreement. g = line("font", "FONT 16 extents8 ") split(g, E8, " ") x8 = E8[10] + 0 emit("libxft/font-extents8", x8 > 0 && E8[2] + 0 > 0, "XftTextExtents8 returned an empty extent: <" g ">") g = line("font", "FONT 16 extents16 xoff ") split(g, E16, " ") emit("libxft/font-extents16-agrees", E16[1] + 0 == x8, "XftTextExtents16 advanced " E16[1] " where XftTextExtents8 advanced " x8) g = line("font", "FONT 16 extents32 xoff ") split(g, E32, " ") emit("libxft/font-extents32-agrees", E32[1] + 0 == x8, "XftTextExtents32 advanced " E32[1] " where XftTextExtents8 advanced " x8) g = line("font", "FONT 16 extentsutf8 xoff ") split(g, EU, " ") emit("libxft/font-extentsutf8-agrees", EU[1] + 0 == x8, "XftTextExtentsUtf8 advanced " EU[1] " where XftTextExtents8 advanced " x8) # A genuinely multi-byte string, so the decoder is driven over something an # 8-bit string cannot express: 9 bytes, 6 characters. Its advance must be # non-zero and SHORTER than the fifteen-character string above -- a decoder # that treated each byte as a character would produce nine advances instead # of six, and this is the assertion that separates the two. g = line("font", "FONT 16 extentsutf8-mb ") split(g, EM, " ") emit("libxft/font-utf8-multibyte", EM[4] + 0 > 0 && EM[4] + 0 < x8, "the multi-byte string advanced " EM[4] " against " x8 " for fifteen" \ " Latin characters: <" g ">") # The empty string: every field zero, into a buffer the probe filled with # 0xff first. An entry point that left the output untouched is silent in # every consumer that only ever measures non-empty text. g = line("font", "FONT 16 extents-empty ") emit("libxft/font-extents-empty", g == "w 0 h 0 x 0 y 0 xoff 0 yoff 0", "want all zero, got <" g ">") # The font cache: a second open of an equal pattern must return the SAME # XftFont. That is XftFontInfoEqual driving XftFontOpenInfo s bucket walk, # and it is what stops a client that opens a font per draw from opening a # face per draw. emit("libxft/font-reopen-cached", line("font", "FONT 16 reopen ") == "same", "a second open of an equal pattern produced a different XftFont") emit("libxft/font-copy", line("font", "FONT 16 copy ") == "same", "XftFontCopy did not return the same object") # The refusals. A pattern with no FC_PIXEL_SIZE must be refused rather than # opened at some accidental size (xftfreetype.c:421), and a file that is not # there must fail at the face open rather than produce a font with no face. emit("libxft/font-refuse-nosize", line("font", "FONT nosize open ") == "refused", "a pattern with no pixelsize was opened") emit("libxft/font-refuse-nofile", line("font", "FONT nofile open ") == "refused", "a nonexistent font file was opened") # The charset the font carries. Bounded from BELOW: it is a property of the # file and 57-freetype pins the glyph count at " numglyphs ", so a charset # larger than that would mean codepoints mapping to glyphs that do not exist. g = line("font", "FONT 16 charset count ") emit("libxft/font-charset", g + 0 > 100 && g + 0 <= numglyphs, "the charset covers " g " codepoints, outside (100, " numglyphs "]") # ---------------- § 10 config ---------------- # WHY THIS SUITE HAS A 900s BOUND AND NOT 240s. This section is the only one # whose cost is set by the IMAGE rather than by the probe. XftListFonts makes # fontconfig materialise the whole font set, and four of the seven font # directories on this image ship no fontconfig cache -- /usr/share/fonts/ttf, # .../truetype, .../X11/misc and .../X11/TTF, 109 files and 25 MB between # them -- so they are rescanned in the guest on every run. Each rescan opens a # font through FreeType, whose ftsystem.c maps the file PROT_READ MAP_PRIVATE, # and the runtime refused that mapping above a length threshold # (linux-in-the-browser#165, printed as: file-backed mmap REFUSED (pread64 # population failed) ... errno 517), leaving the slow read(2) fallback to # carry all 25 MB. Measured # 2026-08-31: the suite was killed at the old 240s bound having emitted 13 # cases, on an image where the same 13 cases and the probe had together taken # 7s a day earlier -- the difference being the fonts that arrived in the image # in between. That is real work on a defective mapping path, not a hang, so # the bound is raised rather than the section weakened: § 10 measures the font # set of the image through the matcher, and narrowing it to the fixture would # measure the data directory of this suite instead. g = line("config", "CONFIG list count ") emit("libxft/config-list", g + 0 > 0, "XftListFonts found no fonts at all on this image -- either fontconfig" \ " has no configuration or no font package is installed") # Every matcher arm must answer, including the one asking for a family that # does not exist: fontconfig s contract is that a match ALWAYS succeeds with # the best available font, and a client that got NULL there would have no # font to draw with at all. nm = 0; nm_file = 0; nm_open = 0 for (i = 1; i <= 5; i++) { fam = (i == 1 ? "sans-serif" : (i == 2 ? "serif" : (i == 3 ? "monospace" : \ (i == 4 ? "Bitstream Vera Sans" : "no-such-family-at-all")))) g = line("config", "CONFIG match \"" fam "\" result ") if (g != "") { nm++ if (index(g, "file \"/") > 0) nm_file++ } g = line("config", "CONFIG match \"" fam "\" opened ascent ") if (g != "") nm_open++ } emit("libxft/config-match-all", nm == 5, nm " of 5 match arms answered") emit("libxft/config-match-file", nm_file == 5, nm_file " of 5 matches carried an absolute font file path") emit("libxft/config-match-opens", nm_open == 5, nm_open " of 5 matched patterns opened as a font -- the full client" \ " chain, name to face, with no server") # The Xft-owned attributes must have survived XftFontMatch. If they had not, # XftFontOpenPattern would default them a SECOND time, and a client that set # antialias=false would silently get antialiased text. g = line("config", "CONFIG match \"sans-serif\" xftattrs ") emit("libxft/config-match-xftattrs", index(g, "antialias ") == 1 && index(g, "hintstyle ") > 0 && index(g, "dpi 100") > 0, "the Xft attributes did not survive the match: <" g ">") # The three convenience openers, each XftFontMatch plus XftFontOpenPattern. emit("libxft/config-openname", index(line("config", "CONFIG openname "), "ascent ") == 1, "XftFontOpenName(\"sans-serif-12\") returned NULL") emit("libxft/config-openxlfd", index(line("config", "CONFIG openxlfd "), "ascent ") == 1, "XftFontOpenXlfd returned NULL -- the XLFD arm of the opener") emit("libxft/config-openva", index(line("config", "CONFIG openva "), "ascent ") == 1, "XftFontOpen(FC_FAMILY, ..., (char *) 0) returned NULL -- the varargs arm") # ---------------- § 11 nodpy ---------------- # THE HONEST-FAILURE ARM, measured rather than asserted. Everything that # DRAWS -- XftDrawCreate, XftColorAllocName, XftDrawStringUtf8 -- needs a # Drawable, and a Drawable needs a server. This image has none, so those # entry points are not merely untested here: they are unreachable, and this # is where that is established rather than claimed. emit("libxft/nodpy", line("nodpy", "NODPY xopendisplay ") == "null", "a display OPENED on this image -- the drawing half is reachable and" \ " this suite does not cover it; that is a change worth a ticket") }