1 /****************************************************************************\
2  Part of the XeTeX typesetting system
3  Copyright (c) 1994-2008 by SIL International
4  Copyright (c) 2009 by Jonathan Kew
5  Copyright (c) 2012-2015 by Khaled Hosny
6 
7  SIL Author(s): Jonathan Kew
8 
9 Permission is hereby granted, free of charge, to any person obtaining
10 a copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sublicense, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16 
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
24 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 
28 Except as contained in this notice, the name of the copyright holders
29 shall not be used in advertising or otherwise to promote the sale,
30 use or other dealings in this Software without prior written
31 authorization from the copyright holders.
32 \****************************************************************************/
33 
34 #include <w2c/config.h>
35 
36 #include <assert.h>
37 
38 #include "XeTeXOTMath.h"
39 
40 #include "XeTeX_web.h"
41 #include "XeTeXLayoutInterface.h"
42 #include "XeTeXFontInst.h"
43 #include "XeTeXswap.h"
44 
getCoverage(const Coverage * coverage,GlyphID g)45 static int32_t getCoverage(const Coverage* coverage, GlyphID g)
46 {
47     if (SWAP(coverage->format) == 1) {
48         const CoverageFormat1 *table = (const CoverageFormat1 *) coverage;
49         for (int i = 0; i < SWAP(table->glyphCount); i++) {
50             if (SWAP(table->glyphArray[i]) == g)
51                 return i;
52         }
53     } else if (SWAP(coverage->format) == 2) {
54         const CoverageFormat2 *table = (const CoverageFormat2 *) coverage;
55         for (int i = 0; i < SWAP(table->rangeCount); i++) {
56             if (SWAP(table->rangeArray[i].start) <= g && SWAP(table->rangeArray[i].end) >= g)
57                 return SWAP(table->rangeArray[i].startCoverageIndex) + (g - SWAP(table->rangeArray[i].start));
58         }
59     }
60 
61     return -1;
62 }
63 
getMathConstant(XeTeXFontInst * fontInst,mathConstantIndex whichConstant)64 static int16_t getMathConstant(XeTeXFontInst* fontInst, mathConstantIndex whichConstant)
65 {
66     const char* table = fontInst->getMathTable();
67     if (table == NULL)
68         return 0;
69 
70     const uint16_t* constants = (const uint16_t*)(table + SWAP(((const MathTableHeader*)table)->mathConstants));
71 
72     if (whichConstant < firstMathValueRecord) {
73         /* it's a simple 16-bit value */
74         return SWAP(constants[whichConstant]);
75     }
76     else if (whichConstant <= lastMathValueRecord) {
77         const MathValueRecord* valueRecords = (const MathValueRecord*)
78             ((char*)constants + firstMathValueRecord * sizeof(uint16_t) - firstMathValueRecord * sizeof(MathValueRecord));
79         return SWAP(valueRecords[whichConstant].value);
80     }
81     else if (whichConstant <= lastMathConstant) {
82         return SWAP(constants[whichConstant + (lastMathValueRecord - firstMathValueRecord + 1)]);
83     }
84     else
85         return 0; /* or abort, with "internal error" or something */
86 }
87 
88 int
get_ot_math_constant(int f,int n)89 get_ot_math_constant(int f, int n)
90 {
91     int rval = 0;
92 
93     if (fontarea[f] == OTGR_FONT_FLAG) {
94         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
95         rval = getMathConstant(font, (mathConstantIndex)n);
96         /* scale according to font size, except the ones that are percentages */
97         if (n > scriptScriptPercentScaleDown && n < radicalDegreeBottomRaisePercent)
98             rval = D2Fix(font->unitsToPoints(rval));
99     }
100     return rval;
101 }
102 
103 /* fontdimen IDs for math symbols font (family 2) */
104 #define math_x_height   5
105 #define math_quad       6
106 #define num1            8   /* numerator shift-up in display styles */
107 #define num2            9   /* numerator shift-up in non-display, non-\.{\\atop} */
108 #define num3            10  /* numerator shift-up in non-display \.{\\atop} */
109 #define denom1          11  /* denominator shift-down in display styles */
110 #define denom2          12  /* denominator shift-down in non-display styles */
111 #define sup1            13  /* superscript shift-up in uncramped display style */
112 #define sup2            14  /* superscript shift-up in uncramped non-display */
113 #define sup3            15  /* superscript shift-up in cramped styles */
114 #define sub1            16  /* subscript shift-down if superscript is absent */
115 #define sub2            17  /* subscript shift-down if superscript is present */
116 #define sup_drop        18  /* superscript baseline below top of large box */
117 #define sub_drop        19  /* subscript baseline below bottom of large box */
118 #define delim1          20  /* size of \.{\\atopwithdelims} delimiters */
119 #define delim2          21  /* size of \.{\\atopwithdelims} delimiters in non-displays */
120 #define axis_height     22  /* height of fraction lines above the baseline */
121 
122 const mathConstantIndex TeX_sym_to_OT_map[] = {
123     unknown,
124     unknown,
125     unknown,
126     unknown,
127     unknown,
128     accentBaseHeight, // x-height
129     unknown, // quad
130     unknown,
131     fractionNumeratorDisplayStyleShiftUp,
132     fractionNumeratorShiftUp,
133     stackTopShiftUp,
134     fractionDenominatorDisplayStyleShiftDown,
135     fractionDenominatorShiftDown,
136     superscriptShiftUp, // ??
137     superscriptShiftUp, // ??
138     superscriptShiftUpCramped,
139     subscriptShiftDown, // ??
140     subscriptShiftDown, // ??
141     superscriptBaselineDropMax, // ??
142     subscriptBaselineDropMin, // ??
143     delimitedSubFormulaMinHeight,
144     unknown, // using quad instead for now
145     axisHeight
146 };
147 
148 int
get_native_mathsy_param(int f,int n)149 get_native_mathsy_param(int f, int n)
150 {
151     int rval = 0;
152 
153     if (n == math_quad || n == delim2)
154         rval = fontsize[f];
155     else {
156         if (n < sizeof(TeX_sym_to_OT_map) / sizeof(mathConstantIndex)) {
157             mathConstantIndex ot_index = TeX_sym_to_OT_map[n];
158             if (ot_index != unknown)
159                 rval = get_ot_math_constant(f, (int)ot_index);
160         }
161     }
162 //  fprintf(stderr, " math_sy(%d, %d) returns %.3f\n", f, n, Fix2D(rval));
163 
164     return rval;
165 }
166 
167 /* fontdimen IDs for math extension font (family 3) */
168 #define default_rule_thickness  8   /* thickness of \.{\\over} bars */
169 #define big_op_spacing1         9   /* minimum clearance above a displayed op */
170 #define big_op_spacing2         10  /* minimum clearance below a displayed op */
171 #define big_op_spacing3         11  /* minimum baselineskip above displayed op */
172 #define big_op_spacing4         12  /* minimum baselineskip below displayed op */
173 #define big_op_spacing5         13  /* padding above and below displayed limits */
174 
175 const mathConstantIndex TeX_ext_to_OT_map[] = {
176     unknown,
177     unknown,
178     unknown,
179     unknown,
180     unknown,
181     accentBaseHeight, // x-height
182     unknown, // quad
183     unknown,
184     fractionRuleThickness, // default_rule_thickness
185     upperLimitGapMin, // big_op_spacing1
186     lowerLimitGapMin, // big_op_spacing2
187     upperLimitBaselineRiseMin, // big_op_spacing3
188     lowerLimitBaselineDropMin, // big_op_spacing4
189     stackGapMin // big_op_spacing5
190 };
191 
192 int
get_native_mathex_param(int f,int n)193 get_native_mathex_param(int f, int n)
194 {
195     int rval = 0;
196 
197     if (n == math_quad)
198         rval = fontsize[f];
199     else {
200         if (n < sizeof(TeX_ext_to_OT_map) / sizeof(mathConstantIndex)) {
201             mathConstantIndex ot_index = TeX_ext_to_OT_map[n];
202             if (ot_index != unknown)
203                 rval = get_ot_math_constant(f, (int)ot_index);
204         }
205     }
206 //  fprintf(stderr, " math_ex(%d, %d) returns %.3f\n", f, n, Fix2D(rval));
207 
208     return rval;
209 }
210 
211 int
get_ot_math_variant(int f,int g,int v,integer * adv,int horiz)212 get_ot_math_variant(int f, int g, int v, integer* adv, int horiz)
213 {
214     int rval = g;
215     *adv = -1;
216 
217     if (fontarea[f] == OTGR_FONT_FLAG) {
218         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
219 
220         const char* table = font->getMathTable();
221         if (table == NULL)
222             return rval;
223 
224         uint16_t    offset = SWAP(((const MathTableHeader*)table)->mathVariants);
225         if (offset == 0)
226             return rval;
227         const MathVariants* variants = (const MathVariants*)(table + offset);
228 
229         offset = horiz ? SWAP(variants->horizGlyphCoverage) : SWAP(variants->vertGlyphCoverage);
230         if (offset == 0)
231             return rval;
232         const Coverage* coverage = (const Coverage*)(((const char*)variants) + offset);
233 
234         int32_t index = getCoverage(coverage, g);
235         if (index >= 0) {
236             if (horiz)
237                 index += SWAP(variants->vertGlyphCount);
238             const MathGlyphConstruction*    construction = (const MathGlyphConstruction*)(((const char*)variants)
239                                                             + SWAP(variants->vertGlyphConstruction[index]));
240             if (v < SWAP(construction->variantCount)) {
241                 rval = SWAP(construction->mathGlyphVariantRecord[v].variantGlyph);
242                 *adv = D2Fix(font->unitsToPoints(SWAP(construction->mathGlyphVariantRecord[v].advanceMeasurement)));
243             }
244         }
245     }
246 
247     return rval;
248 }
249 
250 void*
get_ot_assembly_ptr(int f,int g,int horiz)251 get_ot_assembly_ptr(int f, int g, int horiz)
252 {
253     void*   rval = NULL;
254 
255     if (fontarea[f] == OTGR_FONT_FLAG) {
256         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
257 
258         const char* table = font->getMathTable();
259         if (table == NULL)
260             return rval;
261 
262         uint16_t    offset = SWAP(((const MathTableHeader*)table)->mathVariants);
263         if (offset == 0)
264             return rval;
265         const MathVariants* variants = (const MathVariants*)(table + offset);
266 
267         offset = horiz ? SWAP(variants->horizGlyphCoverage) : SWAP(variants->vertGlyphCoverage);
268         if (offset == 0)
269             return rval;
270         const Coverage* coverage = (const Coverage*)(((const char*)variants) + offset);
271 
272         int32_t index = getCoverage(coverage, g);
273         if (index >= 0) {
274             if (horiz)
275                 index += SWAP(variants->vertGlyphCount);
276             const MathGlyphConstruction*    construction = (const MathGlyphConstruction*)(((const char*)variants)
277                                                             + SWAP(variants->vertGlyphConstruction[index]));
278             offset = SWAP(construction->glyphAssembly);
279             if (offset != 0)
280                 rval = (void*)(((const char*)construction) + offset);
281         }
282     }
283 
284     return rval;
285 }
286 
287 int
get_ot_math_ital_corr(int f,int g)288 get_ot_math_ital_corr(int f, int g)
289 {
290     int rval = 0;
291 
292     if (fontarea[f] == OTGR_FONT_FLAG) {
293         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
294 
295         const char* table = font->getMathTable();
296         if (table == NULL)
297             return rval;
298 
299         uint16_t    offset = SWAP(((const MathTableHeader*)table)->mathGlyphInfo);
300         if (offset == 0)
301             return rval;
302         const MathGlyphInfo* glyphInfo = (const MathGlyphInfo*)(table + offset);
303 
304         offset = SWAP(glyphInfo->mathItalicsCorrectionInfo);
305         if (offset == 0)
306             return rval;
307         const MathItalicsCorrectionInfo* italCorrInfo = (const MathItalicsCorrectionInfo*)(((const char*)glyphInfo) + offset);
308 
309         offset = SWAP(italCorrInfo->coverage);
310         if (offset == 0)
311             return rval;
312         const Coverage* coverage = (const Coverage*)(((const char*)italCorrInfo) + offset);
313 
314         int32_t index = getCoverage(coverage, g);
315         if (index >= 0 && index < SWAP(italCorrInfo->italicsCorrectionCount))
316             rval = D2Fix(font->unitsToPoints(SWAP(italCorrInfo->italicsCorrection[index].value)));
317     }
318 
319     return rval;
320 }
321 
322 int
get_ot_math_accent_pos(int f,int g)323 get_ot_math_accent_pos(int f, int g)
324 {
325     int rval = 0x7fffffffUL;
326 
327     if (fontarea[f] == OTGR_FONT_FLAG) {
328         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
329 
330         const char* table = font->getMathTable();
331         if (table == NULL)
332             return rval;
333 
334         uint16_t    offset = SWAP(((const MathTableHeader*)table)->mathGlyphInfo);
335         if (offset == 0)
336             return rval;
337         const MathGlyphInfo* glyphInfo = (const MathGlyphInfo*)(table + offset);
338 
339         offset = SWAP(glyphInfo->mathTopAccentAttachment);
340         if (offset == 0)
341             return rval;
342         const MathTopAccentAttachment* accentAttachment = (const MathTopAccentAttachment*)(((const char*)glyphInfo) + offset);
343 
344         offset = SWAP(accentAttachment->coverage);
345         if (offset == 0)
346             return rval;
347         const Coverage* coverage = (const Coverage*)(((const char*)accentAttachment) + offset);
348 
349         int32_t index = getCoverage(coverage, g);
350         if (index >= 0 && index < SWAP(accentAttachment->topAccentAttachmentCount)) {
351             rval = (int16_t)SWAP(accentAttachment->topAccentAttachment[index].value);
352             rval = D2Fix(font->unitsToPoints(rval));
353         }
354     }
355 
356     return rval;
357 }
358 
359 int
ot_min_connector_overlap(int f)360 ot_min_connector_overlap(int f)
361 {
362     int rval = 0;
363 
364     if (fontarea[f] == OTGR_FONT_FLAG) {
365         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
366 
367         const char* table = font->getMathTable();
368         if (table == NULL)
369             return rval;
370 
371         uint16_t    offset = SWAP(((const MathTableHeader*)table)->mathVariants);
372         if (offset == 0)
373             return rval;
374         const MathVariants* variants = (const MathVariants*)(table + offset);
375 
376         rval = D2Fix(font->unitsToPoints(SWAP(variants->minConnectorOverlap)));
377     }
378 
379     return rval;
380 }
381 
382 typedef enum {
383     topRight,
384     topLeft,
385     bottomRight,
386     bottomLeft,
387 } MathKernSide;
388 
389 static int
getMathKernAt(int f,int g,MathKernSide side,int height)390 getMathKernAt(int f, int g, MathKernSide side, int height)
391 {
392     int rval = 0;
393     if (fontarea[f] == OTGR_FONT_FLAG) {
394         XeTeXFontInst* font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
395 
396         const char* table = font->getMathTable();
397         if (table == NULL)
398             return rval;
399 
400         uint16_t    offset = SWAP(((const MathTableHeader*)table)->mathGlyphInfo);
401         if (offset == 0)
402             return rval;
403 
404         const MathGlyphInfo* glyphInfo = (const MathGlyphInfo*)(table + offset);
405 
406         offset = SWAP(glyphInfo->mathKernInfo);
407         if (offset == 0)
408             return rval;
409 
410         const MathKernInfo* mathKernInfo = (const MathKernInfo*)(((const char*)glyphInfo) + offset);
411 
412         offset = SWAP(mathKernInfo->coverage);
413         if (offset == 0)
414             return rval;
415 
416         const Coverage* coverage = (const Coverage*)(((const char*)mathKernInfo) + offset);
417 
418         int32_t index = getCoverage(coverage, g);
419         if (index >= 0 && index < SWAP(mathKernInfo->kernInfoCount)) {
420             if (side == topRight)
421                 offset = SWAP(mathKernInfo->kernInfo[index].topRight);
422             else if (side == bottomRight)
423                 offset = SWAP(mathKernInfo->kernInfo[index].bottomRight);
424             else if (side == topLeft)
425                 offset = SWAP(mathKernInfo->kernInfo[index].topLeft);
426             else if (side == bottomLeft)
427                 offset = SWAP(mathKernInfo->kernInfo[index].bottomLeft);
428             else
429                 assert(0); // we should not reach here
430 
431             if (offset == 0)
432                 return rval;
433 
434             const MathKernTable* kernTable = (const MathKernTable*)(((const char*)mathKernInfo) + offset);
435 
436             uint16_t count = SWAP(kernTable->heightCount);
437 
438             // XXX: the following makes no sense WRT my understanding of the
439             // spec! it is just how things worked for me.
440             if (count == 0)
441                 rval = SWAP(kernTable->kern[-1].value);
442             else if (height < SWAP(kernTable->height[0].value))
443                 rval = SWAP(kernTable->kern[1].value);
444             else if (height > SWAP(kernTable->height[count].value))
445                 rval = SWAP(kernTable->kern[count+1].value);
446             else {
447                 for (int i = 0; i < count; i++) {
448                     if (height > SWAP(kernTable->height[i].value)) {
449                         rval = SWAP(kernTable->kern[i+1].value);
450                         break;
451                     }
452                 }
453             }
454 
455             //fprintf(stderr, "   kern: %f %f\n", font->unitsToPoints(height), font->unitsToPoints(rval));
456         }
457     }
458 
459     return rval;
460 }
461 
462 static float
glyph_height(int f,int g)463 glyph_height(int f, int g)
464 {
465     float rval = 0.0;
466 
467     if (fontarea[f] == OTGR_FONT_FLAG) {
468         XeTeXLayoutEngine engine = (XeTeXLayoutEngine)fontlayoutengine[f];
469         getGlyphHeightDepth(engine, g, &rval, NULL);
470     }
471 
472     return rval;
473 }
474 
475 static float
glyph_depth(int f,int g)476 glyph_depth(int f, int g)
477 {
478     float rval = 0.0;
479 
480     if (fontarea[f] == OTGR_FONT_FLAG) {
481         XeTeXLayoutEngine engine = (XeTeXLayoutEngine)fontlayoutengine[f];
482         getGlyphHeightDepth(engine, g, NULL, &rval);
483     }
484 
485     return rval;
486 }
487 
488 // keep in sync with xetex.web
489 #define sup_cmd 0
490 #define sub_cmd 1
491 
492 int
get_ot_math_kern(int f,int g,int sf,int sg,int cmd,int shift)493 get_ot_math_kern(int f, int g, int sf, int sg, int cmd, int shift)
494 {
495     int rval = 0;
496 
497     if (fontarea[f] == OTGR_FONT_FLAG) {
498         XeTeXFontInst* font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
499         int kern = 0, skern = 0;
500         float corr_height_top = 0.0, corr_height_bot = 0.0;
501 
502         shift = Fix2D(shift);
503 
504         if (cmd == sup_cmd) { // superscript
505             corr_height_top =  font->pointsToUnits(glyph_height(f, g));
506             corr_height_bot = -font->pointsToUnits(glyph_depth(sf, sg) + shift);
507 
508             kern = getMathKernAt(f, g, topRight, corr_height_top);
509             skern = getMathKernAt(sf, sg, bottomLeft, corr_height_top);
510             rval = kern + skern;
511 
512             kern = getMathKernAt(f, g, topRight, corr_height_bot);
513             skern = getMathKernAt(sf, sg, bottomLeft, corr_height_bot);
514             if ((kern + skern) < rval)
515                 rval = kern + skern;
516 
517         } else if (cmd == sub_cmd) { // subscript
518             corr_height_top =  font->pointsToUnits(glyph_height(sf, sg) - shift);
519             corr_height_bot = -font->pointsToUnits(glyph_depth(f, g));
520 
521             kern = getMathKernAt(f, g, bottomRight, corr_height_top);
522             skern = getMathKernAt(sf, sg, topLeft, corr_height_top);
523             rval = kern + skern;
524 
525             kern = getMathKernAt(f, g, bottomRight, corr_height_bot);
526             skern = getMathKernAt(sf, sg, topLeft, corr_height_bot);
527             if ((kern + skern) < rval)
528                 rval = kern + skern;
529 
530         } else {
531             assert(0); // we should not reach here
532         }
533 
534         rval = D2Fix(font->unitsToPoints(rval));
535     }
536 
537     return rval;
538 }
539 
540 int
ot_part_count(const GlyphAssembly * a)541 ot_part_count(const GlyphAssembly* a)
542 {
543     return SWAP(a->partCount);
544 }
545 
546 int
ot_part_glyph(const GlyphAssembly * a,int i)547 ot_part_glyph(const GlyphAssembly* a, int i)
548 {
549     return SWAP(a->partRecords[i].glyph);
550 }
551 
552 int
ot_part_is_extender(const GlyphAssembly * a,int i)553 ot_part_is_extender(const GlyphAssembly* a, int i)
554 {
555     return (SWAP(a->partRecords[i].partFlags) & fExtender) != 0;
556 }
557 
558 int
ot_part_start_connector(int f,const GlyphAssembly * a,int i)559 ot_part_start_connector(int f, const GlyphAssembly* a, int i)
560 {
561     int rval = 0;
562 
563     if (fontarea[f] == OTGR_FONT_FLAG) {
564         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
565         rval = D2Fix(font->unitsToPoints(SWAP(a->partRecords[i].startConnectorLength)));
566     }
567 
568     return rval;
569 }
570 
571 int
ot_part_end_connector(int f,const GlyphAssembly * a,int i)572 ot_part_end_connector(int f, const GlyphAssembly* a, int i)
573 {
574     int rval = 0;
575 
576     if (fontarea[f] == OTGR_FONT_FLAG) {
577         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
578         rval = D2Fix(font->unitsToPoints(SWAP(a->partRecords[i].endConnectorLength)));
579     }
580 
581     return rval;
582 }
583 
584 int
ot_part_full_advance(int f,const GlyphAssembly * a,int i)585 ot_part_full_advance(int f, const GlyphAssembly* a, int i)
586 {
587     int rval = 0;
588 
589     if (fontarea[f] == OTGR_FONT_FLAG) {
590         XeTeXFontInst*  font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]);
591         rval = D2Fix(font->unitsToPoints(SWAP(a->partRecords[i].fullAdvance)));
592     }
593 
594     return rval;
595 }
596