1 /** MathML. 2 Copyright: Per Nordlöw 2018-. 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 import nxt.languages : MarkupLang; 12 13 /** Horizontal Alignment. */ 14 enum HAlign 15 { 16 left, ///< Left aligned. 17 center, ///< Center aligned. 18 right ///< Right aligned. 19 } 20 21 /** Vertical Alignment. */ 22 enum VAlign 23 { 24 top, ///< Top aligned. 25 middle, ///< Middle aligned. 26 bottom, ///< Bottom aligned. 27 } 28 29 /** Generic case. */ 30 string toMathML(T)(in T x) @trusted /* pure nothrow */ 31 if (isScalarType!T && 32 !isFloatingPoint!T) 33 { 34 import std.conv : to; 35 return to!string(x); 36 } 37 38 /** Returns: x in $(D MarkupLang) format. 39 * 40 * See_Also: http://forum.dlang.org/thread/awkynfizwqjnbilgddbh@forum.dlang.org#post-awkynfizwqjnbilgddbh:40forum.dlang.org 41 * See_Also: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mn 42 * See_Also: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msup 43 */ 44 string toML(T)(in T x, 45 bool usePowPlus = false, 46 bool useLeadZeros = false, 47 MarkupLang mlang = MarkupLang.HTML) @trusted /* pure nothrow */ 48 if (isFloatingPoint!T) 49 { 50 import std.conv : to; 51 import nxt.find_split_ex : findSplitAmong; 52 const parts = to!string(x).findSplitAmong!('e'); // TODO Use std.bitmanip.FloatRep instead 53 if (parts[2].length >= 1) 54 { 55 // mantissa 56 const mant = parts[0]; 57 58 // TODO These format fixes for the exponent are not needed if we use 59 // std.bitmanip.FloatRep instead 60 61 // exponent 62 auto exp = ((!usePowPlus && 63 parts[2][0] == '+') ? // if leading plus 64 parts[2][1..$] : // skip plus 65 parts[2]); // otherwise whole 66 import nxt.algorithm_ex : dropWhile; 67 auto zexp = useLeadZeros ? exp : exp.dropWhile('0'); 68 69 final switch (mlang) 70 { 71 case MarkupLang.unknown: 72 case MarkupLang.HTML: 73 return (mant ~ `·` ~ `10` ~ `<msup>` ~ zexp ~ `</msup>`); 74 case MarkupLang.MathML: 75 return (`<math>` ~ mant ~ `·` ~ 76 `<msup>` ~ 77 `<mn>10</mn>` ~ 78 `<mn mathsize="80%">` ~ zexp ~ `</mn>` ~ 79 `</msup>` ~ 80 `</math>`); 81 } 82 /* NOTE: This doesn't work in Firefox. Why? */ 83 /* return (`<math>` ~ parts[0] ~ `·` ~ */ 84 /* `<apply><power/>` ~ */ 85 /* `<ci>10</ci>` ~ */ 86 /* `<cn>` ~ parts[2] ~ `</cn>` */ 87 /* `</apply>` ~ */ 88 /* `</math>`); */ 89 } 90 else 91 { 92 return parts[0]; 93 } 94 } 95 96 auto toMathML(T)(in T x, 97 bool usePowPlus = false, 98 bool useLeadZeros = false) @trusted /* pure nothrow */ 99 if (isFloatingPoint!T) 100 { 101 return toML(x, usePowPlus, useLeadZeros, MarkupLang.MathML); 102 } 103 104 auto toHTML(T)(in T x, 105 bool usePowPlus = false, 106 bool useLeadZeros = false) @trusted /* pure nothrow */ 107 if (isFloatingPoint!T) 108 { 109 return toML(x, usePowPlus, useLeadZeros, MarkupLang.HTML); 110 } 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 { 135 alias Q = Rational; 136 auto x = Q!ulong(11, 22); 137 // import nxt.dbgio : dbg; 138 /** dbg(x.toMathML); */ 139 /** dbg(x.toMathML(true)); */ 140 /** dbg(x.toMathML(true, HAlign.left, HAlign.left)); */ 141 }