diff --git a/src/buildTree.js b/src/buildTree.js index cbdf0525d..1939331b3 100644 --- a/src/buildTree.js +++ b/src/buildTree.js @@ -364,14 +364,14 @@ var groupTypes = { [base, supsub]); }, - frac: function(group, options, prev) { + genfrac: function(group, options, prev) { // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e). // Figure out what style this fraction should be in based on the // function used var fstyle = options.style; - if (group.value.size === "dfrac") { + if (group.value.size === "display") { fstyle = Style.DISPLAY; - } else if (group.value.size === "tfrac") { + } else if (group.value.size === "text") { fstyle = Style.TEXT; } @@ -384,60 +384,118 @@ var groupTypes = { var denom = buildGroup(group.value.denom, options.withStyle(dstyle)); var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]); - var ruleWidth = fontMetrics.metrics.defaultRuleThickness / - options.style.sizeMultiplier; + var ruleWidth; + if (group.value.hasBarLine) { + ruleWidth = fontMetrics.metrics.defaultRuleThickness / + options.style.sizeMultiplier; + } else { + ruleWidth = 0; + } - var mid = makeSpan( - [options.style.reset(), Style.TEXT.cls(), "frac-line"]); - // Manually set the height of the line because its height is created in - // CSS - mid.height = ruleWidth; - - // Rule 15b, 15d - var numShift, denomShift, clearance; + // Rule 15b + var numShift; + var clearance; + var denomShift; if (fstyle.size === Style.DISPLAY.size) { numShift = fontMetrics.metrics.num1; + if (ruleWidth > 0) { + clearance = 3 * ruleWidth; + } else { + clearance = 7 * fontMetrics.metrics.defaultRuleThickness; + } denomShift = fontMetrics.metrics.denom1; - clearance = 3 * ruleWidth; } else { - numShift = fontMetrics.metrics.num2; + if (ruleWidth > 0) { + numShift = fontMetrics.metrics.num2; + clearance = ruleWidth; + } else { + numShift = fontMetrics.metrics.num3; + clearance = 3 * fontMetrics.metrics.defaultRuleThickness; + } denomShift = fontMetrics.metrics.denom2; - clearance = ruleWidth; } - var axisHeight = fontMetrics.metrics.axisHeight; + var frac; + if (ruleWidth === 0) { + // Rule 15c + var candiateClearance = + (numShift - numer.depth) - (denom.height - denomShift); + if (candiateClearance < clearance) { + numShift += 0.5 * (clearance - candiateClearance); + denomShift += 0.5 * (clearance - candiateClearance); + } - // Rule 15d - if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) - < clearance) { - numShift += - clearance - ((numShift - numer.depth) - - (axisHeight + 0.5 * ruleWidth)); + frac = buildCommon.makeVList([ + {type: "elem", elem: denomreset, shift: denomShift}, + {type: "elem", elem: numerreset, shift: -numShift} + ], "individualShift", null, options); + } else { + // Rule 15d + var axisHeight = fontMetrics.metrics.axisHeight; + + if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) + < clearance) { + numShift += + clearance - ((numShift - numer.depth) - + (axisHeight + 0.5 * ruleWidth)); + } + + if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift) + < clearance) { + denomShift += + clearance - ((axisHeight - 0.5 * ruleWidth) - + (denom.height - denomShift)); + } + + var mid = makeSpan( + [options.style.reset(), Style.TEXT.cls(), "frac-line"]); + // Manually set the height of the line because its height is + // created in CSS + mid.height = ruleWidth; + + var midShift = -(axisHeight - 0.5 * ruleWidth); + + frac = buildCommon.makeVList([ + {type: "elem", elem: denomreset, shift: denomShift}, + {type: "elem", elem: mid, shift: midShift}, + {type: "elem", elem: numerreset, shift: -numShift} + ], "individualShift", null, options); } - if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift) - < clearance) { - denomShift += - clearance - ((axisHeight - 0.5 * ruleWidth) - - (denom.height - denomShift)); - } - - var midShift = -(axisHeight - 0.5 * ruleWidth); - - var frac = buildCommon.makeVList([ - {type: "elem", elem: denomreset, shift: denomShift}, - {type: "elem", elem: mid, shift: midShift}, - {type: "elem", elem: numerreset, shift: -numShift} - ], "individualShift", null, options); - // Since we manually change the style sometimes (with \dfrac or \tfrac), // account for the possible size change here. frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier; frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier; + // Rule 15e + var innerChildren = [makeSpan(["mfrac"], [frac])]; + + var delimSize; + if (fstyle.size === Style.DISPLAY.size) { + delimSize = fontMetrics.metrics.delim1; + } else { + delimSize = fontMetrics.metrics.getDelim2(fstyle); + } + + if (group.value.leftDelim != null) { + innerChildren.unshift( + delimiter.customSizedDelim( + group.value.leftDelim, delimSize, true, + options.withStyle(fstyle), group.mode) + ); + } + if (group.value.rightDelim != null) { + innerChildren.push( + delimiter.customSizedDelim( + group.value.rightDelim, delimSize, true, + options.withStyle(fstyle), group.mode) + ); + } + return makeSpan( - ["minner", "mfrac", options.style.reset(), fstyle.cls()], - [frac], options.getColor()); + ["minner", options.style.reset(), fstyle.cls()], + innerChildren, + options.getColor()); }, spacing: function(group, options, prev) { diff --git a/src/fontMetrics.js b/src/fontMetrics.js index 3d6bbae13..04423635c 100644 --- a/src/fontMetrics.js +++ b/src/fontMetrics.js @@ -1,5 +1,7 @@ /* jshint unused:false */ +var Style = require("./Style"); + /** * This file contains metrics regarding fonts and individual symbols. The sigma * and xi variables, as well as the metricMap map contain data extracted from @@ -35,7 +37,9 @@ var sigma17 = 0.247; var sigma18 = 0.386; var sigma19 = 0.050; var sigma20 = 2.390; -var sigma21 = 0.101; +var sigma21 = 1.01; +var sigma21Script = 0.81; +var sigma21ScriptScript = 0.71; var sigma22 = 0.250; // These font metrics are extracted from TeX by using @@ -81,8 +85,6 @@ var metrics = { sub2: sigma17, supDrop: sigma18, subDrop: sigma19, - delim1: sigma20, - delim2: sigma21, axisHeight: sigma22, defaultRuleThickness: xi8, bigOpSpacing1: xi9, @@ -90,7 +92,21 @@ var metrics = { bigOpSpacing3: xi11, bigOpSpacing4: xi12, bigOpSpacing5: xi13, - ptPerEm: ptPerEm + ptPerEm: ptPerEm, + + // TODO(alpert): Missing parallel structure here. We should probably add + // style-specific metrics for all of these. + delim1: sigma20, + getDelim2: function(style) { + if (style.size === Style.TEXT.size) { + return sigma21; + } else if (style.size === Style.SCRIPT.size) { + return sigma21Script; + } else if (style.size === Style.SCRIPTSCRIPT.size) { + return sigma21ScriptScript; + } + throw new Error("Unexpected style size: " + style.size); + } }; // This map contains a mapping from font name and character code to character diff --git a/src/functions.js b/src/functions.js index d51d38b4d..0d4992130 100644 --- a/src/functions.js +++ b/src/functions.js @@ -329,16 +329,55 @@ var duplicatedFunctions = [ // Fractions { - funcs: ["\\dfrac", "\\frac", "\\tfrac"], + funcs: [ + "\\dfrac", "\\frac", "\\tfrac", + "\\dbinom", "\\binom", "\\tbinom" + ], data: { numArgs: 2, greediness: 2, handler: function(func, numer, denom) { + var hasBarLine; + var leftDelim = null; + var rightDelim = null; + var size = "auto"; + + switch (func) { + case "\\dfrac": + case "\\frac": + case "\\tfrac": + hasBarLine = true; + break; + case "\\dbinom": + case "\\binom": + case "\\tbinom": + hasBarLine = false; + leftDelim = "("; + rightDelim = ")"; + break; + default: + throw new Error("Unrecognized genfrac command"); + } + + switch (func) { + case "\\dfrac": + case "\\dbinom": + size = "display"; + break; + case "\\tfrac": + case "\\tbinom": + size = "text"; + break; + } + return { - type: "frac", + type: "genfrac", numer: numer, denom: denom, - size: func.slice(1) + hasBarLine: hasBarLine, + leftDelim: leftDelim, + rightDelim: rightDelim, + size: size }; } } diff --git a/test/huxley/Huxleyfile.json b/test/huxley/Huxleyfile.json index 81a8a8c35..673b58927 100644 --- a/test/huxley/Huxleyfile.json +++ b/test/huxley/Huxleyfile.json @@ -11,6 +11,12 @@ "url": "http://localhost:7936/test/huxley/test.html?m=\\dfrac{a}{b}\\frac{a}{b}\\tfrac{a}{b}" }, + { + "name": "BinomTest", + "screenSize": [1024, 768], + "url": "http://localhost:7936/test/huxley/test.html?m=\\dbinom{a}{b}\\tbinom{a}{b}^{\\binom{a}{b}+17}" + }, + { "name": "NestedFractions", "screenSize": [1024, 768], diff --git a/test/huxley/Huxleyfolder/BinomTest.hux/firefox-1.png b/test/huxley/Huxleyfolder/BinomTest.hux/firefox-1.png new file mode 100644 index 000000000..2f45dc3c1 Binary files /dev/null and b/test/huxley/Huxleyfolder/BinomTest.hux/firefox-1.png differ diff --git a/test/huxley/Huxleyfolder/BinomTest.record.json b/test/huxley/Huxleyfolder/BinomTest.record.json new file mode 100644 index 000000000..3cae6ac65 --- /dev/null +++ b/test/huxley/Huxleyfolder/BinomTest.record.json @@ -0,0 +1,5 @@ +[ + { + "action": "screenshot" + } +]