Fix multiple problems with indentshift and indentalign in HTML-CSS and SVG output. (Negative values not handled properly, shift not applied to centering, SVG not handling shift past edges well, percentage shifts now in relation to container, etc.) Resolves issues #769 and #768.

This commit is contained in:
Davide P. Cervone 2014-09-09 10:28:25 -04:00
parent 82d0ea22c2
commit ea42f427f6
8 changed files with 111 additions and 84 deletions

View File

@ -169,7 +169,9 @@ MathJax.Hub.Config({
// These two parameters control the alignment and shifting of displayed equations.
// The first can be "left", "center", or "right", and determines the alignment of
// displayed equations. When the alignment is not "center", the second determines
// an indentation from the left or right side for the displayed equations.
// an indentation from the left or right side for the displayed equations. When
// the alignment is "center", the indent allows you to shift the center to the right
// or left (negative is left).
//
displayAlign: "center",
displayIndent: "0em",

View File

@ -421,28 +421,30 @@ MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
// Place the labels, if any
//
if (C[LABEL]) {
var mw = stack.bbox.w, dw;
var mw = stack.bbox.w;
var indent = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift");
if (indent.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {indent.indentalign = indent.indentalignfirst}
if (indent.indentalign === MML.INDENTALIGN.AUTO) {indent.indentalign = this.displayAlign}
if (indent.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {indent.indentshift = indent.indentshiftfirst}
if (indent.indentshift === "auto") {indent.indentshift = this.displayIndent}
if (indent.indentshift === "auto") {indent.indentshift = "0"}
var shift = HTMLCSS.length2em(indent.indentshift,mu,HTMLCSS.cwidth);
var labelshift = HTMLCSS.length2em(values.minlabelspacing,mu,HTMLCSS.cwidth);
if (this.displayIndent !== "0") {
var indent = HTMLCSS.length2em(this.displayIndent,mu,HTMLCSS.cwidth);
shift += (indent.indentAlign === MML.INDENTALIGN.RIGHT ? -indent: indent);
}
var eqn = HTMLCSS.createStack(span,false,"100%");
HTMLCSS.addBox(eqn,stack); HTMLCSS.alignBox(stack,indent.indentalign,0);
if (indent.indentshift && indent.indentalign !== MML.INDENTALIGN.CENTER) {
dw = HTMLCSS.length2em(indent.indentshift,mu); mw += dw;
stack.style[indent.indentalign] = HTMLCSS.Em(dw);
}
HTMLCSS.addBox(eqn,stack); HTMLCSS.alignBox(stack,indent.indentalign,0,shift);
C[LABEL].parentNode.parentNode.removeChild(C[LABEL].parentNode);
HTMLCSS.addBox(eqn,C[LABEL]); HTMLCSS.alignBox(C[LABEL],CALIGN[LABEL],0);
if (HTMLCSS.msieRelativeWidthBug) {stack.style.top = C[LABEL].style.top = ""}
if (hasRelativeWidth) {stack.style.width = values.width; span.bbox.width = "100%"}
dw = HTMLCSS.length2em(values.minlabelspacing,mu);
C[LABEL].style.marginRight = C[LABEL].style.marginLeft = HTMLCSS.Em(dw);
if (indent.indentalign === MML.INDENTALIGN.CENTER) {mw += 4*dw + 2*C[LABEL].bbox.w}
else if (indent.indentalign !== CALIGN[LABEL]) {mw += 2*dw + C[LABEL].bbox.w}
C[LABEL].style.marginRight = C[LABEL].style.marginLeft = HTMLCSS.Em(labelshift);
if (indent.indentalign === MML.INDENTALIGN.CENTER) {mw += 4*labelshift + 2*C[LABEL].bbox.w}
else if (indent.indentalign !== CALIGN[LABEL]) {mw += 2*labelshift + C[LABEL].bbox.w}
span.style.minWidth = span.bbox.minWidth =
eqn.style.minWidth = eqn.bbox.minWidth = HTMLCSS.Em(mw);
eqn.style.minWidth = eqn.bbox.minWidth = HTMLCSS.Em(Math.max(0,mw+shift));
}
//
// Finish the table

View File

@ -209,13 +209,6 @@ MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
var align = this.HTMLgetAlign(state,values),
shift = this.HTMLgetShift(state,values,align);
//
// Add in space for the shift
//
if (shift) {
HTMLCSS.createBlank(line,shift,(align !== MML.INDENTALIGN.RIGHT));
line.bbox.w += shift; line.bbox.rw += shift;
}
//
// Set the Y offset based on previous depth, leading, and current height
//
if (state.n > 0) {
@ -226,7 +219,7 @@ MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
//
// Place the new line
//
HTMLCSS.alignBox(line,align,state.Y);
HTMLCSS.alignBox(line,align,state.Y,shift);
//
// Save the values needed for the future
//
@ -247,14 +240,18 @@ MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
return align;
},
HTMLgetShift: function (state,values,align) {
if (align === MML.INDENTALIGN.CENTER) {return 0}
var cur = values, prev = state.values, def = state.VALUES, shift;
if (state.n === 0) {shift = cur.indentshiftfirst || prev.indentshiftfirst || def.indentshiftfirst}
else if (state.isLast) {shift = prev.indentshiftlast || def.indentshiftlast}
else {shift = prev.indentshift || def.indentshift}
if (shift === MML.INDENTSHIFT.INDENTSHIFT) {shift = prev.indentshift || def.indentshift}
if (shift === "auto" || shift === "") {shift = (state.isTSop ? this.displayIndent : "0")}
return HTMLCSS.length2em(shift,0);
if (shift === "auto" || shift === "") {shift = "0"}
shift = HTMLCSS.length2em(shift,1,HTMLCSS.cwidth);
if (state.isTop && this.displayIndent !== "0") {
var indent = HTMLCSS.length2em(this.displayIndent,1,HTMLCSS.cwidth);
shift += (align === MML.INDENTALIGN.RIGHT ? -indent : indent);
}
return shift;
},
/****************************************************************/
@ -692,7 +689,7 @@ MathJax.Hub.Register.StartupHook("HTML-CSS Jax Ready",function () {
//
if (penalty >= info.penalty) {return false}
info.penalty = penalty; info.values = values; info.W = W; info.w = w;
values.lineleading = HTMLCSS.length2em(values.lineleading,state.VALUES.lineleading);
values.lineleading = HTMLCSS.length2em(values.lineleading,1,state.VALUES.lineleading);
values.id = this.spanID;
return true;
}

View File

@ -543,7 +543,7 @@
preTranslate: function (state) {
var scripts = state.jax[this.id], i, m = scripts.length,
script, prev, span, div, test, jax, ex, em, scale, maxwidth, relwidth = false,
script, prev, span, div, test, jax, ex, em, scale, maxwidth, relwidth = false, cwidth,
linebreak = this.config.linebreaks.automatic, width = this.config.linebreaks.width;
if (linebreak) {
relwidth = (width.match(/^\s*(\d+(\.\d*)?%\s*)?container\s*$/) != null);
@ -593,7 +593,7 @@
// Add the test span for determining scales and linebreak widths
//
script.parentNode.insertBefore(this.EmExSpan.cloneNode(true),script);
if (relwidth) {div.parentNode.insertBefore(this.linebreakSpan.cloneNode(true),div)}
div.parentNode.insertBefore(this.linebreakSpan.cloneNode(true),div)
}
//
// Determine the scaling factors for each script
@ -605,19 +605,21 @@
jax = script.MathJax.elementJax; if (!jax) continue;
ex = test.firstChild.offsetHeight/60;
em = test.lastChild.firstChild.offsetHeight/60;
if (relwidth) {maxwidth = div.previousSibling.firstChild.offsetWidth}
cwidth = div.previousSibling.firstChild.offsetWidth;
if (relwidth) {maxwidth = cwidth}
if (ex === 0 || ex === "NaN") {
// can't read width, so move to hidden div for processing
// (this will cause a reflow for each math element that is hidden)
this.hiddenDiv.appendChild(div);
jax.HTMLCSS.isHidden = true;
ex = this.defaultEx; em = this.defaultEm;
if (relwidth) {maxwidth = this.defaultWidth}
ex = this.defaultEx; em = this.defaultEm; cwidth = this.defaultWidth;
if (relwidth) {maxwidth = cwidth}
}
scale = (this.config.matchFontHeight ? ex/this.TeX.x_height/em : 1);
scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale)*this.config.scale);
jax.HTMLCSS.scale = scale/100; jax.HTMLCSS.fontSize = scale+"%";
jax.HTMLCSS.em = jax.HTMLCSS.outerEm = em; this.em = em * scale/100; jax.HTMLCSS.ex = ex;
jax.HTMLCSS.cwidth = cwidth/this.em;
jax.HTMLCSS.lineWidth = (linebreak ? this.length2em(width,1,maxwidth/this.em) : 1000000);
}
//
@ -627,11 +629,9 @@
script = scripts[i]; if (!script.parentNode) continue;
test = scripts[i].previousSibling;
jax = scripts[i].MathJax.elementJax; if (!jax) continue;
if (relwidth) {
span = test.previousSibling;
if (!jax.HTMLCSS.isHidden) {span = span.previousSibling}
span.parentNode.removeChild(span);
}
span = test.previousSibling;
if (!jax.HTMLCSS.isHidden) {span = span.previousSibling}
span.parentNode.removeChild(span);
test.parentNode.removeChild(test);
}
//
@ -665,6 +665,7 @@
//
this.em = MML.mbase.prototype.em = jax.HTMLCSS.em * jax.HTMLCSS.scale;
this.outerEm = jax.HTMLCSS.em; this.scale = jax.HTMLCSS.scale;
this.cwidth = jax.HTMLCSS.cwidth;
this.linebreakWidth = jax.HTMLCSS.lineWidth;
if (this.scale !== 1) {span.style.fontSize = jax.HTMLCSS.fontSize}
//
@ -1029,6 +1030,7 @@
isMathJax: true,
style: {display:"inline-block", overflow:"hidden", height:"1px", width:this.Em(w)}
});
if (w < 0) {blank.style.marginRight = blank.style.width; blank.style.width = 0}
if (before) {span.insertBefore(blank,span.firstChild)} else {span.appendChild(blank)}
return blank;
},
@ -1203,8 +1205,9 @@
}
}
},
alignBox: function (span,align,y) {
this.placeBox(span,0,y); // set y position (and left aligned)
alignBox: function (span,align,y,dx) {
if (dx == null) {dx = 0}
this.placeBox(span,dx,y); // set y position (and left aligned)
if (this.msiePlaceBoxBug) {
//
// placeBox() adds an extra &nbsp;, so remove it here.
@ -1215,12 +1218,15 @@
}
var bbox = span.bbox; if (bbox.isMultiline) return;
var isRelative = bbox.width != null && !bbox.isFixed;
var r = 0, c = -bbox.w/2, l = "50%";
var r = 0, c = dx-bbox.w/2, l = "50%";
if (this.initialSkipBug) {r = bbox.w-bbox.rw-.1; c += bbox.lw}
if (this.msieMarginScaleBug) {c = (c*this.em) + "px"} else {c = this.Em(c)}
if (isRelative) {c = ""; l = (50 - parseFloat(bbox.width)/2) + "%"}
if (isRelative) {
c = (dx === 0 ? "" : this.Em(dx));
l = (50 - parseFloat(bbox.width)/2) + "%";
}
HUB.Insert(span.style,({
right: {left:"", right: this.Em(r)},
right: {left:"", right: this.Em(r-dx)},
center: {left:l, marginLeft: c}
})[align]);
},
@ -2812,13 +2818,23 @@
var values = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift");
if (values.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {values.indentalign = values.indentalignfirst}
if (values.indentalign === MML.INDENTALIGN.AUTO) {values.indentalign = this.displayAlign}
node.style.textAlign = values.indentalign;
if (values.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {values.indentshift = values.indentshiftfirst}
if (values.indentshift === "auto") {values.indentshift = this.displayIndent}
if (values.indentshift && values.indentalign !== MML.INDENTALIGN.CENTER) {
span.style[{left:"marginLeft",right:"marginRight"}[values.indentalign]] =
HTMLCSS.Em(HTMLCSS.length2em(values.indentshift));
}
if (values.indentshift === "auto") {values.indentshift = "0"}
var shift = HTMLCSS.length2em(values.indentshift,1,HTMLCSS.cwidth);
if (this.displayIndent !== "0") {
var indent = HTMLCSS.length2em(this.displayIndent,1,HTMLCSS.cwidth);
shift += (values.indentalign === MML.INDENTALIGN.RIGHT ? -indent : indent);
}
node.style.textAlign = values.indentalign;
// ### FIXME: make percentage widths respond to changes in container
if (shift) {
shift *= HTMLCSS.scale * HTMLCSS.em/HTMLCSS.outerEm;
HUB.Insert(span.style,({
left: {marginLeft: HTMLCSS.Em(shift)},
right: {marginLeft: HTMLCSS.Em(Math.max(0,span.bbox.w+shift)), marginRight: HTMLCSS.Em(-shift)},
center: {marginLeft: HTMLCSS.Em(shift), marginRight: HTMLCSS.Em(-shift)}
})[values.indentalign]);
}
}
return span;
},
@ -2862,7 +2878,7 @@
// We also need to wait for the onload handler to run, since the loadComplete
// will call Config and Startup, which need to modify the body.
//
MathJax.Hub.Register.StartupHook("onLoad",function () {
HUB.Register.StartupHook("onLoad",function () {
setTimeout(MathJax.Callback(["loadComplete",HTMLCSS,"jax.js"]),0);
});
});

View File

@ -159,7 +159,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
var math = this; while (math.type !== "math") {math = math.inherit}
var jax = MathJax.Hub.getJaxFor(math.inputID);
this.em = MML.mbase.prototype.em = jax.SVG.em; this.ex = jax.SVG.ex;
this.linebreakWidth = jax.SVG.lineWidth * 1000; this.cwidth = jax.SVG.cwidth;
this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth;
//
// Make a new math element and temporarily move the tooltip to it

View File

@ -41,7 +41,8 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
"align","useHeight","width","side","minlabelspacing");
// Handle relative width as fixed width in relation to container
if (values.width.match(/%$/))
{svg.width = values.width = Math.floor(SVG.cwidth*parseFloat(values.width)/100)+"px"}
{svg.width = values.width = SVG.Em((SVG.cwidth/1000)*(parseFloat(values.width)/100))}
var mu = this.SVGgetMu(svg);
var LABEL = -1;
@ -327,21 +328,18 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
if (indent.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {indent.indentalign = indent.indentalignfirst}
if (indent.indentalign === MML.INDENTALIGN.AUTO) {indent.indentalign = this.displayAlign}
if (indent.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {indent.indentshift = indent.indentshiftfirst}
if (indent.indentshift === "auto") {indent.indentshift = this.displayIndent}
var shift = (indent.indentshift ? SVG.length2em(indent.indentshift,mu) : 0);
var labelshift = SVG.length2em(values.minlabelspacing,mu);
var eqn = svg; svg = this.SVG();
if (indent.indentalign === MML.INDENTALIGN.CENTER) {
svg.w = svg.r = SVG.length2em(SVG.cwidth+"px"); shift = 0; svg.hasIndent = true;
} else if (CALIGN[LABEL] !== indent.indentalign) {
svg.w = svg.r = SVG.length2em(SVG.cwidth+"px") - shift - labelshift;
shift = labelshift = 0;
} else {
svg.w = svg.r = eqn.w + shift;
svg.hasIndent = true;
if (indent.indentshift === "auto" || indent.indentshift === "") {indent.indentshift = "0"}
var shift = SVG.length2em(indent.indentshift,mu,SVG.cwidth);
var labelshift = SVG.length2em(values.minlabelspacing,mu,SVG.cwidth);
if (this.displayIndent !== "0") {
var indent = SVG.length2em(this.displayIndent,mu,SVG.cwidth);
shift += (indent.indentAlign === MML.INDENTALIGN.RIGHT ? -indent: indent);
}
svg.Align(eqn,indent.indentalign,shift,0);
var eqn = svg; svg = this.SVG();
eqn.x = shift; svg.w = svg.r = SVG.cwidth; svg.hasIndent = true;
svg.Align(C[LABEL],CALIGN[LABEL],labelshift,0);
svg.Align(eqn,indent.indentalign,0,0);
svg.w = SVG.cwidth; // in case the equation extends past the right
}
this.SVGsaveData(svg);

View File

@ -91,7 +91,8 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
svg = this.SVG();
if (isTop && parent.type !== "mtd") {
if (SVG.linebreakWidth < SVG.BIGDIMEN) {svg.w = SVG.linebreakWidth}
else {svg.w = SVG.cwidth/SVG.em * 1000}
else {svg.w = SVG.cwidth}
svg.lw = svg.w;
}
var state = {
@ -213,10 +214,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
//
// Add in space for the shift
//
if (shift) {
if (align === MML.INDENTALIGN.LEFT) {line.x = shift} else
if (align === MML.INDENTALIGN.RIGHT) {line.w += shift; line.r = line.w}
}
line.x = shift;
//
// Set the Y offset based on previous depth, leading, and current height
//
@ -229,6 +227,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
// Place the new line
//
svg.Align(line,align,0,state.Y);
svg.w = svg.lw; // in case a line extends past the right
//
// Save the values needed for the future
//
@ -249,14 +248,18 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
return align;
},
SVGgetShift: function (state,values,align) {
if (align === MML.INDENTALIGN.CENTER) {return 0}
var cur = values, prev = state.values, def = state.VALUES, shift;
if (state.n === 0) {shift = cur.indentshiftfirst || prev.indentshiftfirst || def.indentshiftfirst}
else if (state.isLast) {shift = prev.indentshiftlast || def.indentshiftlast}
else {shift = prev.indentshift || def.indentshift}
if (shift === MML.INDENTSHIFT.INDENTSHIFT) {shift = prev.indentshift || def.indentshift}
if (shift === "auto" || shift === "") {shift = (state.isTSop ? this.displayIndent : "0")}
return SVG.length2em(shift,0);
if (shift === "auto" || shift === "") {shift = "0"}
shift = SVG.length2em(shift,1,SVG.cwidth);
if (state.isTop && this.displayIndent !== "0") {
var indent = SVG.length2em(this.displayIndent,1,SVG.cwidth);
shift += (align === MML.INDENTALIGN.RIGHT ? -indent: indent);
}
return shift;
},
/****************************************************************/
@ -617,7 +620,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
//
if (penalty >= info.penalty) {return false}
info.penalty = penalty; info.values = values; info.W = W; info.w = w;
values.lineleading = SVG.length2em(values.lineleading,state.VALUES.lineleading);
values.lineleading = SVG.length2em(values.lineleading,1,state.VALUES.lineleading);
values.last = this;
return true;
}

View File

@ -257,11 +257,12 @@
this.hiddenDiv.appendChild(div);
jax.SVG.isHidden = true;
ex = this.defaultEx; cwidth = this.defaultWidth;
if (relwidth) {maxwidth = this.defaultWidth}
if (relwidth) {maxwidth = cwidth}
}
jax.SVG.ex = ex; jax.SVG.cwidth = cwidth;
jax.SVG.ex = ex;
jax.SVG.em = em = ex / SVG.TeX.x_height * 1000; // scale ex to x_height
jax.SVG.lineWidth = (linebreak ? this.length2em(width,1,maxwidth/em) : 1000000);
jax.SVG.cwidth = cwidth/em * 1000;
jax.SVG.lineWidth = (linebreak ? this.length2em(width,1,maxwidth/em*1000) : SVG.BIGDIMEN);
}
//
// Remove the test spans used for determining scales and linebreak widths
@ -305,7 +306,7 @@
// Set the font metrics
//
this.em = MML.mbase.prototype.em = jax.SVG.em; this.ex = jax.SVG.ex;
this.linebreakWidth = jax.SVG.lineWidth * 1000; this.cwidth = jax.SVG.cwidth;
this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth;
//
// Typeset the math
//
@ -431,7 +432,7 @@
var emex = span.appendChild(this.ExSpan.cloneNode(true));
var ex = emex.firstChild.offsetHeight/60;
this.em = MML.mbase.prototype.em = ex / SVG.TeX.x_height * 1000;
this.cwidth = .85*SVG.defaultWidth;
this.cwidth = .85*SVG.defaultWidth/this.em * 1000;
emex.parentNode.removeChild(emex);
span.appendChild(this.textSVG);
@ -2044,7 +2045,7 @@
style.marginLeft = SVG.Ex(-l); style.marginRight = SVG.Ex(-r);
svg.element.setAttribute("viewBox",SVG.Fixed(-l,1)+" "+SVG.Fixed(-svg.H-SVG.em,1)+" "+
SVG.Fixed(l+svg.w+r,1)+" "+SVG.Fixed(svg.H+svg.D+2*SVG.em,1));
svg.element.style.margin="1px 0px"; // 1px above and below to prevent lines from touching
style.marginTop = style.marginBottom = "1px"; // 1px above and below to prevent lines from touching
//
// If there is extra height or depth, hide that
//
@ -2060,17 +2061,25 @@
//
// Handle indentalign and indentshift for single-line displays
//
if (!this.isMultiline && this.Get("display") === "block") {
if (!this.isMultiline && this.Get("display") === "block" && !svg.hasIndent) {
var values = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift");
if (values.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {values.indentalign = values.indentalignfirst}
if (values.indentalign === MML.INDENTALIGN.AUTO) {values.indentalign = this.displayAlign}
div.style.textAlign = values.indentalign;
if (values.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {values.indentshift = values.indentshiftfirst}
if (values.indentshift === "auto") {values.indentshift = this.displayIndent}
if (values.indentshift && values.indentalign !== MML.INDENTALIGN.CENTER && !svg.hasIndent) {
span.style[{left:"marginLeft",right:"marginRight"}[values.indentalign]] =
SVG.Ex(SVG.length2em(values.indentshift));
}
if (values.indentshift === "auto") {values.indentshift = "0"}
var shift = SVG.length2em(values.indentshift,1,SVG.cwidth);
if (this.displayIndent !== "0") {
var indent = SVG.length2em(this.displayIndent,1,SVG.cwidth);
shift += (values.indentalign === MML.INDENTALIGN.RIGHT ? -indent : indent);
}
div.style.textAlign = values.indentalign;
if (shift) {
HUB.Insert(style,({
left: {marginLeft: SVG.Ex(shift)},
right: {marginRight: SVG.Ex(-shift)},
center: {marginLeft: SVG.Ex(shift), marginRight: SVG.Ex(-shift)}
})[values.indentalign]);
}
}
}
return span;