Upgrade \sqrt zoom and width (#890)

* Fix \sqrt zoom in Safari

This PR evades a Safari bug which causes nested SVGs to zoom improperly. `\sqrt` and single-ended arrow SVGs have been modified.

These have been converted from nested SVGs to single level. Their long tails are now sliced off using CSS `overflow: hidden`.

Safari will still improperly zoom any double-ended stretchy arrows and horizontal braces.

* Fix \sqrt{}

Even if the function argument is empty, still render an SVG whose width equals the surd glyph.

* Fix tall \sqrt when scaled

* update screenshots affected by sqrt fixes

* more screenshots after changes to fix sqrt

* Pick up review comments
This commit is contained in:
Ron Kok
2017-09-18 16:24:20 -07:00
committed by Kevin Barabash
parent 6e75ebdc2d
commit fd45669f7c
23 changed files with 46 additions and 33 deletions

View File

@@ -1115,6 +1115,10 @@ groupTypes.sqrt = function(group, options) {
// First, we do the same steps as in overline to build the inner group
// and line
let inner = buildGroup(group.value.body, options.havingCrampedStyle());
if (inner.height === 0) {
// Render a small surd.
inner.height = options.fontMetrics().xHeight;
}
// Some groups can return document fragments. Handle those by wrapping
// them in a span.
@@ -1159,26 +1163,16 @@ groupTypes.sqrt = function(group, options) {
// Shift the sqrt image
const imgShift = img.height - inner.height - lineClearance - ruleWidth;
// We add a special case here, because even when `inner` is empty, we
// still get a line. So, we use a simple heuristic to decide if we
// should omit the body entirely. (note this doesn't work for something
// like `\sqrt{\rlap{x}}`, but if someone is doing that they deserve for
// it not to work.
let body;
if (inner.height === 0 && inner.depth === 0) {
body = makeSpan();
} else {
inner.style.paddingLeft = img.surdWidth + "em";
inner.style.paddingLeft = img.advanceWidth + "em";
// Overlay the image and the argument.
body = buildCommon.makeVList([
{type: "elem", elem: inner},
{type: "kern", size: -(inner.height + imgShift)},
{type: "elem", elem: img},
{type: "kern", size: ruleWidth},
], "firstBaseline", null, options);
body.children[0].children[0].classes.push("svg-align");
}
// Overlay the image and the argument.
const body = buildCommon.makeVList([
{type: "elem", elem: inner},
{type: "kern", size: -(inner.height + imgShift)},
{type: "elem", elem: img},
{type: "kern", size: ruleWidth},
], "firstBaseline", null, options);
body.children[0].children[0].classes.push("svg-align");
if (!group.value.index) {
return makeSpan(["mord", "sqrt"], [body], options);

View File

@@ -329,14 +329,13 @@ const sqrtSvg = function(sqrtName, height, viewBoxHeight, options) {
}
const pathNode = new domTree.pathNode(sqrtName, alternate);
let attributes = [["width", "100%"], ["height", height + "em"]];
// Note: 1000:1 ratio of viewBox to document em width.
const attributes = [["width", "400em"], ["height", height + "em"]];
attributes.push(["viewBox", "0 0 400000 " + viewBoxHeight]);
attributes.push(["preserveAspectRatio", "xMinYMin slice"]);
const innerSVG = new domTree.svgNode([pathNode], attributes);
const svg = new domTree.svgNode([pathNode], attributes);
attributes = [["width", "100%"], ["height", height + "em"]];
const svg = new domTree.svgNode([innerSVG], attributes);
return buildCommon.makeSpan([], [svg], options);
return buildCommon.makeSpan(["hide-tail"], [svg], options);
};
const sqrtSpan = function(height, delim, options) {
@@ -353,22 +352,25 @@ const sqrtSpan = function(height, delim, options) {
sizeMultiplier = newOptions.sizeMultiplier / options.sizeMultiplier;
spanHeight = 1 * sizeMultiplier;
span = sqrtSvg("sqrtMain", spanHeight, viewBoxHeight, options);
span.surdWidth = 0.833 * sizeMultiplier; // from the font.
span.style.minWidth = "0.853em";
span.advanceWidth = 0.833 * sizeMultiplier; // from the font.
} else if (delim.type === "large") {
// These SVGs come from fonts: KaTeX_Size1, _Size2, etc.
viewBoxHeight = 1000 * sizeToMaxHeight[delim.size];
spanHeight = sizeToMaxHeight[delim.size] / sizeMultiplier;
span = sqrtSvg("sqrtSize" + delim.size, spanHeight, viewBoxHeight, options);
span.surdWidth = 1.0 / sizeMultiplier; // from the font
span.style.minWidth = "1.02em";
span.advanceWidth = 1.0 / sizeMultiplier; // from the font
} else {
// Tall sqrt. In TeX, this would be stacked using multiple glyphs.
// We'll use a single SVG to accomplish the same thing.
spanHeight = height / sizeMultiplier;
viewBoxHeight = Math.floor(1000 * spanHeight);
viewBoxHeight = Math.floor(1000 * height);
span = sqrtSvg("sqrtTall", spanHeight, viewBoxHeight, options);
span.surdWidth = 1.056 / sizeMultiplier;
span.style.minWidth = "0.742em";
span.advanceWidth = 1.056 / sizeMultiplier;
}
span.height = spanHeight;

View File

@@ -158,6 +158,7 @@ const svgSpan = function(group, options) {
let path;
let pathName;
let svgNode;
const classNames = [];
if (utils.contains(["widehat", "widetilde", "undertilde"], label)) {
// There are four SVG images available for each function.
@@ -208,7 +209,7 @@ const svgSpan = function(group, options) {
attributes = [];
if (numSvgChildren === 1) {
width = "100%";
width = "400em";
align = alignOne;
} else if (numSvgChildren === 2) {
// small overlap to prevent a 1 pixel gap.
@@ -231,13 +232,22 @@ const svgSpan = function(group, options) {
attributes.push(["viewBox", `0 0 ${viewBoxWidth} ${vbHeight}`]);
attributes.push(["preserveAspectRatio", align + " slice"]);
innerSVGs.push(new domTree.svgNode([path], attributes));
if (numSvgChildren > 1) {
innerSVGs.push(new domTree.svgNode([path], attributes));
} else {
// The single svgChild is a child of a hide-tail span, not the
// child of another svg.
svgNode = new domTree.svgNode([path], attributes);
classNames.push("hide-tail");
}
}
if (numSvgChildren > 1) {
attributes = [["width", "100%"], ["height", height + "em"]];
svgNode = new domTree.svgNode(innerSVGs, attributes);
}
attributes = [["width", "100%"], ["height", height + "em"]];
svgNode = new domTree.svgNode(innerSVGs, attributes);
}
const span = buildCommon.makeSpan([], [svgNode], options);
const span = buildCommon.makeSpan(classNames, [svgNode], options);
// Note that we are returning span.depth = 0.
// Any adjustments relative to the baseline must be done in buildHTML.
span.height = height;

View File

@@ -575,6 +575,13 @@
}
}
// Hide the long tail of a stretchy SVG.
.hide-tail {
width: 100%; // necessary only to get IE to work properly
position: relative; // ditto
overflow: hidden; // This line applies to all browsers.
}
// Lengthen the extensible arrows via padding.
.x-arrow-pad {
padding: 0 0.5em;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB