font-variant-features.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // data associated with gsubtest test font for testing font features
  2. // prefix
  3. gPrefix = "";
  4. // equivalent properties
  5. // setting prop: value should match the specific feature settings listed
  6. //
  7. // each of these tests evaluate whether a given feature is enabled as required
  8. // and also whether features that shouldn't be enabled are or not.
  9. var gPropertyData = [
  10. // font-variant (shorthand)
  11. // valid values
  12. { prop: "font-variant", value: "normal", features: {"smcp": 0} },
  13. { prop: "font-variant", value: "small-caps", features: {"smcp": 1, "c2sc": 0} },
  14. { prop: "font-variant", value: "none", features: {"liga": 0, "dlig": 0, "clig": 0, "calt": 0, "hlig": 0} },
  15. { prop: "font-variant", value: "all-small-caps", features: {"smcp": 1, "c2sc": 1, "pcap": 0} },
  16. { prop: "font-variant", value: "common-ligatures no-discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
  17. { prop: "font-variant", value: "proportional-nums slashed-zero diagonal-fractions oldstyle-nums ordinal", features: {"frac": 1, "afrc": 0, "tnum": 0, "pnum": 1, "onum": 1, "ordn": 1, "zero": 1} },
  18. { prop: "font-variant", value: "all-small-caps traditional", features: {"smcp": 1, "c2sc": 1, "pcap": 0, "trad": 1, "jp04": 0} },
  19. { prop: "font-variant", value: "styleset(out-of-bounds1, out-of-bounds2) traditional", features: {"ss00": 0, "ss01": 0, "ss99": 0, "trad": 1} }, // out-of-bounds values but not invalid syntax
  20. { prop: "font-variant", value: "styleset(ok-alt-a, ok-alt-b) historical-forms", features: {"ss01": 1, "ss02": 0, "ss03": 1, "ss04": 0, "ss05": 1, "ss19": 1, "ss20": 0, "hist": 1, "hlig": 0} },
  21. { prop: "font-variant", value: "traditional historical-forms styleset(ok-alt-a, ok-alt-b)", features: {"trad": 1, "ss01": 1, "ss02": 0, "ss03": 1, "ss04": 0, "ss05": 1, "ss19": 1, "ss20": 0, "hist": 1, "hlig": 0} },
  22. { prop: "font-variant", value: "styleset(scope-test2)", features: {"ss23": 0, "ss24": 1, "ss01": 1} },
  23. { prop: "font-variant", value: "character-variant(scope-test2)", features: {"cv23": 2, "cv24": 0, "cv01": 0} },
  24. // invalid values
  25. { prop: "font-variant", value: "normal small-caps", features: {"smcp": 0}, invalid: true },
  26. { prop: "font-variant", value: "common-ligatures none", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  27. { prop: "font-variant", value: "none common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  28. { prop: "font-variant", value: "small-caps potato", features: {"smcp": 0}, invalid: true },
  29. { prop: "font-variant", value: "common-ligatures traditional no-common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "trad": 0}, invalid: true },
  30. { prop: "font-variant", value: "common-ligatures traditional common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "trad": 0}, invalid: true },
  31. { prop: "font-variant", value: "small-caps jis83 all-small-caps", features: {"smcp": 0, "c2sc": 0, "jp83": 0}, invalid: true },
  32. { prop: "font-variant", value: "lining-nums traditional slashed-zero ordinal normal", features: {"lnum": 0, "onum": 0, "zero": 0, "trad": 0}, invalid: true },
  33. { prop: "font-variant", value: "diagonal-fractions stacked-fractions", features: {"frac": 0, "afrc": 0}, invalid: true },
  34. { prop: "font-variant", value: "stacked-fractions diagonal-fractions historical-ligatures", features: {"frac": 0, "afrc": 0, "hlig": 0}, invalid: true },
  35. { prop: "font-variant", value: "super sub", features: {"subs": 0, "sups": 0}, invalid: true },
  36. { prop: "font-variant", value: "super historical-ligatures sub", features: {"subs": 0, "sups": 0, "hlig": 0}, invalid: true },
  37. { prop: "font-variant", value: "annotation(circled) annotation(circled)", features: {"nalt": 0, "lnum": 0, "onum": 0, "pnum": 0}, invalid: true },
  38. // font-variant-alternates
  39. // valid values
  40. { prop: "font-variant-alternates", value: "normal", features: {"salt": 0, "swsh": 0} },
  41. { prop: "font-variant-alternates", value: "historical-forms", features: {"hist": 1, "hlig": 0} },
  42. { prop: "font-variant-alternates", value: "styleset(ok-alt-a, ok-alt-b)", features: {"ss01": 1, "ss02": 0, "ss03": 1, "ss04": 0, "ss05": 1, "ss19": 1, "ss20": 0} },
  43. { prop: "font-variant-alternates", value: "styleset(ok-alt-a, ok-alt-b) historical-forms", features: {"ss01": 1, "ss02": 0, "ss03": 1, "ss04": 0, "ss05": 1, "ss19": 1, "ss20": 0, "hist": 1, "hlig": 0} },
  44. { prop: "font-variant-alternates", value: "historical-forms styleset(ok-alt-a, ok-alt-b)", features: {"ss01": 1, "ss02": 0, "ss03": 1, "ss04": 0, "ss05": 1, "ss19": 1, "ss20": 0, "hist": 1, "hlig": 0} },
  45. { prop: "font-variant-alternates", value: "character-variant(ok-1)", features: {"cv78": 2, "cv79": 0, "cv77": 0} },
  46. { prop: "font-variant-alternates", value: "character-variant(ok-1, ok-3)", features: {"cv78": 2, "cv79": 0, "cv77": 0, "cv23": 1, "cv22": 0, "cv24": 0} },
  47. { prop: "font-variant-alternates", value: "annotation(circled)", features: {"nalt": 1} },
  48. { prop: "font-variant-alternates", value: "styleset(out-of-bounds1, out-of-bounds2)", features: {"ss00": 0, "ss01": 0, "ss99": 0} }, // out-of-bounds values but not invalid syntax
  49. { prop: "font-variant-alternates", value: "styleset(circled)", features: {"nalt": 0, "ss00": 0, "ss01": 0} }, // circled defined for annotation not styleset
  50. { prop: "font-variant-alternates", value: "styleset(scope-test1)", features: {"ss23": 1, "ss24": 0} },
  51. { prop: "font-variant-alternates", value: "character-variant(scope-test1)", features: {"cv23": 0, "cv24": 1} },
  52. { prop: "font-variant-alternates", value: "styleset(scope-test2)", features: {"ss23": 0, "ss24": 1, "ss01": 1} },
  53. { prop: "font-variant-alternates", value: "character-variant(scope-test2)", features: {"cv23": 2, "cv24": 0, "cv01": 0} },
  54. { prop: "font-variant-alternates", value: "character-variant(overlap1, overlap2)", features: {"cv23": 2} },
  55. { prop: "font-variant-alternates", value: "character-variant(overlap2, overlap1)", features: {"cv23": 1} },
  56. // invalid values
  57. { prop: "font-variant-alternates", value: "historical-forms normal", features: {"hist": 0}, invalid: true },
  58. { prop: "font-variant-alternates", value: "historical-forms historical-forms", features: {"hist": 0}, invalid: true },
  59. { prop: "font-variant-alternates", value: "swash", features: {"swsh": 0}, invalid: true },
  60. { prop: "font-variant-alternates", value: "swash(3)", features: {"swsh": 0}, invalid: true },
  61. { prop: "font-variant-alternates", value: "annotation(a, b)", features: {"nalt": 0}, invalid: true },
  62. { prop: "font-variant-alternates", value: "ornaments(a,b)", features: {"ornm": 0, "nalt": 0}, invalid: true },
  63. // font-variant-caps
  64. // valid values
  65. { prop: "font-variant-caps", value: "normal", features: {"smcp": 0} },
  66. { prop: "font-variant-caps", value: "small-caps", features: {"smcp": 1, "c2sc": 0} },
  67. { prop: "font-variant-caps", value: "all-small-caps", features: {"smcp": 1, "c2sc": 1, "pcap": 0} },
  68. { prop: "font-variant-caps", value: "petite-caps", features: {"pcap": 1, "smcp": 0} },
  69. { prop: "font-variant-caps", value: "all-petite-caps", features: {"c2pc": 1, "pcap": 1, "smcp": 0} },
  70. { prop: "font-variant-caps", value: "titling-caps", features: {"titl": 1, "smcp": 0} },
  71. { prop: "font-variant-caps", value: "unicase", features: {"unic": 1, "titl": 0} },
  72. // invalid values
  73. { prop: "font-variant-caps", value: "normal small-caps", features: {"smcp": 0}, invalid: true },
  74. { prop: "font-variant-caps", value: "small-caps potato", features: {"smcp": 0}, invalid: true },
  75. { prop: "font-variant-caps", value: "small-caps petite-caps", features: {"smcp": 0, "pcap": 0}, invalid: true },
  76. { prop: "font-variant-caps", value: "small-caps all-small-caps", features: {"smcp": 0, "c2sc": 0}, invalid: true },
  77. { prop: "font-variant-caps", value: "small-cap", features: {"smcp": 0}, invalid: true },
  78. // font-variant-east-asian
  79. // valid values
  80. { prop: "font-variant-east-asian", value: "jis78", features: {"jp78": 1, "jp04": 0} },
  81. { prop: "font-variant-east-asian", value: "jis83", features: {"jp83": 1, "jp04": 0} },
  82. { prop: "font-variant-east-asian", value: "jis90", features: {"jp90": 1, "jp04": 0} },
  83. { prop: "font-variant-east-asian", value: "jis04", features: {"jp04": 1, "jp78": 0} },
  84. { prop: "font-variant-east-asian", value: "simplified", features: {"smpl": 1, "jp04": 0} },
  85. { prop: "font-variant-east-asian", value: "traditional", features: {"trad": 1, "jp04": 0} },
  86. { prop: "font-variant-east-asian", value: "full-width", features: {"fwid": 1, "jp04": 0} },
  87. { prop: "font-variant-east-asian", value: "proportional-width", features: {"pwid": 1, "jp04": 0} },
  88. { prop: "font-variant-east-asian", value: "ruby", features: {"ruby": 1, "jp04": 0} },
  89. { prop: "font-variant-east-asian", value: "jis78 full-width", features: {"jp78": 1, "fwid": 1, "jp83": 0} },
  90. { prop: "font-variant-east-asian", value: "jis78 full-width ruby", features: {"jp78": 1, "fwid": 1, "jp83": 0, "ruby": 1} },
  91. { prop: "font-variant-east-asian", value: "simplified proportional-width", features: {"smpl": 1, "pwid": 1, "jp83": 0} },
  92. { prop: "font-variant-east-asian", value: "ruby simplified", features: {"ruby": 1, "smpl": 1, "trad": 0} },
  93. // invalid values
  94. { prop: "font-variant-east-asian", value: "ruby normal", features: {"ruby": 0}, invalid: true },
  95. { prop: "font-variant-east-asian", value: "jis90 jis04", features: {"jp90": 0, "jp04": 0}, invalid: true },
  96. { prop: "font-variant-east-asian", value: "simplified traditional", features: {"smpl": 0, "trad": 0}, invalid: true },
  97. { prop: "font-variant-east-asian", value: "full-width proportional-width", features: {"fwid": 0, "pwid": 0}, invalid: true },
  98. { prop: "font-variant-east-asian", value: "ruby simplified ruby", features: {"ruby": 0, "smpl": 0, "jp04": 0}, invalid: true },
  99. { prop: "font-variant-east-asian", value: "jis78 ruby simplified", features: {"ruby": 0, "smpl": 0, "jp78": 0}, invalid: true },
  100. // font-variant-ligatures
  101. // valid values
  102. { prop: "font-variant-ligatures", value: "none", features: {"liga": 0, "dlig": 0, "clig": 0, "calt": 0, "hlig": 0} },
  103. { prop: "font-variant-ligatures", value: "normal", features: {"liga": 1, "dlig": 0} },
  104. { prop: "font-variant-ligatures", value: "common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
  105. { prop: "font-variant-ligatures", value: "no-common-ligatures", features: {"liga": 0, "clig": 0, "dlig": 0, "hlig": 0, "calt": 1} },
  106. { prop: "font-variant-ligatures", value: "discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 1, "hlig": 0, "calt": 1} },
  107. { prop: "font-variant-ligatures", value: "no-discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
  108. { prop: "font-variant-ligatures", value: "historical-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 1, "calt": 1} },
  109. { prop: "font-variant-ligatures", value: "no-historical-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
  110. { prop: "font-variant-ligatures", value: "contextual", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
  111. { prop: "font-variant-ligatures", value: "no-contextual", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 0} },
  112. { prop: "font-variant-ligatures", value: "common-ligatures no-discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0, "calt": 1} },
  113. { prop: "font-variant-ligatures", value: "historical-ligatures no-common-ligatures", features: {"clig": 0, "liga": 0, "dlig": 0, "hlig": 1, "calt": 1} },
  114. { prop: "font-variant-ligatures", value: "no-historical-ligatures discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 1, "hlig": 0, "calt": 1} },
  115. { prop: "font-variant-ligatures", value: "common-ligatures no-discretionary-ligatures historical-ligatures no-contextual", features: {"clig": 1, "dlig": 0, "hlig": 1, "liga": 1, "calt": 0} },
  116. // invalid values
  117. { prop: "font-variant-ligatures", value: "common-ligatures none", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  118. { prop: "font-variant-ligatures", value: "none common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  119. { prop: "font-variant-ligatures", value: "common-ligatures normal", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  120. { prop: "font-variant-ligatures", value: "common-ligatures no-common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  121. { prop: "font-variant-ligatures", value: "common-ligatures common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  122. { prop: "font-variant-ligatures", value: "no-historical-ligatures historical-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0}, invalid: true },
  123. { prop: "font-variant-ligatures", value: "no-contextual contextual", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0}, invalid: true },
  124. { prop: "font-variant-ligatures", value: "no-discretionary-ligatures discretionary-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  125. { prop: "font-variant-ligatures", value: "common-ligatures no-discretionary-ligatures no-common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0}, invalid: true },
  126. // font-variant-numeric
  127. // valid values
  128. { prop: "font-variant-numeric", value: "normal", features: {"lnum": 0, "tnum": 0, "pnum": 0, "onum": 0} },
  129. { prop: "font-variant-numeric", value: "lining-nums", features: {"lnum": 1, "onum": 0, "pnum": 0} },
  130. { prop: "font-variant-numeric", value: "oldstyle-nums", features: {"lnum": 0, "onum": 1, "pnum": 0} },
  131. { prop: "font-variant-numeric", value: "proportional-nums", features: {"lnum": 0, "onum": 0, "pnum": 1, "tnum": 0} },
  132. { prop: "font-variant-numeric", value: "proportional-nums oldstyle-nums", features: {"lnum": 0, "onum": 1, "pnum": 1, "tnum": 0} },
  133. { prop: "font-variant-numeric", value: "tabular-nums", features: {"tnum": 1, "onum": 0, "pnum": 0} },
  134. { prop: "font-variant-numeric", value: "diagonal-fractions", features: {"frac": 1, "afrc": 0, "pnum": 0} },
  135. { prop: "font-variant-numeric", value: "stacked-fractions", features: {"frac": 0, "afrc": 1, "pnum": 0} },
  136. { prop: "font-variant-numeric", value: "slashed-zero", features: {"zero": 1, "pnum": 0} },
  137. { prop: "font-variant-numeric", value: "ordinal", features: {"ordn": 1, "pnum": 0} },
  138. { prop: "font-variant-numeric", value: "lining-nums diagonal-fractions", features: {"frac": 1, "afrc": 0, "lnum": 1} },
  139. { prop: "font-variant-numeric", value: "tabular-nums stacked-fractions", features: {"frac": 0, "afrc": 1, "tnum": 1} },
  140. { prop: "font-variant-numeric", value: "tabular-nums slashed-zero stacked-fractions", features: {"frac": 0, "afrc": 1, "tnum": 1, "zero": 1} },
  141. { prop: "font-variant-numeric", value: "proportional-nums slashed-zero diagonal-fractions oldstyle-nums ordinal", features: {"frac": 1, "afrc": 0, "tnum": 0, "pnum": 1, "onum": 1, "ordn": 1, "zero": 1} },
  142. // invalid values
  143. { prop: "font-variant-numeric", value: "lining-nums normal", features: {"lnum": 0, "onum": 0}, invalid: true },
  144. { prop: "font-variant-numeric", value: "lining-nums oldstyle-nums", features: {"lnum": 0, "onum": 0}, invalid: true },
  145. { prop: "font-variant-numeric", value: "lining-nums normal slashed-zero ordinal", features: {"lnum": 0, "onum": 0, "zero": 0}, invalid: true },
  146. { prop: "font-variant-numeric", value: "proportional-nums tabular-nums", features: {"pnum": 0, "tnum": 0}, invalid: true },
  147. { prop: "font-variant-numeric", value: "diagonal-fractions stacked-fractions", features: {"frac": 0, "afrc": 0}, invalid: true },
  148. { prop: "font-variant-numeric", value: "slashed-zero diagonal-fractions slashed-zero", features: {"frac": 0, "afrc": 0, "zero": 0}, invalid: true },
  149. { prop: "font-variant-numeric", value: "lining-nums slashed-zero diagonal-fractions oldstyle-nums", features: {"frac": 0, "afrc": 0, "zero": 0, "onum": 0}, invalid: true },
  150. // font-variant-position
  151. // valid values
  152. { prop: "font-variant-position", value: "normal", features: {"subs": 0, "sups": 0} },
  153. // note: because of fallback, can *only* test activated features here
  154. { prop: "font-variant-position", value: "super", features: {"sups": 1} },
  155. { prop: "font-variant-position", value: "sub", features: {"subs": 1} },
  156. // invalid values
  157. { prop: "font-variant-position", value: "super sub", features: {"subs": 0, "sups": 0}, invalid: true },
  158. ];
  159. // note: the code below requires an array "gFeatures" from :
  160. // layout/reftests/fonts/gsubtest/gsubtest-features.js
  161. // The font defines feature lookups for all OpenType features for a
  162. // specific set of PUA codepoints, as listed in the gFeatures array.
  163. // Using these codepoints and feature combinations, tests can be
  164. // constructed to detect when certain features are enabled or not.
  165. // return a created table containing tests for a given property
  166. //
  167. // Ex: { prop: "font-variant-ligatures", value: "common-ligatures", features: {"liga": 1, "clig": 1, "dlig": 0, "hlig": 0} }
  168. //
  169. // This means that for the property 'font-variant-ligatures' with the value 'common-ligatures', the features listed should
  170. // either be explicitly enabled or disabled.
  171. // propData is the prop/value list with corresponding feature assertions
  172. // whichProp is either "all" or a specific subproperty (i.e. "font-variant-position")
  173. // isRef is true when this is the reference
  174. // debug outputs the prop/value pair along with the tests
  175. // default PASS codepoint used for reference rendering
  176. // need to use a PUA codepoint to avoid problems related to Freetype auto-hinting
  177. const kRefCodepoint = 0xe00c;
  178. function createFeatureTestTable(propData, whichProp, isRef, debug)
  179. {
  180. var table = document.createElement("table");
  181. if (typeof(isRef) == "undefined") {
  182. isRef = false;
  183. }
  184. if (typeof(debug) == "undefined") {
  185. debug = false;
  186. }
  187. var doAll = (whichProp == "all");
  188. for (var i in propData) {
  189. var data = propData[i];
  190. if (!doAll && data.prop != whichProp) continue;
  191. var row = document.createElement("tr");
  192. var invalid = false;
  193. if ("invalid" in data) {
  194. invalid = true;
  195. row.className = "invalid";
  196. }
  197. var cell = document.createElement("td");
  198. cell.className = "prop";
  199. var styledecl = gPrefix + data.prop + ": " + data.value + ";";
  200. cell.innerHTML = styledecl;
  201. row.appendChild(cell);
  202. if (debug) {
  203. table.appendChild(row);
  204. }
  205. row = document.createElement("tr");
  206. if (invalid) {
  207. row.className = "invalid";
  208. }
  209. cell = document.createElement("td");
  210. cell.className = "features";
  211. if (!isRef) {
  212. cell.style.cssText = styledecl;
  213. }
  214. for (var f in data.features) {
  215. var feature = data.features[f];
  216. var cp, unsupported = "F".charCodeAt(0);
  217. var basecp = gFeatures[f];
  218. if (typeof(basecp) == "undefined") {
  219. cp = unsupported;
  220. } else {
  221. switch(feature) {
  222. case 0:
  223. cp = basecp;
  224. break;
  225. case 1:
  226. cp = basecp + 1;
  227. break;
  228. case 2:
  229. cp = basecp + 2;
  230. break;
  231. case 3:
  232. cp = basecp + 3;
  233. break;
  234. default:
  235. cp = basecp + 1;
  236. break;
  237. }
  238. }
  239. var span = document.createElement("span");
  240. var cpOut = (isRef ? kRefCodepoint : cp);
  241. span.innerHTML = "&#x" + cpOut.toString(16) + ";";
  242. span.title = f + "=" + feature;
  243. cell.appendChild(span);
  244. }
  245. row.appendChild(cell);
  246. table.appendChild(row);
  247. }
  248. return table;
  249. }