xhtml.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2021 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.XhtmlNamespace = void 0;
  27. var _xfa_object = require("./xfa_object.js");
  28. var _namespaces = require("./namespaces.js");
  29. var _html_utils = require("./html_utils.js");
  30. var _utils = require("./utils.js");
  31. const XHTML_NS_ID = _namespaces.NamespaceIds.xhtml.id;
  32. const VALID_STYLES = new Set(["color", "font", "font-family", "font-size", "font-stretch", "font-style", "font-weight", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "letter-spacing", "line-height", "orphans", "page-break-after", "page-break-before", "page-break-inside", "tab-interval", "tab-stop", "text-align", "text-decoration", "text-indent", "vertical-align", "widows", "kerning-mode", "xfa-font-horizontal-scale", "xfa-font-vertical-scale", "xfa-spacerun", "xfa-tab-stops"]);
  33. const StyleMapping = new Map([["page-break-after", "breakAfter"], ["page-break-before", "breakBefore"], ["page-break-inside", "breakInside"], ["kerning-mode", value => value === "none" ? "none" : "normal"], ["xfa-font-horizontal-scale", value => `scaleX(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-font-vertical-scale", value => `scaleY(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-spacerun", ""], ["xfa-tab-stops", ""], ["font-size", (value, original) => {
  34. value = original.fontSize = (0, _utils.getMeasurement)(value);
  35. return (0, _html_utils.measureToString)(0.99 * value);
  36. }], ["letter-spacing", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["line-height", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-bottom", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-left", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-right", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-top", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["text-indent", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["font-family", value => value]]);
  37. const spacesRegExp = /\s+/g;
  38. const crlfRegExp = /[\r\n]+/g;
  39. function mapStyle(styleStr, node) {
  40. const style = Object.create(null);
  41. if (!styleStr) {
  42. return style;
  43. }
  44. const original = Object.create(null);
  45. for (const [key, value] of styleStr.split(";").map(s => s.split(":", 2))) {
  46. const mapping = StyleMapping.get(key);
  47. if (mapping === "") {
  48. continue;
  49. }
  50. let newValue = value;
  51. if (mapping) {
  52. if (typeof mapping === "string") {
  53. newValue = mapping;
  54. } else {
  55. newValue = mapping(value, original);
  56. }
  57. }
  58. if (key.endsWith("scale")) {
  59. if (style.transform) {
  60. style.transform = `${style[key]} ${newValue}`;
  61. } else {
  62. style.transform = newValue;
  63. }
  64. } else {
  65. style[key.replaceAll(/-([a-zA-Z])/g, (_, x) => x.toUpperCase())] = newValue;
  66. }
  67. }
  68. if (style.fontFamily) {
  69. (0, _html_utils.setFontFamily)({
  70. typeface: style.fontFamily,
  71. weight: style.fontWeight || "normal",
  72. posture: style.fontStyle || "normal",
  73. size: original.fontSize || 0
  74. }, node, node[_xfa_object.$globalData].fontFinder, style);
  75. }
  76. (0, _html_utils.fixTextIndent)(style);
  77. return style;
  78. }
  79. function checkStyle(node) {
  80. if (!node.style) {
  81. return "";
  82. }
  83. return node.style.trim().split(/\s*;\s*/).filter(s => !!s).map(s => s.split(/\s*:\s*/, 2)).filter(([key, value]) => {
  84. if (key === "font-family") {
  85. node[_xfa_object.$globalData].usedTypefaces.add(value);
  86. }
  87. return VALID_STYLES.has(key);
  88. }).map(kv => kv.join(":")).join(";");
  89. }
  90. const NoWhites = new Set(["body", "html"]);
  91. class XhtmlObject extends _xfa_object.XmlObject {
  92. constructor(attributes, name) {
  93. super(XHTML_NS_ID, name);
  94. this.style = attributes.style || "";
  95. }
  96. [_xfa_object.$clean](builder) {
  97. super[_xfa_object.$clean](builder);
  98. this.style = checkStyle(this);
  99. }
  100. [_xfa_object.$acceptWhitespace]() {
  101. return !NoWhites.has(this[_xfa_object.$nodeName]);
  102. }
  103. [_xfa_object.$onText](str) {
  104. str = str.replace(crlfRegExp, "");
  105. if (!this.style.includes("xfa-spacerun:yes")) {
  106. str = str.replace(spacesRegExp, " ");
  107. }
  108. if (str) {
  109. this[_xfa_object.$content] += str;
  110. }
  111. }
  112. [_xfa_object.$pushGlyphs](measure, mustPop = true) {
  113. const xfaFont = Object.create(null);
  114. const margin = {
  115. top: NaN,
  116. bottom: NaN,
  117. left: NaN,
  118. right: NaN
  119. };
  120. let lineHeight = null;
  121. for (const [key, value] of this.style.split(";").map(s => s.split(":", 2))) {
  122. switch (key) {
  123. case "font-family":
  124. xfaFont.typeface = (0, _utils.stripQuotes)(value);
  125. break;
  126. case "font-size":
  127. xfaFont.size = (0, _utils.getMeasurement)(value);
  128. break;
  129. case "font-weight":
  130. xfaFont.weight = value;
  131. break;
  132. case "font-style":
  133. xfaFont.posture = value;
  134. break;
  135. case "letter-spacing":
  136. xfaFont.letterSpacing = (0, _utils.getMeasurement)(value);
  137. break;
  138. case "margin":
  139. const values = value.split(/ \t/).map(x => (0, _utils.getMeasurement)(x));
  140. switch (values.length) {
  141. case 1:
  142. margin.top = margin.bottom = margin.left = margin.right = values[0];
  143. break;
  144. case 2:
  145. margin.top = margin.bottom = values[0];
  146. margin.left = margin.right = values[1];
  147. break;
  148. case 3:
  149. margin.top = values[0];
  150. margin.bottom = values[2];
  151. margin.left = margin.right = values[1];
  152. break;
  153. case 4:
  154. margin.top = values[0];
  155. margin.left = values[1];
  156. margin.bottom = values[2];
  157. margin.right = values[3];
  158. break;
  159. }
  160. break;
  161. case "margin-top":
  162. margin.top = (0, _utils.getMeasurement)(value);
  163. break;
  164. case "margin-bottom":
  165. margin.bottom = (0, _utils.getMeasurement)(value);
  166. break;
  167. case "margin-left":
  168. margin.left = (0, _utils.getMeasurement)(value);
  169. break;
  170. case "margin-right":
  171. margin.right = (0, _utils.getMeasurement)(value);
  172. break;
  173. case "line-height":
  174. lineHeight = (0, _utils.getMeasurement)(value);
  175. break;
  176. }
  177. }
  178. measure.pushData(xfaFont, margin, lineHeight);
  179. if (this[_xfa_object.$content]) {
  180. measure.addString(this[_xfa_object.$content]);
  181. } else {
  182. for (const child of this[_xfa_object.$getChildren]()) {
  183. if (child[_xfa_object.$nodeName] === "#text") {
  184. measure.addString(child[_xfa_object.$content]);
  185. continue;
  186. }
  187. child[_xfa_object.$pushGlyphs](measure);
  188. }
  189. }
  190. if (mustPop) {
  191. measure.popFont();
  192. }
  193. }
  194. [_xfa_object.$toHTML](availableSpace) {
  195. const children = [];
  196. this[_xfa_object.$extra] = {
  197. children
  198. };
  199. this[_xfa_object.$childrenToHTML]({});
  200. if (children.length === 0 && !this[_xfa_object.$content]) {
  201. return _utils.HTMLResult.EMPTY;
  202. }
  203. return _utils.HTMLResult.success({
  204. name: this[_xfa_object.$nodeName],
  205. attributes: {
  206. href: this.href,
  207. style: mapStyle(this.style, this)
  208. },
  209. children,
  210. value: this[_xfa_object.$content] || ""
  211. });
  212. }
  213. }
  214. class A extends XhtmlObject {
  215. constructor(attributes) {
  216. super(attributes, "a");
  217. this.href = (0, _html_utils.fixURL)(attributes.href) || "";
  218. }
  219. }
  220. class B extends XhtmlObject {
  221. constructor(attributes) {
  222. super(attributes, "b");
  223. }
  224. [_xfa_object.$pushGlyphs](measure) {
  225. measure.pushFont({
  226. weight: "bold"
  227. });
  228. super[_xfa_object.$pushGlyphs](measure);
  229. measure.popFont();
  230. }
  231. }
  232. class Body extends XhtmlObject {
  233. constructor(attributes) {
  234. super(attributes, "body");
  235. }
  236. [_xfa_object.$toHTML](availableSpace) {
  237. const res = super[_xfa_object.$toHTML](availableSpace);
  238. const {
  239. html
  240. } = res;
  241. if (!html) {
  242. return _utils.HTMLResult.EMPTY;
  243. }
  244. html.name = "div";
  245. html.attributes.class = ["xfaRich"];
  246. return res;
  247. }
  248. }
  249. class Br extends XhtmlObject {
  250. constructor(attributes) {
  251. super(attributes, "br");
  252. }
  253. [_xfa_object.$text]() {
  254. return "\n";
  255. }
  256. [_xfa_object.$pushGlyphs](measure) {
  257. measure.addString("\n");
  258. }
  259. [_xfa_object.$toHTML](availableSpace) {
  260. return _utils.HTMLResult.success({
  261. name: "br"
  262. });
  263. }
  264. }
  265. class Html extends XhtmlObject {
  266. constructor(attributes) {
  267. super(attributes, "html");
  268. }
  269. [_xfa_object.$toHTML](availableSpace) {
  270. const children = [];
  271. this[_xfa_object.$extra] = {
  272. children
  273. };
  274. this[_xfa_object.$childrenToHTML]({});
  275. if (children.length === 0) {
  276. return _utils.HTMLResult.success({
  277. name: "div",
  278. attributes: {
  279. class: ["xfaRich"],
  280. style: {}
  281. },
  282. value: this[_xfa_object.$content] || ""
  283. });
  284. }
  285. if (children.length === 1) {
  286. const child = children[0];
  287. if (child.attributes && child.attributes.class.includes("xfaRich")) {
  288. return _utils.HTMLResult.success(child);
  289. }
  290. }
  291. return _utils.HTMLResult.success({
  292. name: "div",
  293. attributes: {
  294. class: ["xfaRich"],
  295. style: {}
  296. },
  297. children
  298. });
  299. }
  300. }
  301. class I extends XhtmlObject {
  302. constructor(attributes) {
  303. super(attributes, "i");
  304. }
  305. [_xfa_object.$pushGlyphs](measure) {
  306. measure.pushFont({
  307. posture: "italic"
  308. });
  309. super[_xfa_object.$pushGlyphs](measure);
  310. measure.popFont();
  311. }
  312. }
  313. class Li extends XhtmlObject {
  314. constructor(attributes) {
  315. super(attributes, "li");
  316. }
  317. }
  318. class Ol extends XhtmlObject {
  319. constructor(attributes) {
  320. super(attributes, "ol");
  321. }
  322. }
  323. class P extends XhtmlObject {
  324. constructor(attributes) {
  325. super(attributes, "p");
  326. }
  327. [_xfa_object.$pushGlyphs](measure) {
  328. super[_xfa_object.$pushGlyphs](measure, false);
  329. measure.addString("\n");
  330. measure.addPara();
  331. measure.popFont();
  332. }
  333. [_xfa_object.$text]() {
  334. return super[_xfa_object.$text]() + "\n";
  335. }
  336. }
  337. class Span extends XhtmlObject {
  338. constructor(attributes) {
  339. super(attributes, "span");
  340. }
  341. }
  342. class Sub extends XhtmlObject {
  343. constructor(attributes) {
  344. super(attributes, "sub");
  345. }
  346. }
  347. class Sup extends XhtmlObject {
  348. constructor(attributes) {
  349. super(attributes, "sup");
  350. }
  351. }
  352. class Ul extends XhtmlObject {
  353. constructor(attributes) {
  354. super(attributes, "ul");
  355. }
  356. }
  357. class XhtmlNamespace {
  358. static [_namespaces.$buildXFAObject](name, attributes) {
  359. if (XhtmlNamespace.hasOwnProperty(name)) {
  360. return XhtmlNamespace[name](attributes);
  361. }
  362. return undefined;
  363. }
  364. static a(attributes) {
  365. return new A(attributes);
  366. }
  367. static b(attributes) {
  368. return new B(attributes);
  369. }
  370. static body(attributes) {
  371. return new Body(attributes);
  372. }
  373. static br(attributes) {
  374. return new Br(attributes);
  375. }
  376. static html(attributes) {
  377. return new Html(attributes);
  378. }
  379. static i(attributes) {
  380. return new I(attributes);
  381. }
  382. static li(attributes) {
  383. return new Li(attributes);
  384. }
  385. static ol(attributes) {
  386. return new Ol(attributes);
  387. }
  388. static p(attributes) {
  389. return new P(attributes);
  390. }
  391. static span(attributes) {
  392. return new Span(attributes);
  393. }
  394. static sub(attributes) {
  395. return new Sub(attributes);
  396. }
  397. static sup(attributes) {
  398. return new Sup(attributes);
  399. }
  400. static ul(attributes) {
  401. return new Ul(attributes);
  402. }
  403. }
  404. exports.XhtmlNamespace = XhtmlNamespace;