/src/mozilla-central/layout/generic/MathMLTextRunFactory.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "MathMLTextRunFactory.h" |
8 | | |
9 | | #include "mozilla/ArrayUtils.h" |
10 | | #include "mozilla/BinarySearch.h" |
11 | | #include "mozilla/ComputedStyle.h" |
12 | | #include "mozilla/ComputedStyleInlines.h" |
13 | | |
14 | | #include "nsStyleConsts.h" |
15 | | #include "nsTextFrameUtils.h" |
16 | | #include "nsFontMetrics.h" |
17 | | #include "nsDeviceContext.h" |
18 | | #include "nsUnicodeScriptCodes.h" |
19 | | |
20 | | using namespace mozilla; |
21 | | |
22 | | /* |
23 | | Entries for the mathvariant lookup tables. mKey represents the Unicode |
24 | | character to be transformed and is used for searching the tables. |
25 | | mReplacement represents the mapped mathvariant Unicode character. |
26 | | */ |
27 | | typedef struct |
28 | | { |
29 | | uint32_t mKey; |
30 | | uint32_t mReplacement; |
31 | | } MathVarMapping; |
32 | | |
33 | | /* |
34 | | Lookup tables for use with mathvariant mappings to transform a unicode |
35 | | character point to another unicode character that indicates the proper output. |
36 | | mKey represents one of two concepts. |
37 | | 1. In the Latin table it represents a hole in the mathematical alphanumeric |
38 | | block, where the character that should occupy that position is located |
39 | | elsewhere. |
40 | | 2. It represents an Arabic letter. |
41 | | |
42 | | As a replacement, 0 is reserved to indicate no mapping was found. |
43 | | */ |
44 | | static const MathVarMapping gArabicInitialMapTable[] = { |
45 | | { 0x628, 0x1EE21 }, |
46 | | { 0x62A, 0x1EE35 }, |
47 | | { 0x62B, 0x1EE36 }, |
48 | | { 0x62C, 0x1EE22 }, |
49 | | { 0x62D, 0x1EE27 }, |
50 | | { 0x62E, 0x1EE37 }, |
51 | | { 0x633, 0x1EE2E }, |
52 | | { 0x634, 0x1EE34 }, |
53 | | { 0x635, 0x1EE31 }, |
54 | | { 0x636, 0x1EE39 }, |
55 | | { 0x639, 0x1EE2F }, |
56 | | { 0x63A, 0x1EE3B }, |
57 | | { 0x641, 0x1EE30 }, |
58 | | { 0x642, 0x1EE32 }, |
59 | | { 0x643, 0x1EE2A }, |
60 | | { 0x644, 0x1EE2B }, |
61 | | { 0x645, 0x1EE2C }, |
62 | | { 0x646, 0x1EE2D }, |
63 | | { 0x647, 0x1EE24 }, |
64 | | { 0x64A, 0x1EE29 } |
65 | | }; |
66 | | |
67 | | static const MathVarMapping gArabicTailedMapTable[] = { |
68 | | { 0x62C, 0x1EE42 }, |
69 | | { 0x62D, 0x1EE47 }, |
70 | | { 0x62E, 0x1EE57 }, |
71 | | { 0x633, 0x1EE4E }, |
72 | | { 0x634, 0x1EE54 }, |
73 | | { 0x635, 0x1EE51 }, |
74 | | { 0x636, 0x1EE59 }, |
75 | | { 0x639, 0x1EE4F }, |
76 | | { 0x63A, 0x1EE5B }, |
77 | | { 0x642, 0x1EE52 }, |
78 | | { 0x644, 0x1EE4B }, |
79 | | { 0x646, 0x1EE4D }, |
80 | | { 0x64A, 0x1EE49 }, |
81 | | { 0x66F, 0x1EE5F }, |
82 | | { 0x6BA, 0x1EE5D } |
83 | | }; |
84 | | |
85 | | static const MathVarMapping gArabicStretchedMapTable[] = { |
86 | | { 0x628, 0x1EE61 }, |
87 | | { 0x62A, 0x1EE75 }, |
88 | | { 0x62B, 0x1EE76 }, |
89 | | { 0x62C, 0x1EE62 }, |
90 | | { 0x62D, 0x1EE67 }, |
91 | | { 0x62E, 0x1EE77 }, |
92 | | { 0x633, 0x1EE6E }, |
93 | | { 0x634, 0x1EE74 }, |
94 | | { 0x635, 0x1EE71 }, |
95 | | { 0x636, 0x1EE79 }, |
96 | | { 0x637, 0x1EE68 }, |
97 | | { 0x638, 0x1EE7A }, |
98 | | { 0x639, 0x1EE6F }, |
99 | | { 0x63A, 0x1EE7B }, |
100 | | { 0x641, 0x1EE70 }, |
101 | | { 0x642, 0x1EE72 }, |
102 | | { 0x643, 0x1EE6A }, |
103 | | { 0x645, 0x1EE6C }, |
104 | | { 0x646, 0x1EE6D }, |
105 | | { 0x647, 0x1EE64 }, |
106 | | { 0x64A, 0x1EE69 }, |
107 | | { 0x66E, 0x1EE7C }, |
108 | | { 0x6A1, 0x1EE7E } |
109 | | }; |
110 | | |
111 | | static const MathVarMapping gArabicLoopedMapTable[] = { |
112 | | { 0x627, 0x1EE80 }, |
113 | | { 0x628, 0x1EE81 }, |
114 | | { 0x62A, 0x1EE95 }, |
115 | | { 0x62B, 0x1EE96 }, |
116 | | { 0x62C, 0x1EE82 }, |
117 | | { 0x62D, 0x1EE87 }, |
118 | | { 0x62E, 0x1EE97 }, |
119 | | { 0x62F, 0x1EE83 }, |
120 | | { 0x630, 0x1EE98 }, |
121 | | { 0x631, 0x1EE93 }, |
122 | | { 0x632, 0x1EE86 }, |
123 | | { 0x633, 0x1EE8E }, |
124 | | { 0x634, 0x1EE94 }, |
125 | | { 0x635, 0x1EE91 }, |
126 | | { 0x636, 0x1EE99 }, |
127 | | { 0x637, 0x1EE88 }, |
128 | | { 0x638, 0x1EE9A }, |
129 | | { 0x639, 0x1EE8F }, |
130 | | { 0x63A, 0x1EE9B }, |
131 | | { 0x641, 0x1EE90 }, |
132 | | { 0x642, 0x1EE92 }, |
133 | | { 0x644, 0x1EE8B }, |
134 | | { 0x645, 0x1EE8C }, |
135 | | { 0x646, 0x1EE8D }, |
136 | | { 0x647, 0x1EE84 }, |
137 | | { 0x648, 0x1EE85 }, |
138 | | { 0x64A, 0x1EE89 } |
139 | | }; |
140 | | |
141 | | static const MathVarMapping gArabicDoubleMapTable[] = { |
142 | | { 0x628, 0x1EEA1 }, |
143 | | { 0x62A, 0x1EEB5 }, |
144 | | { 0x62B, 0x1EEB6 }, |
145 | | { 0x62C, 0x1EEA2 }, |
146 | | { 0x62D, 0x1EEA7 }, |
147 | | { 0x62E, 0x1EEB7 }, |
148 | | { 0x62F, 0x1EEA3 }, |
149 | | { 0x630, 0x1EEB8 }, |
150 | | { 0x631, 0x1EEB3 }, |
151 | | { 0x632, 0x1EEA6 }, |
152 | | { 0x633, 0x1EEAE }, |
153 | | { 0x634, 0x1EEB4 }, |
154 | | { 0x635, 0x1EEB1 }, |
155 | | { 0x636, 0x1EEB9 }, |
156 | | { 0x637, 0x1EEA8 }, |
157 | | { 0x638, 0x1EEBA }, |
158 | | { 0x639, 0x1EEAF }, |
159 | | { 0x63A, 0x1EEBB }, |
160 | | { 0x641, 0x1EEB0 }, |
161 | | { 0x642, 0x1EEB2 }, |
162 | | { 0x644, 0x1EEAB }, |
163 | | { 0x645, 0x1EEAC }, |
164 | | { 0x646, 0x1EEAD }, |
165 | | { 0x648, 0x1EEA5 }, |
166 | | { 0x64A, 0x1EEA9 } |
167 | | }; |
168 | | |
169 | | static const MathVarMapping gLatinExceptionMapTable[] = { |
170 | | { 0x1D455, 0x210E }, |
171 | | { 0x1D49D, 0x212C }, |
172 | | { 0x1D4A0, 0x2130 }, |
173 | | { 0x1D4A1, 0x2131 }, |
174 | | { 0x1D4A3, 0x210B }, |
175 | | { 0x1D4A4, 0x2110 }, |
176 | | { 0x1D4A7, 0x2112 }, |
177 | | { 0x1D4A8, 0x2133 }, |
178 | | { 0x1D4AD, 0x211B }, |
179 | | { 0x1D4BA, 0x212F }, |
180 | | { 0x1D4BC, 0x210A }, |
181 | | { 0x1D4C4, 0x2134 }, |
182 | | { 0x1D506, 0x212D }, |
183 | | { 0x1D50B, 0x210C }, |
184 | | { 0x1D50C, 0x2111 }, |
185 | | { 0x1D515, 0x211C }, |
186 | | { 0x1D51D, 0x2128 }, |
187 | | { 0x1D53A, 0x2102 }, |
188 | | { 0x1D53F, 0x210D }, |
189 | | { 0x1D545, 0x2115 }, |
190 | | { 0x1D547, 0x2119 }, |
191 | | { 0x1D548, 0x211A }, |
192 | | { 0x1D549, 0x211D }, |
193 | | { 0x1D551, 0x2124 } |
194 | | }; |
195 | | |
196 | | namespace { |
197 | | |
198 | | struct MathVarMappingWrapper |
199 | | { |
200 | | const MathVarMapping* const mTable; |
201 | 0 | explicit MathVarMappingWrapper(const MathVarMapping* aTable) : mTable(aTable) {} |
202 | 0 | uint32_t operator[](size_t index) const { |
203 | 0 | return mTable[index].mKey; |
204 | 0 | } |
205 | | }; |
206 | | |
207 | | } // namespace |
208 | | |
209 | | // Finds a MathVarMapping struct with the specified key (aKey) within aTable. |
210 | | // aTable must be an array, whose length is specified by aNumElements |
211 | | static uint32_t |
212 | | MathvarMappingSearch(uint32_t aKey, const MathVarMapping* aTable, uint32_t aNumElements) |
213 | 0 | { |
214 | 0 | size_t index; |
215 | 0 | if (BinarySearch(MathVarMappingWrapper(aTable), 0, aNumElements, aKey, &index)) { |
216 | 0 | return aTable[index].mReplacement; |
217 | 0 | } |
218 | 0 | |
219 | 0 | return 0; |
220 | 0 | } |
221 | | |
222 | 0 | #define GREEK_UPPER_THETA 0x03F4 |
223 | 0 | #define HOLE_GREEK_UPPER_THETA 0x03A2 |
224 | 0 | #define NABLA 0x2207 |
225 | 0 | #define PARTIAL_DIFFERENTIAL 0x2202 |
226 | 0 | #define GREEK_UPPER_ALPHA 0x0391 |
227 | 0 | #define GREEK_UPPER_OMEGA 0x03A9 |
228 | 0 | #define GREEK_LOWER_ALPHA 0x03B1 |
229 | 0 | #define GREEK_LOWER_OMEGA 0x03C9 |
230 | 0 | #define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5 |
231 | 0 | #define GREEK_THETA_SYMBOL 0x03D1 |
232 | 0 | #define GREEK_KAPPA_SYMBOL 0x03F0 |
233 | 0 | #define GREEK_PHI_SYMBOL 0x03D5 |
234 | 0 | #define GREEK_RHO_SYMBOL 0x03F1 |
235 | 0 | #define GREEK_PI_SYMBOL 0x03D6 |
236 | 0 | #define GREEK_LETTER_DIGAMMA 0x03DC |
237 | 0 | #define GREEK_SMALL_LETTER_DIGAMMA 0x03DD |
238 | 0 | #define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA |
239 | 0 | #define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB |
240 | | |
241 | 0 | #define LATIN_SMALL_LETTER_DOTLESS_I 0x0131 |
242 | 0 | #define LATIN_SMALL_LETTER_DOTLESS_J 0x0237 |
243 | | |
244 | 0 | #define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4 |
245 | 0 | #define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5 |
246 | | |
247 | 0 | #define MATH_BOLD_UPPER_A 0x1D400 |
248 | 0 | #define MATH_ITALIC_UPPER_A 0x1D434 |
249 | 0 | #define MATH_BOLD_SMALL_A 0x1D41A |
250 | 0 | #define MATH_BOLD_UPPER_ALPHA 0x1D6A8 |
251 | 0 | #define MATH_BOLD_SMALL_ALPHA 0x1D6C2 |
252 | 0 | #define MATH_ITALIC_UPPER_ALPHA 0x1D6E2 |
253 | 0 | #define MATH_BOLD_DIGIT_ZERO 0x1D7CE |
254 | 0 | #define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8 |
255 | | |
256 | 0 | #define MATH_BOLD_UPPER_THETA 0x1D6B9 |
257 | 0 | #define MATH_BOLD_NABLA 0x1D6C1 |
258 | 0 | #define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB |
259 | 0 | #define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC |
260 | 0 | #define MATH_BOLD_THETA_SYMBOL 0x1D6DD |
261 | 0 | #define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE |
262 | 0 | #define MATH_BOLD_PHI_SYMBOL 0x1D6DF |
263 | 0 | #define MATH_BOLD_RHO_SYMBOL 0x1D6E0 |
264 | 0 | #define MATH_BOLD_PI_SYMBOL 0x1D6E1 |
265 | | |
266 | | /* |
267 | | Performs the character mapping needed to implement MathML's mathvariant |
268 | | attribute. It takes a unicode character and maps it to its appropriate |
269 | | mathvariant counterpart specified by aMathVar. The mapped character is |
270 | | typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but |
271 | | there are exceptions which this function accounts for. |
272 | | Characters without a valid mapping or valid aMathvar value are returned |
273 | | unaltered. Characters already in the mathematical blocks (or are one of the |
274 | | exceptions) are never transformed. |
275 | | Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h. |
276 | | The transformable characters can be found at: |
277 | | http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and |
278 | | https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols |
279 | | */ |
280 | | static uint32_t |
281 | | MathVariant(uint32_t aCh, uint8_t aMathVar) |
282 | 0 | { |
283 | 0 | uint32_t baseChar; |
284 | 0 | enum CharacterType { |
285 | 0 | kIsLatin, |
286 | 0 | kIsGreekish, |
287 | 0 | kIsNumber, |
288 | 0 | kIsArabic, |
289 | 0 | }; |
290 | 0 | CharacterType varType; |
291 | 0 |
|
292 | 0 | int8_t multiplier; |
293 | 0 |
|
294 | 0 | if (aMathVar <= NS_MATHML_MATHVARIANT_NORMAL) { |
295 | 0 | // nothing to do here |
296 | 0 | return aCh; |
297 | 0 | } |
298 | 0 | if (aMathVar > NS_MATHML_MATHVARIANT_STRETCHED) { |
299 | 0 | NS_ASSERTION(false, "Illegal mathvariant value"); |
300 | 0 | return aCh; |
301 | 0 | } |
302 | 0 |
|
303 | 0 | // Exceptional characters with at most one possible transformation |
304 | 0 | if (aCh == HOLE_GREEK_UPPER_THETA) { |
305 | 0 | // Nothing at this code point is transformed |
306 | 0 | return aCh; |
307 | 0 | } |
308 | 0 | if (aCh == GREEK_LETTER_DIGAMMA) { |
309 | 0 | if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) { |
310 | 0 | return MATH_BOLD_CAPITAL_DIGAMMA; |
311 | 0 | } |
312 | 0 | return aCh; |
313 | 0 | } |
314 | 0 | if (aCh == GREEK_SMALL_LETTER_DIGAMMA) { |
315 | 0 | if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) { |
316 | 0 | return MATH_BOLD_SMALL_DIGAMMA; |
317 | 0 | } |
318 | 0 | return aCh; |
319 | 0 | } |
320 | 0 | if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) { |
321 | 0 | if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) { |
322 | 0 | return MATH_ITALIC_SMALL_DOTLESS_I; |
323 | 0 | } |
324 | 0 | return aCh; |
325 | 0 | } |
326 | 0 | if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) { |
327 | 0 | if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) { |
328 | 0 | return MATH_ITALIC_SMALL_DOTLESS_J; |
329 | 0 | } |
330 | 0 | return aCh; |
331 | 0 | } |
332 | 0 | |
333 | 0 | // The Unicode mathematical blocks are divided into four segments: Latin, |
334 | 0 | // Greek, numbers and Arabic. In the case of the first three |
335 | 0 | // baseChar represents the relative order in which the characters are |
336 | 0 | // encoded in the Unicode mathematical block, normalised to the first |
337 | 0 | // character of that sequence. |
338 | 0 | // |
339 | 0 | if ('A' <= aCh && aCh <= 'Z') { |
340 | 0 | baseChar = aCh - 'A'; |
341 | 0 | varType = kIsLatin; |
342 | 0 | } else if ('a' <= aCh && aCh <= 'z') { |
343 | 0 | // Lowercase characters are placed immediately after the uppercase |
344 | 0 | // characters in the Unicode mathematical block. The constant subtraction |
345 | 0 | // represents the number of characters between the start of the sequence |
346 | 0 | // (capital A) and the first lowercase letter. |
347 | 0 | baseChar = MATH_BOLD_SMALL_A-MATH_BOLD_UPPER_A + aCh - 'a'; |
348 | 0 | varType = kIsLatin; |
349 | 0 | } else if ('0' <= aCh && aCh <= '9') { |
350 | 0 | baseChar = aCh - '0'; |
351 | 0 | varType = kIsNumber; |
352 | 0 | } else if (GREEK_UPPER_ALPHA <= aCh && aCh <= GREEK_UPPER_OMEGA) { |
353 | 0 | baseChar = aCh-GREEK_UPPER_ALPHA; |
354 | 0 | varType = kIsGreekish; |
355 | 0 | } else if (GREEK_LOWER_ALPHA <= aCh && aCh <= GREEK_LOWER_OMEGA) { |
356 | 0 | // Lowercase Greek comes after uppercase Greek. |
357 | 0 | // Note in this instance the presence of an additional character (Nabla) |
358 | 0 | // between the end of the uppercase Greek characters and the lowercase |
359 | 0 | // ones. |
360 | 0 | baseChar = MATH_BOLD_SMALL_ALPHA - MATH_BOLD_UPPER_ALPHA |
361 | 0 | + aCh-GREEK_LOWER_ALPHA; |
362 | 0 | varType = kIsGreekish; |
363 | 0 | } else if (0x0600 <= aCh && aCh <= 0x06FF) { |
364 | 0 | // Arabic characters are defined within this range |
365 | 0 | varType = kIsArabic; |
366 | 0 | } else { |
367 | 0 | switch (aCh) { |
368 | 0 | case GREEK_UPPER_THETA: |
369 | 0 | baseChar = MATH_BOLD_UPPER_THETA-MATH_BOLD_UPPER_ALPHA; |
370 | 0 | break; |
371 | 0 | case NABLA: |
372 | 0 | baseChar = MATH_BOLD_NABLA-MATH_BOLD_UPPER_ALPHA; |
373 | 0 | break; |
374 | 0 | case PARTIAL_DIFFERENTIAL: |
375 | 0 | baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA; |
376 | 0 | break; |
377 | 0 | case GREEK_LUNATE_EPSILON_SYMBOL: |
378 | 0 | baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
379 | 0 | break; |
380 | 0 | case GREEK_THETA_SYMBOL: |
381 | 0 | baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
382 | 0 | break; |
383 | 0 | case GREEK_KAPPA_SYMBOL: |
384 | 0 | baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
385 | 0 | break; |
386 | 0 | case GREEK_PHI_SYMBOL: |
387 | 0 | baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
388 | 0 | break; |
389 | 0 | case GREEK_RHO_SYMBOL: |
390 | 0 | baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
391 | 0 | break; |
392 | 0 | case GREEK_PI_SYMBOL: |
393 | 0 | baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
394 | 0 | break; |
395 | 0 | default: |
396 | 0 | return aCh; |
397 | 0 | } |
398 | 0 | |
399 | 0 | varType = kIsGreekish; |
400 | 0 | } |
401 | 0 |
|
402 | 0 | if (varType == kIsNumber) { |
403 | 0 | switch (aMathVar) { |
404 | 0 | // Each possible number mathvariant is encoded in a single, contiguous |
405 | 0 | // block. For example the beginning of the double struck number range |
406 | 0 | // follows immediately after the end of the bold number range. |
407 | 0 | // multiplier represents the order of the sequences relative to the first |
408 | 0 | // one. |
409 | 0 | case NS_MATHML_MATHVARIANT_BOLD: |
410 | 0 | multiplier = 0; |
411 | 0 | break; |
412 | 0 | case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK: |
413 | 0 | multiplier = 1; |
414 | 0 | break; |
415 | 0 | case NS_MATHML_MATHVARIANT_SANS_SERIF: |
416 | 0 | multiplier = 2; |
417 | 0 | break; |
418 | 0 | case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF: |
419 | 0 | multiplier = 3; |
420 | 0 | break; |
421 | 0 | case NS_MATHML_MATHVARIANT_MONOSPACE: |
422 | 0 | multiplier = 4; |
423 | 0 | break; |
424 | 0 | default: |
425 | 0 | // This mathvariant isn't defined for numbers or is otherwise normal |
426 | 0 | return aCh; |
427 | 0 | } |
428 | 0 | // As the ranges are contiguous, to find the desired mathvariant range it |
429 | 0 | // is sufficient to multiply the position within the sequence order |
430 | 0 | // (multiplier) with the period of the sequence (which is constant for all |
431 | 0 | // number sequences) and to add the character point of the first character |
432 | 0 | // within the number mathvariant range. |
433 | 0 | // To this the baseChar calculated earlier is added to obtain the final |
434 | 0 | // code point. |
435 | 0 | return baseChar+multiplier*(MATH_DOUBLE_STRUCK_ZERO-MATH_BOLD_DIGIT_ZERO) |
436 | 0 | +MATH_BOLD_DIGIT_ZERO; |
437 | 0 | } else if (varType == kIsGreekish) { |
438 | 0 | switch (aMathVar) { |
439 | 0 | case NS_MATHML_MATHVARIANT_BOLD: |
440 | 0 | multiplier = 0; |
441 | 0 | break; |
442 | 0 | case NS_MATHML_MATHVARIANT_ITALIC: |
443 | 0 | multiplier = 1; |
444 | 0 | break; |
445 | 0 | case NS_MATHML_MATHVARIANT_BOLD_ITALIC: |
446 | 0 | multiplier = 2; |
447 | 0 | break; |
448 | 0 | case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF: |
449 | 0 | multiplier = 3; |
450 | 0 | break; |
451 | 0 | case NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC: |
452 | 0 | multiplier = 4; |
453 | 0 | break; |
454 | 0 | default: |
455 | 0 | // This mathvariant isn't defined for Greek or is otherwise normal |
456 | 0 | return aCh; |
457 | 0 | } |
458 | 0 | // See the kIsNumber case for an explanation of the following calculation |
459 | 0 | return baseChar + MATH_BOLD_UPPER_ALPHA + |
460 | 0 | multiplier*(MATH_ITALIC_UPPER_ALPHA - MATH_BOLD_UPPER_ALPHA); |
461 | 0 | } |
462 | 0 |
|
463 | 0 | uint32_t tempChar; |
464 | 0 | uint32_t newChar; |
465 | 0 | if (varType == kIsArabic) { |
466 | 0 | const MathVarMapping* mapTable; |
467 | 0 | uint32_t tableLength; |
468 | 0 | switch (aMathVar) { |
469 | 0 | /* The Arabic mathematical block is not continuous, nor does it have a |
470 | 0 | * monotonic mapping to the unencoded characters, requiring the use of a |
471 | 0 | * lookup table. |
472 | 0 | */ |
473 | 0 | case NS_MATHML_MATHVARIANT_INITIAL: |
474 | 0 | mapTable = gArabicInitialMapTable; |
475 | 0 | tableLength = ArrayLength(gArabicInitialMapTable); |
476 | 0 | break; |
477 | 0 | case NS_MATHML_MATHVARIANT_TAILED: |
478 | 0 | mapTable = gArabicTailedMapTable; |
479 | 0 | tableLength = ArrayLength(gArabicTailedMapTable); |
480 | 0 | break; |
481 | 0 | case NS_MATHML_MATHVARIANT_STRETCHED: |
482 | 0 | mapTable = gArabicStretchedMapTable; |
483 | 0 | tableLength = ArrayLength(gArabicStretchedMapTable); |
484 | 0 | break; |
485 | 0 | case NS_MATHML_MATHVARIANT_LOOPED: |
486 | 0 | mapTable = gArabicLoopedMapTable; |
487 | 0 | tableLength = ArrayLength(gArabicLoopedMapTable); |
488 | 0 | break; |
489 | 0 | case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK: |
490 | 0 | mapTable = gArabicDoubleMapTable; |
491 | 0 | tableLength = ArrayLength(gArabicDoubleMapTable); |
492 | 0 | break; |
493 | 0 | default: |
494 | 0 | // No valid transformations exist |
495 | 0 | return aCh; |
496 | 0 | } |
497 | 0 | newChar = MathvarMappingSearch(aCh, mapTable, tableLength); |
498 | 0 | } else { |
499 | 0 | // Must be Latin |
500 | 0 | if (aMathVar > NS_MATHML_MATHVARIANT_MONOSPACE) { |
501 | 0 | // Latin doesn't support the Arabic mathvariants |
502 | 0 | return aCh; |
503 | 0 | } |
504 | 0 | multiplier = aMathVar - 2; |
505 | 0 | // This is possible because the values for NS_MATHML_MATHVARIANT_* are |
506 | 0 | // chosen to coincide with the order in which the encoded mathvariant |
507 | 0 | // characters are located within their unicode block (less an offset to |
508 | 0 | // avoid _NONE and _NORMAL variants) |
509 | 0 | // See the kIsNumber case for an explanation of the following calculation |
510 | 0 | tempChar = baseChar + MATH_BOLD_UPPER_A + |
511 | 0 | multiplier*(MATH_ITALIC_UPPER_A - MATH_BOLD_UPPER_A); |
512 | 0 | // There are roughly twenty characters that are located outside of the |
513 | 0 | // mathematical block, so the spaces where they ought to be are used |
514 | 0 | // as keys for a lookup table containing the correct character mappings. |
515 | 0 | newChar = MathvarMappingSearch(tempChar, gLatinExceptionMapTable, |
516 | 0 | ArrayLength(gLatinExceptionMapTable)); |
517 | 0 | } |
518 | 0 |
|
519 | 0 | if (newChar) { |
520 | 0 | return newChar; |
521 | 0 | } else if (varType == kIsLatin) { |
522 | 0 | return tempChar; |
523 | 0 | } else { |
524 | 0 | // An Arabic character without a corresponding mapping |
525 | 0 | return aCh; |
526 | 0 | } |
527 | 0 |
|
528 | 0 | } |
529 | | |
530 | 0 | #define TT_SSTY TRUETYPE_TAG('s', 's', 't', 'y') |
531 | 0 | #define TT_DTLS TRUETYPE_TAG('d', 't', 'l', 's') |
532 | | |
533 | | void |
534 | | MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, |
535 | | mozilla::gfx::DrawTarget* aRefDrawTarget, |
536 | | gfxMissingFontRecorder* aMFR) |
537 | 0 | { |
538 | 0 | gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); |
539 | 0 |
|
540 | 0 | nsAutoString convertedString; |
541 | 0 | AutoTArray<bool,50> charsToMergeArray; |
542 | 0 | AutoTArray<bool,50> deletedCharsArray; |
543 | 0 | AutoTArray<RefPtr<nsTransformedCharStyle>,50> styleArray; |
544 | 0 | AutoTArray<uint8_t,50> canBreakBeforeArray; |
545 | 0 | bool mergeNeeded = false; |
546 | 0 |
|
547 | 0 | bool singleCharMI = |
548 | 0 | !!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::TEXT_IS_SINGLE_CHAR_MI); |
549 | 0 |
|
550 | 0 | uint32_t length = aTextRun->GetLength(); |
551 | 0 | const char16_t* str = aTextRun->mString.BeginReading(); |
552 | 0 | const nsTArray<RefPtr<nsTransformedCharStyle>>& styles = aTextRun->mStyles; |
553 | 0 | nsFont font; |
554 | 0 | if (length) { |
555 | 0 | font = styles[0]->mFont; |
556 | 0 |
|
557 | 0 | if (mSSTYScriptLevel || (mFlags & MATH_FONT_FEATURE_DTLS)) { |
558 | 0 | bool foundSSTY = false; |
559 | 0 | bool foundDTLS = false; |
560 | 0 | // We respect ssty settings explicitly set by the user |
561 | 0 | for (uint32_t i = 0; i < font.fontFeatureSettings.Length(); i++) { |
562 | 0 | if (font.fontFeatureSettings[i].mTag == TT_SSTY) { |
563 | 0 | foundSSTY = true; |
564 | 0 | } else if (font.fontFeatureSettings[i].mTag == TT_DTLS) { |
565 | 0 | foundDTLS = true; |
566 | 0 | } |
567 | 0 | } |
568 | 0 | if (mSSTYScriptLevel && !foundSSTY) { |
569 | 0 | uint8_t sstyLevel = 0; |
570 | 0 | float scriptScaling = pow(styles[0]->mScriptSizeMultiplier, |
571 | 0 | mSSTYScriptLevel); |
572 | 0 | static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER < 1, |
573 | 0 | "Shouldn't it make things smaller?"); |
574 | 0 | /* |
575 | 0 | An SSTY level of 2 is set if the scaling factor is less than or equal |
576 | 0 | to halfway between that for a scriptlevel of 1 (0.71) and that of a |
577 | 0 | scriptlevel of 2 (0.71^2), assuming the default script size multiplier. |
578 | 0 | An SSTY level of 1 is set if the script scaling factor is less than |
579 | 0 | or equal that for a scriptlevel of 1 assuming the default script size |
580 | 0 | multiplier. |
581 | 0 |
|
582 | 0 | User specified values of script size multiplier will change the scaling |
583 | 0 | factor which mSSTYScriptLevel values correspond to. |
584 | 0 |
|
585 | 0 | In the event that the script size multiplier actually makes things |
586 | 0 | larger, no change is made. |
587 | 0 |
|
588 | 0 | To opt out of this change, add the following to the stylesheet: |
589 | 0 | "font-feature-settings: 'ssty' 0" |
590 | 0 | */ |
591 | 0 | if (scriptScaling <= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER + |
592 | 0 | (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER * |
593 | 0 | NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER))/2) { |
594 | 0 | // Currently only the first two ssty settings are used, so two is large |
595 | 0 | // as we go |
596 | 0 | sstyLevel = 2; |
597 | 0 | } else if (scriptScaling <= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) { |
598 | 0 | sstyLevel = 1; |
599 | 0 | } |
600 | 0 | if (sstyLevel) { |
601 | 0 | gfxFontFeature settingSSTY; |
602 | 0 | settingSSTY.mTag = TT_SSTY; |
603 | 0 | settingSSTY.mValue = sstyLevel; |
604 | 0 | font.fontFeatureSettings.AppendElement(settingSSTY); |
605 | 0 | } |
606 | 0 | } |
607 | 0 | /* |
608 | 0 | Apply the dtls font feature setting (dotless). |
609 | 0 | This gets applied to the base frame and all descendants of the base |
610 | 0 | frame of certain <mover> and <munderover> frames. |
611 | 0 |
|
612 | 0 | See nsMathMLmunderoverFrame.cpp for a full description. |
613 | 0 |
|
614 | 0 | To opt out of this change, add the following to the stylesheet: |
615 | 0 | "font-feature-settings: 'dtls' 0" |
616 | 0 | */ |
617 | 0 | if ((mFlags & MATH_FONT_FEATURE_DTLS) && !foundDTLS) { |
618 | 0 | gfxFontFeature settingDTLS; |
619 | 0 | settingDTLS.mTag = TT_DTLS; |
620 | 0 | settingDTLS.mValue = 1; |
621 | 0 | font.fontFeatureSettings.AppendElement(settingDTLS); |
622 | 0 | } |
623 | 0 | } |
624 | 0 | } |
625 | 0 |
|
626 | 0 | uint8_t mathVar = NS_MATHML_MATHVARIANT_NONE; |
627 | 0 | bool doMathvariantStyling = true; |
628 | 0 |
|
629 | 0 | for (uint32_t i = 0; i < length; ++i) { |
630 | 0 | int extraChars = 0; |
631 | 0 | mathVar = styles[i]->mMathVariant; |
632 | 0 |
|
633 | 0 | if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) { |
634 | 0 | // If the user has explicitly set a non-default value for fontstyle or |
635 | 0 | // fontweight, the italic mathvariant behaviour of <mi> is disabled |
636 | 0 | // This overrides the initial values specified in fontStyle, to avoid |
637 | 0 | // inconsistencies in which attributes allow CSS changes and which do not. |
638 | 0 | if (mFlags & MATH_FONT_WEIGHT_BOLD) { |
639 | 0 | font.weight = FontWeight::Bold(); |
640 | 0 | if (mFlags & MATH_FONT_STYLING_NORMAL) { |
641 | 0 | font.style = FontSlantStyle::Normal(); |
642 | 0 | } else { |
643 | 0 | font.style = FontSlantStyle::Italic(); |
644 | 0 | } |
645 | 0 | } else if (mFlags & MATH_FONT_STYLING_NORMAL) { |
646 | 0 | font.style = FontSlantStyle::Normal(); |
647 | 0 | font.weight = FontWeight::Normal(); |
648 | 0 | } else { |
649 | 0 | mathVar = NS_MATHML_MATHVARIANT_ITALIC; |
650 | 0 | } |
651 | 0 | } |
652 | 0 |
|
653 | 0 | uint32_t ch = str[i]; |
654 | 0 | if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 && |
655 | 0 | NS_IS_LOW_SURROGATE(str[i + 1])) { |
656 | 0 | ch = SURROGATE_TO_UCS4(ch, str[i + 1]); |
657 | 0 | } |
658 | 0 | uint32_t ch2 = MathVariant(ch, mathVar); |
659 | 0 |
|
660 | 0 | if (mathVar == NS_MATHML_MATHVARIANT_BOLD || |
661 | 0 | mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC || |
662 | 0 | mathVar == NS_MATHML_MATHVARIANT_ITALIC) { |
663 | 0 | if (ch == ch2 && ch != 0x20 && ch != 0xA0) { |
664 | 0 | // Don't apply the CSS style if a character cannot be |
665 | 0 | // transformed. There is an exception for whitespace as it is both |
666 | 0 | // common and innocuous. |
667 | 0 | doMathvariantStyling = false; |
668 | 0 | } |
669 | 0 | if (ch2 != ch) { |
670 | 0 | // Bug 930504. Some platforms do not have fonts for Mathematical |
671 | 0 | // Alphanumeric Symbols. Hence we check whether the transformed |
672 | 0 | // character is actually available. |
673 | 0 | gfxTextRange::MatchType matchType; |
674 | 0 | RefPtr<gfxFont> mathFont = fontGroup-> |
675 | 0 | FindFontForChar(ch2, 0, 0, unicode::Script::COMMON, nullptr, &matchType); |
676 | 0 | if (mathFont) { |
677 | 0 | // Don't apply the CSS style if there is a math font for at least one |
678 | 0 | // of the transformed character in this text run. |
679 | 0 | doMathvariantStyling = false; |
680 | 0 | } else { |
681 | 0 | // We fallback to the original character. |
682 | 0 | ch2 = ch; |
683 | 0 | if (aMFR) { |
684 | 0 | aMFR->RecordScript(unicode::Script::MATHEMATICAL_NOTATION); |
685 | 0 | } |
686 | 0 | } |
687 | 0 | } |
688 | 0 | } |
689 | 0 |
|
690 | 0 | deletedCharsArray.AppendElement(false); |
691 | 0 | charsToMergeArray.AppendElement(false); |
692 | 0 | styleArray.AppendElement(styles[i]); |
693 | 0 | canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); |
694 | 0 |
|
695 | 0 | if (IS_IN_BMP(ch2)) { |
696 | 0 | convertedString.Append(ch2); |
697 | 0 | } else { |
698 | 0 | convertedString.Append(H_SURROGATE(ch2)); |
699 | 0 | convertedString.Append(L_SURROGATE(ch2)); |
700 | 0 | ++extraChars; |
701 | 0 | if (!IS_IN_BMP(ch)) { |
702 | 0 | deletedCharsArray.AppendElement(true); // not exactly deleted, but |
703 | 0 | // the trailing surrogate is skipped |
704 | 0 | ++i; |
705 | 0 | } |
706 | 0 | } |
707 | 0 |
|
708 | 0 | while (extraChars-- > 0) { |
709 | 0 | mergeNeeded = true; |
710 | 0 | charsToMergeArray.AppendElement(true); |
711 | 0 | styleArray.AppendElement(styles[i]); |
712 | 0 | canBreakBeforeArray.AppendElement(false); |
713 | 0 | } |
714 | 0 | } |
715 | 0 |
|
716 | 0 | gfx::ShapedTextFlags flags; |
717 | 0 | gfxTextRunFactory::Parameters innerParams = |
718 | 0 | GetParametersForInner(aTextRun, &flags, aRefDrawTarget); |
719 | 0 |
|
720 | 0 | RefPtr<nsTransformedTextRun> transformedChild; |
721 | 0 | RefPtr<gfxTextRun> cachedChild; |
722 | 0 | gfxTextRun* child; |
723 | 0 |
|
724 | 0 | if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) { |
725 | 0 | font.style = FontSlantStyle::Normal(); |
726 | 0 | font.weight = FontWeight::Bold(); |
727 | 0 | } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) { |
728 | 0 | font.style = FontSlantStyle::Italic(); |
729 | 0 | font.weight = FontWeight::Normal(); |
730 | 0 | } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC && |
731 | 0 | doMathvariantStyling) { |
732 | 0 | font.style = FontSlantStyle::Italic(); |
733 | 0 | font.weight = FontWeight::Bold(); |
734 | 0 | } else if (mathVar != NS_MATHML_MATHVARIANT_NONE) { |
735 | 0 | // Mathvariant overrides fontstyle and fontweight |
736 | 0 | // Need to check to see if mathvariant is actually applied as this function |
737 | 0 | // is used for other purposes. |
738 | 0 | font.style = FontSlantStyle::Normal(); |
739 | 0 | font.weight = FontWeight::Normal(); |
740 | 0 | } |
741 | 0 | gfxFontGroup* newFontGroup = nullptr; |
742 | 0 |
|
743 | 0 | // Get the correct gfxFontGroup that corresponds to the earlier font changes. |
744 | 0 | if (length) { |
745 | 0 | font.size = NSToCoordRound(font.size * mFontInflation); |
746 | 0 | nsPresContext* pc = styles[0]->mPresContext; |
747 | 0 | nsFontMetrics::Params params; |
748 | 0 | params.language = styles[0]->mLanguage; |
749 | 0 | params.explicitLanguage = styles[0]->mExplicitLanguage; |
750 | 0 | params.userFontSet = pc->GetUserFontSet(); |
751 | 0 | params.textPerf = pc->GetTextPerfMetrics(); |
752 | 0 | RefPtr<nsFontMetrics> metrics = |
753 | 0 | pc->DeviceContext()->GetMetricsFor(font, params); |
754 | 0 | newFontGroup = metrics->GetThebesFontGroup(); |
755 | 0 | } |
756 | 0 |
|
757 | 0 | if (!newFontGroup) { |
758 | 0 | // If we can't get a new font group, fall back to the old one. Rendering |
759 | 0 | // will be incorrect, but not significantly so. |
760 | 0 | newFontGroup = fontGroup; |
761 | 0 | } |
762 | 0 |
|
763 | 0 | if (mInnerTransformingTextRunFactory) { |
764 | 0 | transformedChild = mInnerTransformingTextRunFactory->MakeTextRun( |
765 | 0 | convertedString.BeginReading(), convertedString.Length(), |
766 | 0 | &innerParams, newFontGroup, flags, nsTextFrameUtils::Flags(), |
767 | 0 | std::move(styleArray), false); |
768 | 0 | child = transformedChild.get(); |
769 | 0 | } else { |
770 | 0 | cachedChild = newFontGroup->MakeTextRun( |
771 | 0 | convertedString.BeginReading(), convertedString.Length(), |
772 | 0 | &innerParams, flags, nsTextFrameUtils::Flags(), aMFR); |
773 | 0 | child = cachedChild.get(); |
774 | 0 | } |
775 | 0 | if (!child) |
776 | 0 | return; |
777 | 0 | |
778 | 0 | typedef gfxTextRun::Range Range; |
779 | 0 |
|
780 | 0 | // Copy potential linebreaks into child so they're preserved |
781 | 0 | // (and also child will be shaped appropriately) |
782 | 0 | NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(), |
783 | 0 | "Dropped characters or break-before values somewhere!"); |
784 | 0 | Range range(0, uint32_t(canBreakBeforeArray.Length())); |
785 | 0 | child->SetPotentialLineBreaks(range, canBreakBeforeArray.Elements()); |
786 | 0 | if (transformedChild) { |
787 | 0 | transformedChild->FinishSettingProperties(aRefDrawTarget, aMFR); |
788 | 0 | } |
789 | 0 |
|
790 | 0 | if (mergeNeeded) { |
791 | 0 | // Now merge multiple characters into one multi-glyph character as required |
792 | 0 | NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(), |
793 | 0 | "source length mismatch"); |
794 | 0 | NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(), |
795 | 0 | "destination length mismatch"); |
796 | 0 | MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(), |
797 | 0 | deletedCharsArray.Elements()); |
798 | 0 | } else { |
799 | 0 | // No merging to do, so just copy; this produces a more optimized textrun. |
800 | 0 | // We can't steal the data because the child may be cached and stealing |
801 | 0 | // the data would break the cache. |
802 | 0 | aTextRun->ResetGlyphRuns(); |
803 | 0 | aTextRun->CopyGlyphDataFrom(child, Range(child), 0); |
804 | 0 | } |
805 | 0 | } |