1 |
|
2 | import defineFunction from "../defineFunction";
|
3 | import type {Measurement} from "../units";
|
4 | import {calculateSize, validUnit} from "../units";
|
5 | import ParseError from "../ParseError";
|
6 | import {Img} from "../domTree";
|
7 | import mathMLTree from "../mathMLTree";
|
8 | import {assertNodeType} from "../parseNode";
|
9 | import type {CssStyle} from "../domTree";
|
10 |
|
11 | const sizeData = function(str: string): Measurement {
|
12 | if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) {
|
13 |
|
14 |
|
15 | return {number: +str, unit: "bp"};
|
16 | } else {
|
17 | const match = (/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/).exec(str);
|
18 | if (!match) {
|
19 | throw new ParseError("Invalid size: '" + str
|
20 | + "' in \\includegraphics");
|
21 | }
|
22 | const data = {
|
23 | number: +(match[1] + match[2]),
|
24 | unit: match[3],
|
25 | };
|
26 | if (!validUnit(data)) {
|
27 | throw new ParseError("Invalid unit: '" + data.unit
|
28 | + "' in \\includegraphics.");
|
29 | }
|
30 | return data;
|
31 | }
|
32 | };
|
33 |
|
34 | defineFunction({
|
35 | type: "includegraphics",
|
36 | names: ["\\includegraphics"],
|
37 | props: {
|
38 | numArgs: 1,
|
39 | numOptionalArgs: 1,
|
40 | argTypes: ["raw", "url"],
|
41 | allowedInText: false,
|
42 | },
|
43 | handler: ({parser}, args, optArgs) => {
|
44 | let width = {number: 0, unit: "em"};
|
45 | let height = {number: 0.9, unit: "em"};
|
46 | let totalheight = {number: 0, unit: "em"};
|
47 | let alt = "";
|
48 |
|
49 | if (optArgs[0]) {
|
50 | const attributeStr = assertNodeType(optArgs[0], "raw").string;
|
51 |
|
52 |
|
53 | const attributes = attributeStr.split(",");
|
54 | for (let i = 0; i < attributes.length; i++) {
|
55 | const keyVal = attributes[i].split("=");
|
56 | if (keyVal.length === 2) {
|
57 | const str = keyVal[1].trim();
|
58 | switch (keyVal[0].trim()) {
|
59 | case "alt":
|
60 | alt = str;
|
61 | break;
|
62 | case "width":
|
63 | width = sizeData(str);
|
64 | break;
|
65 | case "height":
|
66 | height = sizeData(str);
|
67 | break;
|
68 | case "totalheight":
|
69 | totalheight = sizeData(str);
|
70 | break;
|
71 | default:
|
72 | throw new ParseError("Invalid key: '" + keyVal[0] +
|
73 | "' in \\includegraphics.");
|
74 | }
|
75 | }
|
76 | }
|
77 | }
|
78 |
|
79 | const src = assertNodeType(args[0], "url").url;
|
80 |
|
81 | if (alt === "") {
|
82 |
|
83 | alt = src;
|
84 | alt = alt.replace(/^.*[\\/]/, '');
|
85 | alt = alt.substring(0, alt.lastIndexOf('.'));
|
86 | }
|
87 |
|
88 | return {
|
89 | type: "includegraphics",
|
90 | mode: parser.mode,
|
91 | alt: alt,
|
92 | width: width,
|
93 | height: height,
|
94 | totalheight: totalheight,
|
95 | src: src,
|
96 | };
|
97 | },
|
98 | htmlBuilder: (group, options) => {
|
99 | const height = calculateSize(group.height, options);
|
100 | let depth = 0;
|
101 |
|
102 | if (group.totalheight.number > 0) {
|
103 | depth = calculateSize(group.totalheight, options) - height;
|
104 | depth = Number(depth.toFixed(2));
|
105 | }
|
106 |
|
107 | let width = 0;
|
108 | if (group.width.number > 0) {
|
109 | width = calculateSize(group.width, options);
|
110 | }
|
111 |
|
112 | const style: CssStyle = {height: height + depth + "em"};
|
113 | if (width > 0) {
|
114 | style.width = width + "em";
|
115 | }
|
116 | if (depth > 0) {
|
117 | style.verticalAlign = -depth + "em";
|
118 | }
|
119 |
|
120 | const node = new Img(group.src, group.alt, style);
|
121 | node.height = height;
|
122 | node.depth = depth;
|
123 |
|
124 | return node;
|
125 | },
|
126 | mathmlBuilder: (group, options) => {
|
127 | const node = new mathMLTree.MathNode("mglyph", []);
|
128 | node.setAttribute("alt", group.alt);
|
129 |
|
130 | const height = calculateSize(group.height, options);
|
131 | let depth = 0;
|
132 | if (group.totalheight.number > 0) {
|
133 | depth = calculateSize(group.totalheight, options) - height;
|
134 | depth = depth.toFixed(2);
|
135 | node.setAttribute("valign", "-" + depth + "em");
|
136 | }
|
137 | node.setAttribute("height", height + depth + "em");
|
138 |
|
139 | if (group.width.number > 0) {
|
140 | const width = calculateSize(group.width, options);
|
141 | node.setAttribute("width", width + "em");
|
142 | }
|
143 | node.setAttribute("src", group.src);
|
144 | return node;
|
145 | },
|
146 | });
|