UNPKG

4.91 kBJavaScriptView Raw
1// @flow
2import defineFunction from "../defineFunction";
3import type {Measurement} from "../units";
4import {calculateSize, validUnit} from "../units";
5import ParseError from "../ParseError";
6import {Img} from "../domTree";
7import mathMLTree from "../mathMLTree";
8import {assertNodeType} from "../parseNode";
9import type {CssStyle} from "../domTree";
10
11const sizeData = function(str: string): Measurement {
12 if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) {
13 // str is a number with no unit specified.
14 // default unit is bp, per graphix package.
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]), // sign + magnitude, cast to number
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
34defineFunction({
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"}; // sorta character sized.
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 // Parser.js does not parse key/value pairs. We get a string.
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 // No alt given. Use the file name. Strip away the path.
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});