mml3-node.ts 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2021-2022 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Auxiliary function for elementary MathML3 support (experimental)
  19. * using David Carlisle's XLST transform.
  20. *
  21. * @author dpvc@mathjax.org (Davide Cervone)
  22. */
  23. import {MathDocument} from '../../../core/MathDocument.js';
  24. /**
  25. * Create the transform function that uses Saxon-js to perform the
  26. * xslt transformation.
  27. *
  28. * @template N The HTMLElement node class
  29. * @template T The Text node class
  30. * @template D The Document class
  31. *
  32. * @return {(node: N, doc: MathDocument<N,T,D>) => N)} The transformation function
  33. */
  34. export function createTransform<N, T, D>(): (node: N, doc: MathDocument<N, T, D>) => N {
  35. /* tslint:disable-next-line:no-eval */
  36. const nodeRequire = eval('require'); // get the actual require from node.
  37. /* tslint:disable-next-line:no-eval */
  38. const dirname = eval('__dirname'); // get the actual __dirname
  39. try {
  40. nodeRequire.resolve('saxon-js'); // check if saxon-js is installed.
  41. } catch (err) {
  42. throw Error('Saxon-js not found. Run the command:\n npm install saxon-js\nand try again.');
  43. }
  44. const Saxon = nodeRequire('saxon-js'); // dynamically load Saxon-JS.
  45. const path = nodeRequire('path'); // use the real version from node.
  46. const fs = nodeRequire('fs'); // use the real version from node.
  47. const xsltFile = path.resolve(dirname, 'mml3.sef.json'); // load the preprocessed stylesheet.
  48. const xslt = JSON.parse(fs.readFileSync(xsltFile)); // and parse it.
  49. return (node: N, doc: MathDocument<N, T, D>) => {
  50. const adaptor = doc.adaptor;
  51. let mml = adaptor.outerHTML(node);
  52. //
  53. // Make sure the namespace is present
  54. //
  55. if (!mml.match(/ xmlns[=:]/)) {
  56. mml = mml.replace(/<(?:(\w+)(:))?math/, '<$1$2math xmlns$2$1="http://www.w3.org/1998/Math/MathML"');
  57. }
  58. //
  59. // Try to run the transform, and if it fails, return the original MathML
  60. //
  61. let result;
  62. try {
  63. result = adaptor.firstChild(adaptor.body(adaptor.parse(Saxon.transform({
  64. stylesheetInternal: xslt,
  65. sourceText: mml,
  66. destination: 'serialized'
  67. }).principalResult))) as N;
  68. } catch (err) {
  69. result = node;
  70. }
  71. return result;
  72. };
  73. }