1 /** MathML. 2 Copyright: Per Nordlöw 2022-. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: $(WEB Per Nordlöw) 5 */ 6 module nxt.mathml; 7 8 import std.traits : isScalarType, isFloatingPoint; 9 10 import nxt.rational : Rational; /+ TODO: Can we turn this dep into a duck type dep? +/ 11 12 /** Markup language. */ 13 enum MarkupLanguage { 14 unknown, nullValue = unknown, // `HybridHashMap` null support 15 HTML, 16 MathML 17 } 18 19 /** Horizontal Alignment. */ 20 enum HAlign { 21 unknown, nullValue = unknown, // `HybridHashMap` null support 22 left, ///< Left aligned. 23 center, ///< Center aligned. 24 right ///< Right aligned. 25 } 26 27 /** Vertical Alignment. */ 28 enum VAlign { 29 unknown, 30 top, ///< Top aligned. 31 middle, ///< Middle aligned. 32 bottom, ///< Bottom aligned. 33 } 34 35 /** Generic case. */ 36 string toMathML(T)(in T x) @trusted /* pure nothrow */ 37 if (isScalarType!T && 38 !isFloatingPoint!T) 39 { 40 import std.conv : to; 41 return to!string(x); 42 } 43 44 /** Returns: x in $(D MarkupLanguage) format. 45 * 46 * See_Also: http://forum.dlang.org/thread/awkynfizwqjnbilgddbh@forum.dlang.org#post-awkynfizwqjnbilgddbh:40forum.dlang.org 47 * See_Also: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mn 48 * See_Also: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msup 49 */ 50 string toML(T)(in T x, 51 bool usePowPlus = false, 52 bool useLeadZeros = false, 53 MarkupLanguage mlang = MarkupLanguage.HTML) @trusted /* pure nothrow */ 54 if (isFloatingPoint!T) 55 { 56 import std.conv : to; 57 import nxt.algorithm.searching : findSplitAmong; 58 const parts = to!string(x).findSplitAmong!('e'); /+ TODO: Use std.bitmanip.FloatRep instead +/ 59 if (parts[2].length >= 1) 60 { 61 // mantissa 62 const mant = parts[0]; 63 64 /+ TODO: These format fixes for the exponent are not needed if we use +/ 65 // std.bitmanip.FloatRep instead 66 67 // exponent 68 auto exp = ((!usePowPlus && 69 parts[2][0] == '+') ? // if leading plus 70 parts[2][1..$] : // skip plus 71 parts[2]); // otherwise whole 72 import nxt.algorithm_ex : dropWhile; 73 auto zexp = useLeadZeros ? exp : exp.dropWhile('0'); 74 75 final switch (mlang) 76 { 77 case MarkupLanguage.unknown: 78 case MarkupLanguage.HTML: 79 return (mant ~ `·` ~ `10` ~ `<msup>` ~ zexp ~ `</msup>`); 80 case MarkupLanguage.MathML: 81 return (`<math>` ~ mant ~ `·` ~ 82 `<msup>` ~ 83 `<mn>10</mn>` ~ 84 `<mn mathsize="80%">` ~ zexp ~ `</mn>` ~ 85 `</msup>` ~ 86 `</math>`); 87 } 88 /* NOTE: This doesn't work in Firefox. Why? */ 89 /* return (`<math>` ~ parts[0] ~ `·` ~ */ 90 /* `<apply><power/>` ~ */ 91 /* `<ci>10</ci>` ~ */ 92 /* `<cn>` ~ parts[2] ~ `</cn>` */ 93 /* `</apply>` ~ */ 94 /* `</math>`); */ 95 } 96 else 97 { 98 return parts[0]; 99 } 100 } 101 102 auto toMathML(T)(in T x, 103 bool usePowPlus = false, 104 bool useLeadZeros = false) @trusted /* pure nothrow */ if (isFloatingPoint!T) 105 => toML(x, usePowPlus, useLeadZeros, MarkupLanguage.MathML); 106 107 auto toHTML(T)(in T x, 108 bool usePowPlus = false, 109 bool useLeadZeros = false) @trusted /* pure nothrow */ if (isFloatingPoint!T) 110 => toML(x, usePowPlus, useLeadZeros, MarkupLanguage.HTML); 111 112 /** Returns: MathML Representation of $(D x). 113 * 114 * See_Also: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfrac 115 */ 116 string toMathML(T)(in Rational!T x, 117 bool bevelled = false, 118 HAlign numAlign = HAlign.center, 119 HAlign denomAlign = HAlign.center, 120 in string href = null) @safe pure 121 { 122 import std.conv : to; 123 return (`<math><mfrac` ~ 124 (bevelled ? ` bevelled="true"` : ``) ~ 125 (numAlign != HAlign.center ? ` numAlign="` ~ to!string(numAlign) ~ `"` : ``) ~ 126 (denomAlign != HAlign.center ? ` denomAlign="` ~ to!string(denomAlign) ~ `"` : ``) ~ 127 `><mi>` 128 ~ to!string(x.numerator) ~ `</mi><mi>` ~ 129 to!string(x.denominator) ~ 130 `</mi></mfrac></math>`); 131 } 132 133 unittest { 134 alias Q = Rational; 135 auto x = Q!ulong(11, 22); 136 // import nxt.debugio : dbg; 137 /** dbg(x.toMathML); */ 138 /** dbg(x.toMathML(true)); */ 139 /** dbg(x.toMathML(true, HAlign.left, HAlign.left)); */ 140 }