1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | import {SymbolNode, Anchor, Span, PathNode, SvgNode, createClass} from "./domTree";
|
9 | import {getCharacterMetrics} from "./fontMetrics";
|
10 | import symbols, {ligatures} from "./symbols";
|
11 | import utils from "./utils";
|
12 | import {wideCharacterFont} from "./wide-character";
|
13 | import {calculateSize} from "./units";
|
14 | import {DocumentFragment} from "./tree";
|
15 |
|
16 | import type Options from "./Options";
|
17 | import type {ParseNode} from "./parseNode";
|
18 | import type {CharacterMetrics} from "./fontMetrics";
|
19 | import type {FontVariant, Mode} from "./types";
|
20 | import type {documentFragment as HtmlDocumentFragment} from "./domTree";
|
21 | import type {HtmlDomNode, DomSpan, SvgSpan, CssStyle} from "./domTree";
|
22 | import type {Measurement} from "./units";
|
23 |
|
24 |
|
25 | const mathitLetters = [
|
26 | "\\imath", "ı",
|
27 | "\\jmath", "ȷ",
|
28 | "\\pounds", "\\mathsterling", "\\textsterling", "£",
|
29 | ];
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | const lookupSymbol = function(
|
36 | value: string,
|
37 | // TODO(#963): Use a union type for this.
|
38 | fontName: string,
|
39 | mode: Mode,
|
40 | ): {value: string, metrics: ?CharacterMetrics} {
|
41 |
|
42 | if (symbols[mode][value] && symbols[mode][value].replace) {
|
43 | value = symbols[mode][value].replace;
|
44 | }
|
45 | return {
|
46 | value: value,
|
47 | metrics: getCharacterMetrics(value, fontName, mode),
|
48 | };
|
49 | };
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | const makeSymbol = function(
|
62 | value: string,
|
63 | fontName: string,
|
64 | mode: Mode,
|
65 | options?: Options,
|
66 | classes?: string[],
|
67 | ): SymbolNode {
|
68 | const lookup = lookupSymbol(value, fontName, mode);
|
69 | const metrics = lookup.metrics;
|
70 | value = lookup.value;
|
71 |
|
72 | let symbolNode;
|
73 | if (metrics) {
|
74 | let italic = metrics.italic;
|
75 | if (mode === "text" || (options && options.font === "mathit")) {
|
76 | italic = 0;
|
77 | }
|
78 | symbolNode = new SymbolNode(
|
79 | value, metrics.height, metrics.depth, italic, metrics.skew,
|
80 | metrics.width, classes);
|
81 | } else {
|
82 |
|
83 | typeof console !== "undefined" && console.warn(
|
84 | "No character metrics for '" + value + "' in style '" +
|
85 | fontName + "'");
|
86 | symbolNode = new SymbolNode(value, 0, 0, 0, 0, 0, classes);
|
87 | }
|
88 |
|
89 | if (options) {
|
90 | symbolNode.maxFontSize = options.sizeMultiplier;
|
91 | if (options.style.isTight()) {
|
92 | symbolNode.classes.push("mtight");
|
93 | }
|
94 | const color = options.getColor();
|
95 | if (color) {
|
96 | symbolNode.style.color = color;
|
97 | }
|
98 | }
|
99 |
|
100 | return symbolNode;
|
101 | };
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | const mathsym = function(
|
110 | value: string,
|
111 | mode: Mode,
|
112 | options?: Options,
|
113 | classes?: string[] = [],
|
114 | ): SymbolNode {
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 | if ((options && options.font && options.font === "boldsymbol") &&
|
123 | lookupSymbol(value, "Main-Bold", mode).metrics) {
|
124 | return makeSymbol(value, "Main-Bold", mode, options,
|
125 | classes.concat(["mathbf"]));
|
126 | } else if (value === "\\" || symbols[mode][value].font === "main") {
|
127 | return makeSymbol(value, "Main-Regular", mode, options, classes);
|
128 | } else {
|
129 | return makeSymbol(
|
130 | value, "AMS-Regular", mode, options, classes.concat(["amsrm"]));
|
131 | }
|
132 | };
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | const mathdefault = function(
|
140 | value: string,
|
141 | mode: Mode,
|
142 | options: Options,
|
143 | classes: string[],
|
144 | ): {| fontName: string, fontClass: string |} {
|
145 | if (/[0-9]/.test(value.charAt(0)) ||
|
146 |
|
147 |
|
148 | utils.contains(mathitLetters, value)) {
|
149 | return {
|
150 | fontName: "Main-Italic",
|
151 | fontClass: "mathit",
|
152 | };
|
153 | } else {
|
154 | return {
|
155 | fontName: "Math-Italic",
|
156 | fontClass: "mathdefault",
|
157 | };
|
158 | }
|
159 | };
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | const mathnormal = function(
|
168 | value: string,
|
169 | mode: Mode,
|
170 | options: Options,
|
171 | classes: string[],
|
172 | ): {| fontName: string, fontClass: string |} {
|
173 | if (utils.contains(mathitLetters, value)) {
|
174 | return {
|
175 | fontName: "Main-Italic",
|
176 | fontClass: "mathit",
|
177 | };
|
178 | } else if (/[0-9]/.test(value.charAt(0))) {
|
179 | return {
|
180 | fontName: "Caligraphic-Regular",
|
181 | fontClass: "mathcal",
|
182 | };
|
183 | } else {
|
184 | return {
|
185 | fontName: "Math-Italic",
|
186 | fontClass: "mathdefault",
|
187 | };
|
188 | }
|
189 | };
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | const boldsymbol = function(
|
198 | value: string,
|
199 | mode: Mode,
|
200 | options: Options,
|
201 | classes: string[],
|
202 | ): {| fontName: string, fontClass: string |} {
|
203 | if (lookupSymbol(value, "Math-BoldItalic", mode).metrics) {
|
204 | return {
|
205 | fontName: "Math-BoldItalic",
|
206 | fontClass: "boldsymbol",
|
207 | };
|
208 | } else {
|
209 |
|
210 |
|
211 | return {
|
212 | fontName: "Main-Bold",
|
213 | fontClass: "mathbf",
|
214 | };
|
215 | }
|
216 | };
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
|
222 | group: ParseNode<NODETYPE>,
|
223 | options: Options,
|
224 | type: "mathord" | "textord",
|
225 | ): HtmlDocumentFragment | SymbolNode {
|
226 | const mode = group.mode;
|
227 | const text = group.text;
|
228 |
|
229 | const classes = ["mord"];
|
230 |
|
231 |
|
232 | const isFont = mode === "math" || (mode === "text" && options.font);
|
233 | const fontOrFamily = isFont ? options.font : options.fontFamily;
|
234 | if (text.charCodeAt(0) === 0xD835) {
|
235 |
|
236 | const [wideFontName, wideFontClass] = wideCharacterFont(text, mode);
|
237 | return makeSymbol(text, wideFontName, mode, options,
|
238 | classes.concat(wideFontClass));
|
239 | } else if (fontOrFamily) {
|
240 | let fontName;
|
241 | let fontClasses;
|
242 | if (fontOrFamily === "boldsymbol" || fontOrFamily === "mathnormal") {
|
243 | const fontData = fontOrFamily === "boldsymbol"
|
244 | ? boldsymbol(text, mode, options, classes)
|
245 | : mathnormal(text, mode, options, classes);
|
246 | fontName = fontData.fontName;
|
247 | fontClasses = [fontData.fontClass];
|
248 | } else if (utils.contains(mathitLetters, text)) {
|
249 | fontName = "Main-Italic";
|
250 | fontClasses = ["mathit"];
|
251 | } else if (isFont) {
|
252 | fontName = fontMap[fontOrFamily].fontName;
|
253 | fontClasses = [fontOrFamily];
|
254 | } else {
|
255 | fontName = retrieveTextFontName(fontOrFamily, options.fontWeight,
|
256 | options.fontShape);
|
257 | fontClasses = [fontOrFamily, options.fontWeight, options.fontShape];
|
258 | }
|
259 |
|
260 | if (lookupSymbol(text, fontName, mode).metrics) {
|
261 | return makeSymbol(text, fontName, mode, options,
|
262 | classes.concat(fontClasses));
|
263 | } else if (ligatures.hasOwnProperty(text) &&
|
264 | fontName.substr(0, 10) === "Typewriter") {
|
265 |
|
266 | const parts = [];
|
267 | for (let i = 0; i < text.length; i++) {
|
268 | parts.push(makeSymbol(text[i], fontName, mode, options,
|
269 | classes.concat(fontClasses)));
|
270 | }
|
271 | return makeFragment(parts);
|
272 | }
|
273 | }
|
274 |
|
275 |
|
276 | if (type === "mathord") {
|
277 | const fontLookup = mathdefault(text, mode, options, classes);
|
278 | return makeSymbol(text, fontLookup.fontName, mode, options,
|
279 | classes.concat([fontLookup.fontClass]));
|
280 | } else if (type === "textord") {
|
281 | const font = symbols[mode][text] && symbols[mode][text].font;
|
282 | if (font === "ams") {
|
283 | const fontName = retrieveTextFontName("amsrm", options.fontWeight,
|
284 | options.fontShape);
|
285 | return makeSymbol(
|
286 | text, fontName, mode, options,
|
287 | classes.concat("amsrm", options.fontWeight, options.fontShape));
|
288 | } else if (font === "main" || !font) {
|
289 | const fontName = retrieveTextFontName("textrm", options.fontWeight,
|
290 | options.fontShape);
|
291 | return makeSymbol(
|
292 | text, fontName, mode, options,
|
293 | classes.concat(options.fontWeight, options.fontShape));
|
294 | } else {
|
295 | const fontName = retrieveTextFontName(font, options.fontWeight,
|
296 | options.fontShape);
|
297 |
|
298 | return makeSymbol(
|
299 | text, fontName, mode, options,
|
300 | classes.concat(fontName, options.fontWeight, options.fontShape));
|
301 | }
|
302 | } else {
|
303 | throw new Error("unexpected type: " + type + " in makeOrd");
|
304 | }
|
305 | };
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 | const canCombine = (prev: SymbolNode, next: SymbolNode) => {
|
312 | if (createClass(prev.classes) !== createClass(next.classes)
|
313 | || prev.skew !== next.skew
|
314 | || prev.maxFontSize !== next.maxFontSize) {
|
315 | return false;
|
316 | }
|
317 |
|
318 | for (const style in prev.style) {
|
319 | if (prev.style.hasOwnProperty(style)
|
320 | && prev.style[style] !== next.style[style]) {
|
321 | return false;
|
322 | }
|
323 | }
|
324 |
|
325 | for (const style in next.style) {
|
326 | if (next.style.hasOwnProperty(style)
|
327 | && prev.style[style] !== next.style[style]) {
|
328 | return false;
|
329 | }
|
330 | }
|
331 |
|
332 | return true;
|
333 | };
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 | const tryCombineChars = (chars: HtmlDomNode[]): HtmlDomNode[] => {
|
340 | for (let i = 0; i < chars.length - 1; i++) {
|
341 | const prev = chars[i];
|
342 | const next = chars[i + 1];
|
343 | if (prev instanceof SymbolNode
|
344 | && next instanceof SymbolNode
|
345 | && canCombine(prev, next)) {
|
346 |
|
347 | prev.text += next.text;
|
348 | prev.height = Math.max(prev.height, next.height);
|
349 | prev.depth = Math.max(prev.depth, next.depth);
|
350 |
|
351 |
|
352 |
|
353 | prev.italic = next.italic;
|
354 | chars.splice(i + 1, 1);
|
355 | i--;
|
356 | }
|
357 | }
|
358 | return chars;
|
359 | };
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | const sizeElementFromChildren = function(
|
366 | elem: DomSpan | Anchor | HtmlDocumentFragment,
|
367 | ) {
|
368 | let height = 0;
|
369 | let depth = 0;
|
370 | let maxFontSize = 0;
|
371 |
|
372 | for (let i = 0; i < elem.children.length; i++) {
|
373 | const child = elem.children[i];
|
374 | if (child.height > height) {
|
375 | height = child.height;
|
376 | }
|
377 | if (child.depth > depth) {
|
378 | depth = child.depth;
|
379 | }
|
380 | if (child.maxFontSize > maxFontSize) {
|
381 | maxFontSize = child.maxFontSize;
|
382 | }
|
383 | }
|
384 |
|
385 | elem.height = height;
|
386 | elem.depth = depth;
|
387 | elem.maxFontSize = maxFontSize;
|
388 | };
|
389 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 | const makeSpan = function(
|
399 | classes?: string[],
|
400 | children?: HtmlDomNode[],
|
401 | options?: Options,
|
402 | style?: CssStyle,
|
403 | ): DomSpan {
|
404 | const span = new Span(classes, children, options, style);
|
405 |
|
406 | sizeElementFromChildren(span);
|
407 |
|
408 | return span;
|
409 | };
|
410 |
|
411 |
|
412 |
|
413 | const makeSvgSpan = (
|
414 | classes?: string[],
|
415 | children?: SvgNode[],
|
416 | options?: Options,
|
417 | style?: CssStyle,
|
418 | ): SvgSpan => new Span(classes, children, options, style);
|
419 |
|
420 | const makeLineSpan = function(
|
421 | className: string,
|
422 | options: Options,
|
423 | thickness?: number,
|
424 | ) {
|
425 | const line = makeSpan([className], [], options);
|
426 | line.height = thickness || options.fontMetrics().defaultRuleThickness;
|
427 | line.style.borderBottomWidth = line.height + "em";
|
428 | line.maxFontSize = 1.0;
|
429 | return line;
|
430 | };
|
431 |
|
432 |
|
433 |
|
434 |
|
435 |
|
436 | const makeAnchor = function(
|
437 | href: string,
|
438 | classes: string[],
|
439 | children: HtmlDomNode[],
|
440 | options: Options,
|
441 | ) {
|
442 | const anchor = new Anchor(href, classes, children, options);
|
443 |
|
444 | sizeElementFromChildren(anchor);
|
445 |
|
446 | return anchor;
|
447 | };
|
448 |
|
449 |
|
450 |
|
451 |
|
452 | const makeFragment = function(
|
453 | children: HtmlDomNode[],
|
454 | ): HtmlDocumentFragment {
|
455 | const fragment = new DocumentFragment(children);
|
456 |
|
457 | sizeElementFromChildren(fragment);
|
458 |
|
459 | return fragment;
|
460 | };
|
461 |
|
462 |
|
463 |
|
464 |
|
465 |
|
466 | const wrapFragment = function(
|
467 | group: HtmlDomNode,
|
468 | options: Options,
|
469 | ): HtmlDomNode {
|
470 | if (group instanceof DocumentFragment) {
|
471 | return makeSpan([], [group], options);
|
472 | }
|
473 | return group;
|
474 | };
|
475 |
|
476 |
|
477 |
|
478 | export type VListElem = {|
|
479 | type: "elem",
|
480 | elem: HtmlDomNode,
|
481 | marginLeft?: ?string,
|
482 | marginRight?: string,
|
483 | wrapperClasses?: string[],
|
484 | wrapperStyle?: CssStyle,
|
485 | |};
|
486 | type VListElemAndShift = {|
|
487 | type: "elem",
|
488 | elem: HtmlDomNode,
|
489 | shift: number,
|
490 | marginLeft?: ?string,
|
491 | marginRight?: string,
|
492 | wrapperClasses?: string[],
|
493 | wrapperStyle?: CssStyle,
|
494 | |};
|
495 | type VListKern = {| type: "kern", size: number |};
|
496 |
|
497 |
|
498 |
|
499 | type VListChild = VListElem | VListKern;
|
500 |
|
501 | type VListParam = {|
|
502 |
|
503 | positionType: "individualShift",
|
504 | children: VListElemAndShift[],
|
505 | |} | {|
|
506 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 | positionType: "top" | "bottom" | "shift",
|
514 | positionData: number,
|
515 | children: VListChild[],
|
516 | |} | {|
|
517 |
|
518 |
|
519 |
|
520 | positionType: "firstBaseline",
|
521 | children: VListChild[],
|
522 | |};
|
523 |
|
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 | const getVListChildrenAndDepth = function(params: VListParam): {
|
530 | children: (VListChild | VListElemAndShift)[] | VListChild[],
|
531 | depth: number,
|
532 | } {
|
533 | if (params.positionType === "individualShift") {
|
534 | const oldChildren = params.children;
|
535 | const children: (VListChild | VListElemAndShift)[] = [oldChildren[0]];
|
536 |
|
537 |
|
538 |
|
539 | const depth = -oldChildren[0].shift - oldChildren[0].elem.depth;
|
540 | let currPos = depth;
|
541 | for (let i = 1; i < oldChildren.length; i++) {
|
542 | const diff = -oldChildren[i].shift - currPos -
|
543 | oldChildren[i].elem.depth;
|
544 | const size = diff -
|
545 | (oldChildren[i - 1].elem.height +
|
546 | oldChildren[i - 1].elem.depth);
|
547 |
|
548 | currPos = currPos + diff;
|
549 |
|
550 | children.push({type: "kern", size});
|
551 | children.push(oldChildren[i]);
|
552 | }
|
553 |
|
554 | return {children, depth};
|
555 | }
|
556 |
|
557 | let depth;
|
558 | if (params.positionType === "top") {
|
559 |
|
560 |
|
561 | let bottom = params.positionData;
|
562 | for (let i = 0; i < params.children.length; i++) {
|
563 | const child = params.children[i];
|
564 | bottom -= child.type === "kern"
|
565 | ? child.size
|
566 | : child.elem.height + child.elem.depth;
|
567 | }
|
568 | depth = bottom;
|
569 | } else if (params.positionType === "bottom") {
|
570 | depth = -params.positionData;
|
571 | } else {
|
572 | const firstChild = params.children[0];
|
573 | if (firstChild.type !== "elem") {
|
574 | throw new Error('First child must have type "elem".');
|
575 | }
|
576 | if (params.positionType === "shift") {
|
577 | depth = -firstChild.elem.depth - params.positionData;
|
578 | } else if (params.positionType === "firstBaseline") {
|
579 | depth = -firstChild.elem.depth;
|
580 | } else {
|
581 | throw new Error(`Invalid positionType ${params.positionType}.`);
|
582 | }
|
583 | }
|
584 | return {children: params.children, depth};
|
585 | };
|
586 |
|
587 |
|
588 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 | const makeVList = function(params: VListParam, options: Options): DomSpan {
|
594 | const {children, depth} = getVListChildrenAndDepth(params);
|
595 |
|
596 |
|
597 |
|
598 |
|
599 |
|
600 |
|
601 |
|
602 |
|
603 | let pstrutSize = 0;
|
604 | for (let i = 0; i < children.length; i++) {
|
605 | const child = children[i];
|
606 | if (child.type === "elem") {
|
607 | const elem = child.elem;
|
608 | pstrutSize = Math.max(pstrutSize, elem.maxFontSize, elem.height);
|
609 | }
|
610 | }
|
611 | pstrutSize += 2;
|
612 | const pstrut = makeSpan(["pstrut"], []);
|
613 | pstrut.style.height = pstrutSize + "em";
|
614 |
|
615 |
|
616 | const realChildren = [];
|
617 | let minPos = depth;
|
618 | let maxPos = depth;
|
619 | let currPos = depth;
|
620 | for (let i = 0; i < children.length; i++) {
|
621 | const child = children[i];
|
622 | if (child.type === "kern") {
|
623 | currPos += child.size;
|
624 | } else {
|
625 | const elem = child.elem;
|
626 | const classes = child.wrapperClasses || [];
|
627 | const style = child.wrapperStyle || {};
|
628 |
|
629 | const childWrap = makeSpan(classes, [pstrut, elem], undefined, style);
|
630 | childWrap.style.top = (-pstrutSize - currPos - elem.depth) + "em";
|
631 | if (child.marginLeft) {
|
632 | childWrap.style.marginLeft = child.marginLeft;
|
633 | }
|
634 | if (child.marginRight) {
|
635 | childWrap.style.marginRight = child.marginRight;
|
636 | }
|
637 |
|
638 | realChildren.push(childWrap);
|
639 | currPos += elem.height + elem.depth;
|
640 | }
|
641 | minPos = Math.min(minPos, currPos);
|
642 | maxPos = Math.max(maxPos, currPos);
|
643 | }
|
644 |
|
645 |
|
646 |
|
647 |
|
648 | const vlist = makeSpan(["vlist"], realChildren);
|
649 | vlist.style.height = maxPos + "em";
|
650 |
|
651 |
|
652 | let rows;
|
653 | if (minPos < 0) {
|
654 |
|
655 |
|
656 |
|
657 |
|
658 |
|
659 | const emptySpan = makeSpan([], []);
|
660 | const depthStrut = makeSpan(["vlist"], [emptySpan]);
|
661 | depthStrut.style.height = -minPos + "em";
|
662 |
|
663 |
|
664 |
|
665 | const topStrut = makeSpan(["vlist-s"], [new SymbolNode("\u200b")]);
|
666 |
|
667 | rows = [makeSpan(["vlist-r"], [vlist, topStrut]),
|
668 | makeSpan(["vlist-r"], [depthStrut])];
|
669 | } else {
|
670 | rows = [makeSpan(["vlist-r"], [vlist])];
|
671 | }
|
672 |
|
673 | const vtable = makeSpan(["vlist-t"], rows);
|
674 | if (rows.length === 2) {
|
675 | vtable.classes.push("vlist-t2");
|
676 | }
|
677 | vtable.height = maxPos;
|
678 | vtable.depth = -minPos;
|
679 | return vtable;
|
680 | };
|
681 |
|
682 |
|
683 |
|
684 |
|
685 | const makeGlue = (measurement: Measurement, options: Options): DomSpan => {
|
686 |
|
687 | const rule = makeSpan(["mspace"], [], options);
|
688 | const size = calculateSize(measurement, options);
|
689 | rule.style.marginRight = `${size}em`;
|
690 | return rule;
|
691 | };
|
692 |
|
693 |
|
694 | const retrieveTextFontName = function(
|
695 | fontFamily: string,
|
696 | fontWeight: string,
|
697 | fontShape: string,
|
698 | ): string {
|
699 | let baseFontName = "";
|
700 | switch (fontFamily) {
|
701 | case "amsrm":
|
702 | baseFontName = "AMS";
|
703 | break;
|
704 | case "textrm":
|
705 | baseFontName = "Main";
|
706 | break;
|
707 | case "textsf":
|
708 | baseFontName = "SansSerif";
|
709 | break;
|
710 | case "texttt":
|
711 | baseFontName = "Typewriter";
|
712 | break;
|
713 | default:
|
714 | baseFontName = fontFamily;
|
715 | }
|
716 |
|
717 | let fontStylesName;
|
718 | if (fontWeight === "textbf" && fontShape === "textit") {
|
719 | fontStylesName = "BoldItalic";
|
720 | } else if (fontWeight === "textbf") {
|
721 | fontStylesName = "Bold";
|
722 | } else if (fontWeight === "textit") {
|
723 | fontStylesName = "Italic";
|
724 | } else {
|
725 | fontStylesName = "Regular";
|
726 | }
|
727 |
|
728 | return `${baseFontName}-${fontStylesName}`;
|
729 | };
|
730 |
|
731 |
|
732 |
|
733 |
|
734 |
|
735 |
|
736 |
|
737 | const fontMap: {[string]: {| variant: FontVariant, fontName: string |}} = {
|
738 |
|
739 | "mathbf": {
|
740 | variant: "bold",
|
741 | fontName: "Main-Bold",
|
742 | },
|
743 | "mathrm": {
|
744 | variant: "normal",
|
745 | fontName: "Main-Regular",
|
746 | },
|
747 | "textit": {
|
748 | variant: "italic",
|
749 | fontName: "Main-Italic",
|
750 | },
|
751 | "mathit": {
|
752 | variant: "italic",
|
753 | fontName: "Main-Italic",
|
754 | },
|
755 |
|
756 |
|
757 |
|
758 |
|
759 |
|
760 |
|
761 |
|
762 |
|
763 |
|
764 | "mathbb": {
|
765 | variant: "double-struck",
|
766 | fontName: "AMS-Regular",
|
767 | },
|
768 | "mathcal": {
|
769 | variant: "script",
|
770 | fontName: "Caligraphic-Regular",
|
771 | },
|
772 | "mathfrak": {
|
773 | variant: "fraktur",
|
774 | fontName: "Fraktur-Regular",
|
775 | },
|
776 | "mathscr": {
|
777 | variant: "script",
|
778 | fontName: "Script-Regular",
|
779 | },
|
780 | "mathsf": {
|
781 | variant: "sans-serif",
|
782 | fontName: "SansSerif-Regular",
|
783 | },
|
784 | "mathtt": {
|
785 | variant: "monospace",
|
786 | fontName: "Typewriter-Regular",
|
787 | },
|
788 | };
|
789 |
|
790 | const svgData: {
|
791 | [string]: ([string, number, number])
|
792 | } = {
|
793 |
|
794 | vec: ["vec", 0.471, 0.714],
|
795 | oiintSize1: ["oiintSize1", 0.957, 0.499],
|
796 | oiintSize2: ["oiintSize2", 1.472, 0.659],
|
797 | oiiintSize1: ["oiiintSize1", 1.304, 0.499],
|
798 | oiiintSize2: ["oiiintSize2", 1.98, 0.659],
|
799 | };
|
800 |
|
801 | const staticSvg = function(value: string, options: Options): SvgSpan {
|
802 |
|
803 | const [pathName, width, height] = svgData[value];
|
804 | const path = new PathNode(pathName);
|
805 | const svgNode = new SvgNode([path], {
|
806 | "width": width + "em",
|
807 | "height": height + "em",
|
808 |
|
809 | "style": "width:" + width + "em",
|
810 | "viewBox": "0 0 " + 1000 * width + " " + 1000 * height,
|
811 | "preserveAspectRatio": "xMinYMin",
|
812 | });
|
813 | const span = makeSvgSpan(["overlay"], [svgNode], options);
|
814 | span.height = height;
|
815 | span.style.height = height + "em";
|
816 | span.style.width = width + "em";
|
817 | return span;
|
818 | };
|
819 |
|
820 | export default {
|
821 | fontMap,
|
822 | makeSymbol,
|
823 | mathsym,
|
824 | makeSpan,
|
825 | makeSvgSpan,
|
826 | makeLineSpan,
|
827 | makeAnchor,
|
828 | makeFragment,
|
829 | wrapFragment,
|
830 | makeVList,
|
831 | makeOrd,
|
832 | makeGlue,
|
833 | staticSvg,
|
834 | svgData,
|
835 | tryCombineChars,
|
836 | };
|