@@ -9,16 +9,25 @@ pre- and post-filters associated with MathJax's input and output jax.
99These are prioritized lists of functions that run either before or
1010after the jax processes a :data: `MathItem `, and they can be used to
1111pre-process or post-process MathJax's compiling and typesetting
12- functions. Input jax have both pre- and post-filters, but output jax
13- have only post-filters; pre-filtering can be done by an input jax
14- post-filter, if needed .
12+ functions. Input and output jax have both pre- and post-filters, and
13+ the MathML input jax has an extra set of filters for the parsed MathML
14+ as well .
1515
16- To add a pre- or post-filter to an input jax use
16+ When using :ref: `Mathjax Components framework <web-components >`, you
17+ can use the MathJax configuration object to specify input and output
18+ jax filters. The :data: `preFilter ` and :data: `postFilter `
19+ configuration options in the :data: `tex `, :data: `mathml `,
20+ :data: `output `, :data: `chtml `, or :data: `svg ` blocks allow you to
21+ specify arrays of filters (or filters together with their priorities).
22+ See the :ref: `configuring-mathjax ` section for details.
23+
24+ When using direct access to the MathJax modules in node applications,
25+ to add a pre- or post-filter to an input jax use
1726
1827.. js :function :: InputJax .preFilters .add (fn, priority)
1928 InputJax .postFilters .add (fn, priority)
2029
21- :param (arg) => boolean|void: The filter function to be called.
30+ :param (arg)=> boolean|void: The filter function to be called.
2231 The :data: `arg ` argument is an object
2332 with three keys: :data: `math `,
2433 :data: `document `, and :data: `data `.
@@ -35,7 +44,8 @@ To add a pre- or post-filter to an input jax use
3544 functions anywhere in the filter list.
3645
3746For the TeX input jax, the :data: `data ` item is the
38- :data: `ParseOptions ` object for the input jax.
47+ :data: `ParseOptions ` object for the input jax, which holds
48+ configuration data about the TeX input jax.
3949
4050For the MathML input jax, the pre-filter only runs in the case that
4151the MathML is a serialized MathML string, as it is when converting a
@@ -54,14 +64,15 @@ input jax converts the MathML into MathJax's internal format. The
5464The AsciiMath input jax does not currently execute any pre- or
5565post-filters.
5666
57- For an output jax, the post-filters can be added via
67+ For an output jax, the pre- and post-filters can be added via
5868
59- .. js :function :: OutputJax .postFilters .add (fn, priority)
69+ .. js :function :: OutputJax .preFilters .add (fn, priority)
70+ OutputJax .postFilters .add (fn, priority)
6071
6172with arguments as above. In this case, the :data: `data ` is the
6273``mjx-container `` node in which the output DOM elements have been
6374placed. This will become the :data: `MathItem.typesetRoot ` value, but
64- it has not yet been set when the post- filters run.
75+ it has not yet been set when the filters run.
6576
6677In an application that is using MathJax Components, the input jax can
6778be obtained from :data: `MathJax.startup.document.inputJax.tex ` or
@@ -74,6 +85,7 @@ to them; if not, then they can be obtained from the
7485:js:meth: `mathjax.document() ` by using that in place of
7586:data: `MathJax.startup.document ` above.
7687
88+
7789-----
7890
7991.. _filter-number-space :
@@ -90,20 +102,17 @@ displayed as ``12345``.
90102
91103 MathJax = {
92104 tex: {
93- numberPattern: / ^ (?:[0-9 ] + (?:(?: + | \{ ,\} )[0-9 ] + )* (?:\. [0-9 ] * )? | \. [0-9 ] + )/
94- },
95- startup: {
96- ready () {
97- MathJax .startup .defaultReady ();
98- MathJax .startup .document .inputJax .tex .postFilters .add (({data}) => {
105+ numberPattern: / ^ (?:[0-9 ] + (?:(?: + | \{ ,\} )[0-9 ] + )* (?:\. [0-9 ] * )? | \. [0-9 ] + )/ ,
106+ postFilters: [
107+ ({data}) => {
99108 for (const mn of data .getList (' mn' )) {
100109 const textNode = mn .childNodes [0 ];
101110 textNode .text = textNode .text .replace (/ / g , ' ' );
102111 }
103- });
104- }
105- }
106- }
112+ }
113+ ],
114+ },
115+ };
107116
108117 We set the :data: `numberPattern ` option to allow spaces within the
109118number, and then use a post-filter to remove the spaces from the text
@@ -123,18 +132,15 @@ to better quality output.
123132.. code-block :: js
124133
125134 MathJax = {
126- startup: {
127- ready () {
128- MathJax .startup .defaultReady ();
129- MathJax .startup .document .inputJax .tex .preFilters .add (
130- ({math}) => {
131- math .math = math .math .replace (/ [\uFF01 -\uFF5E ] / g ,
132- (c ) => String .fromCodePoint (c .codePointAt (0 ) - 0xFF00 + 0x20 ));
133- }
134- );
135- }
135+ tex: {
136+ preFilters: [
137+ ({math}) => {
138+ math .math = math .math .replace (/ [\uFF01 -\uFF5E ] / g ,
139+ (c ) => String .fromCodePoint (c .codePointAt (0 ) - 0xFF00 + 0x20 ));
140+ }
141+ ]
136142 }
137- }
143+ };
138144
139145 This uses a pre-filter to replace characters in the full-width range
140146by an equivalent one in the usual ASCII character range. This will
@@ -155,31 +161,25 @@ and subscripts.
155161.. code-block :: js
156162
157163 MathJax = {
158- startup: {
159- ready () {
160- //
161- // Do usual setup
162- //
163- MathJax .startup .defaultReady ();
164- //
165- // The pseudoscript numbers 0 through 9, and a pattern for plus-or-minus a number
166- //
167- const scripts = ' \u2070\u00B9\u00B2\u00B3\u2074\u2075\u2076\u2077\u2078\u2079 ' ;
168- const scriptRE = / ([\u207A\u207B ] )? ([\u2070\u00B9\u00B2\u00B3 \u2074 -\u2079 ] + )/ g ;
169- //
170- // Add a TeX prefilter to convert pseudoscript numbers to actual superscripts
171- //
172- MathJax .startup .document .inputJax .tex .preFilters .add (({math}) => {
173- math .math = math .math .replace (scriptRE, (match , pm , n ) => {
174- const N = n .split (' ' ).map (c => scripts .indexOf (c)); // convert digits
164+ //
165+ // The pseudoscript numbers 0 through 9, and a pattern for plus-or-minus a number
166+ //
167+ scripts: ' \u2070\u00B9\u00B2\u00B3\u2074\u2075\u2076\u2077\u2078\u2079 ' ,
168+ scriptRE: / ([\u207A\u207B ] )? ([\u2070\u00B9\u00B2\u00B3 \u2074 -\u2079 ] + )/ g ,
169+
170+ tex: {
171+ preFilters: [
172+ ({math}) => {
173+ math .math = math .math .replace (MathJax .config .scriptRE , (match , pm , n ) => {
174+ const N = n .split (' ' ).map (c => MathJax .config .scripts .indexOf (c)); // convert digits
175175 pm === ' \u207A ' && N .unshift (' +' ); // add plus, if given
176176 pm === ' \u207B ' && N .unshift (' -' ); // add minus, if given
177177 return ' ^{' + N .join (' ' ) + ' }' ; // make it an actual power
178178 });
179- });
180- }
179+ }
180+ ]
181181 }
182- }
182+ };
183183
184184 This uses a TeX input jax pre-filter to scan the TeX expression for
185185Unicode superscript numerals, with optional plus or minus signs, and
@@ -204,22 +204,21 @@ those measurements to `px` units instead.
204204.. code-block :: js
205205
206206 MathJax = {
207- startup: {
208- ready () {
209- MathJax .startup .defaultReady ();
210- const fixed = MathJax .startup .document .outputJax .fixed ;
211- MathJax .startup .document .outputJax .postFilters .add (({data}) => {
207+ svg: {
208+ postFilters: [
209+ ({data}) => {
210+ const fixed = MathJax .startup .document .outputJax .fixed ;
212211 const svg = data .querySelector (' svg' );
213212 if (svg? .hasAttribute (' viewBox' )) {
214213 const [ , , w , h ] = svg .getAttribute (' viewBox' ).split (/ / );
215- const em = document .outputJax .pxPerEm / 1000 ;
214+ const em = MathJax . startup . document .outputJax .pxPerEm / 1000 ;
216215 svg .setAttribute (' width' , fixed (w * em) + ' px' );
217216 svg .setAttribute (' height' , fixed (h * em) + ' px' );
218217 }
219- });
220- }
218+ }
219+ ]
221220 }
222- }
221+ };
223222
224223We use an output jax post- filter to modify the ` ` svg` ` element' s
225224attributes, taking advantage of the output jax' s : meth: ` fixed()`
@@ -239,18 +238,18 @@ This configuration implements a substitute for the v2 `autobold` extension.
239238.. code - block:: js
240239
241240 MathJax = {
242- startup: {
243- ready () {
244- MathJax .startup .defaultReady ();
245- MathJax .startup .document .inputJax .tex .preFilters .add (({math}) => {
241+ tex: {
242+ preFilters: [
243+ ({math}) => {
246244 const styles = window .getComputedStyle (math .start .node .parentNode );
247- if (styles .fontWeight >= 700 ) {
245+ if (styles .fontWeight >= 700 && ! math . inputData . bolded ) {
248246 math .math = ' \\ boldsymbol{' + math .math + ' }' ;
247+ math .inputData .bolded = true ;
249248 }
250- });
251- }
249+ }
250+ ]
252251 }
253- }
252+ };
254253
255254It uses a TeX input jax pre- filter that tests if the parent element of
256255the math string has CSS with ` ` font- weight` ` of 700 or more (the
@@ -259,6 +258,12 @@ usual ``bold`` value), and if so, it wraps the TeX code in
259258expression itself includes bold notation, that does not become extra
260259bold, so may not be distinguishable from the rest of the expression.
261260
261+ We track the fact that bolding has been added using the
262+ : data: ` inputData` object of the : data: ` math` object . That way, if the
263+ expression needs to be reparsed (e .g ., for a ` ` \require` ` command, or
264+ other dynamic data being loaded), we won' t add ``\b oldsymbol`` more
265+ than once.
266+
262267-----
263268
264269.. _filter-mathvariant:
@@ -330,6 +335,23 @@ browsers that implement MathML-Core.
330335 0x6F : 0x2134 ,
331336 }, ' \uFE00 ' ],
332337 ' -tex-bold-calligraphic' : [0 , 0x1D4D0 , 0x1D4EA , 0 , 0 , {}, ' \uFE00 ' ],
338+ ' -tex-mathit' : [0 , 0x1D434 , 0x1D44E , 0x1D6E2 , 0x1D6FC , {0x68 : 0x210E }],
339+ };
340+ //
341+ // Styles to use for characters that can't be translated.
342+ //
343+ const variantStyles = {
344+ bold: ' font-weight: bold' ,
345+ italic: ' font-style: italic' ,
346+ ' bold-italic' : ' font-weight; bold; font-style: italic' ,
347+ ' script' : ' font-family: cursive' ,
348+ ' bold-script' : ' font-family: cursive; font-weight: bold' ,
349+ ' sans-serif' : ' font-family: sans-serif' ,
350+ ' bold-sans-serif' : ' font-family: sans-serif; font-weight: bold' ,
351+ ' sans-serif-italic' : ' font-family: sans-serif; font-style: italic' ,
352+ ' sans-serif-bold-italic' : ' font-family: sans-serif; font-weight: bold; font-style: italic' ,
353+ ' monospace' : ' font-family: monospace' ,
354+ ' -tex-mathit' : ' font-style: italic' ,
333355 };
334356 //
335357 // The filter function
@@ -362,11 +384,20 @@ browsers that implement MathML-Core.
362384 //
363385 // Convert the text of the child nodes
364386 //
387+ let converted = true ;
365388 for (const child of node .childNodes ) {
366389 if (child .isKind (' text' )) {
367- convertText(child, start, remap, modifier);
390+ converted &= convertText (child, start, remap, modifier);
368391 }
369392 }
393+ //
394+ // If not all characters were converted, add styles, if possible,
395+ // but not when it would already be in italics.
396+ //
397+ if (! converted &&
398+ ! ([' italic' , ' -tex-mathit' ].includes (variant) && text .length === 1 && node .isKind (' mi' ))) {
399+ addStyles (node, variant);
400+ }
370401 });
371402 }
372403 //
@@ -380,6 +411,7 @@ browsers that implement MathML-Core.
380411 //
381412 // Loop through the characters in the text
382413 //
414+ let converted = 0 ;
383415 for (let i = 0 ; i < text .length ; i++ ) {
384416 let C = text[i].codePointAt (0 );
385417 //
@@ -395,9 +427,11 @@ browsers that implement MathML-Core.
395427 //
396428 if (map[C ]) {
397429 text[i] = String .fromCodePoint (map[C ] - m + start[j]) + modifier;
430+ converted++ ;
398431 break ;
399432 } else if (remap[C ] || C <= M ) {
400433 text[i] = String .fromCodePoint (remap[C ] || C - m + start[j]) + modifier;
434+ converted++ ;
401435 break ;
402436 }
403437 }
@@ -406,17 +440,34 @@ browsers that implement MathML-Core.
406440 // Put back the modified text content
407441 //
408442 node .setText (text .join (' ' ));
443+ //
444+ // Return true if all characters were converted, false otherwise.
445+ //
446+ return converted === text .length ;
409447 }
410448 //
411- // Add the input post-filters
449+ // Add styles when conversion isn't possible.
450+ //
451+ function addStyles (node , variant ) {
452+ let styles = variantStyles[variant];
453+ if (styles) {
454+ if (node .attributes .hasExplicit (styles)) {
455+ styles = node .attributes .get (' style' ) + ' ' + styles;
456+ }
457+ node .attributes .set (' style' , styles);
458+ }
459+ }
460+
461+ //
462+ // Add the post-filters to all input jax
412463 //
413464 MathJax .startup .defaultReady ();
414465 for (jax of MathJax .startup .document .inputJax ) {
415466 jax .postFilters .add (({data}) => unicodeVariants (data .root || data));
416467 }
417468 }
418469 }
419- }
470+ };
420471
421472This example adds a post- filter to each of the input jax that are
422473loaded (so it will work with both the MathML input as well as TeX
@@ -425,12 +476,16 @@ elements with :attr:`mathvariant` attributes, and then converts the
425476content of the child text nodes of those token nodes to use the proper
426477Unicode values for any alphabetic, numeric, or Greek characters that
427478can be represented using the Mathematical Alphanumeric and Letterlike
428- Symbols blocks.
479+ Symbols blocks . If any characters can' t be converted to something in
480+ these blocks, we use a :attr:`style` attribute, when possible, to
481+ simulate the proper output.
429482
430483The :data:`ranges` variable gives the character ranges that will be
431- converted, and the :data:`variants` object gives the data needed to
432- make those ranges to the various Mathematical Alphanumerics characters
433- for the different :attr:`mathvariant` values.
484+ converted, the :data:`variants` object gives the data needed to make
485+ those ranges to the various Mathematical Alphanumerics characters for
486+ the different :attr:`mathvariant` values, and the
487+ :data:`variantStyles` object to hold the styles that need to be
488+ applied for each variant.
434489
435490The special ``-tex-calligraphic`` and ``-tex-bold-calligraphic``
436491variants are used internally in MathJax to produce the Chancery
@@ -444,7 +499,9 @@ for the TeX calligraphic variants. You may wish to add U+FE01 to the
444499script variants to explicitly request the Roundhand versions as well.
445500Note, however, that not all fonts support these variant specifiers, so
446501you may get the same characters in both cases, and which you get will
447- depend on the font.
502+ depend on the font. Some browsers may also show unknown character
503+ glyphs for these select codes when they don' t understand how to
504+ process them.
448505
449506
450507| ---- - |
0 commit comments