Skip to content

Commit 3956779

Browse files
committed
Fix interaction between styles and sizes by implementing styles as sizes.
Rather than having both `textstyle` CSS classes and `size5` CSS classes affect the font size (and step on each other), implement sizes more the way TeX does: a command like `\displaystyle` changes the current size. This is actually a simplification, since now only `size` affects the size. Simplifies CSS and computation. Many screenshotter tests change; they change to be more like TeX. For instance, `\sqrt` fixes some discrepancies in size treatment.
1 parent 9913242 commit 3956779

30 files changed

+291
-361
lines changed

src/Options.js

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@
55
* `.reset` functions.
66
*/
77

8+
const Style = require("./Style");
9+
10+
const sizeStyleMap = [ // [textsize, scriptsize, scriptscriptsize]
11+
[1, 1, 1], // size1: 0.5
12+
[2, 1, 1], // size2: 0.7
13+
[3, 2, 1], // size3: 0.8
14+
[4, 2, 1], // size4: 0.9
15+
[5, 2, 1], // size5: 1.0
16+
[6, 3, 2], // size6: 1.2
17+
[7, 5, 2], // size7: 1.44
18+
[8, 6, 4], // size8: 1.73
19+
[9, 7, 5], // size9: 2.07
20+
[10, 8, 6], // size10: 2.49
21+
];
22+
23+
const sizeMultipliers = [
24+
0.5, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.73, 2.07, 2.49,
25+
];
26+
827
/**
928
* This is the main options class. It contains the style, size, color, and font
1029
* of the current parse level. It also contains the style and size of the parent
@@ -18,20 +37,10 @@ function Options(data) {
1837
this.style = data.style;
1938
this.color = data.color;
2039
this.size = data.size;
40+
this.baseSize = data.baseSize || this.size;
2141
this.phantom = data.phantom;
2242
this.font = data.font;
23-
24-
if (data.parentStyle === undefined) {
25-
this.parentStyle = data.style;
26-
} else {
27-
this.parentStyle = data.parentStyle;
28-
}
29-
30-
if (data.parentSize === undefined) {
31-
this.parentSize = data.size;
32-
} else {
33-
this.parentSize = data.parentSize;
34-
}
43+
this.sizeMultiplier = sizeMultipliers[this.size - 1];
3544
}
3645

3746
/**
@@ -42,6 +51,7 @@ Options.prototype.extend = function(extension) {
4251
const data = {
4352
style: this.style,
4453
size: this.size,
54+
baseSize: this.baseSize,
4555
color: this.color,
4656
parentStyle: this.style,
4757
parentSize: this.size,
@@ -58,22 +68,69 @@ Options.prototype.extend = function(extension) {
5868
return new Options(data);
5969
};
6070

71+
function sizeAtStyle(size, style) {
72+
return style.size < 2 ? size : sizeStyleMap[size - 1][style.size - 1];
73+
}
74+
6175
/**
62-
* Create a new options object with the given style.
76+
* Return an options object with the given style. If `this.style === style`,
77+
* returns `this`.
6378
*/
64-
Options.prototype.withStyle = function(style) {
65-
return this.extend({
66-
style: style,
67-
});
79+
Options.prototype.havingStyle = function(style) {
80+
if (this.style === style) {
81+
return this;
82+
} else {
83+
return this.extend({
84+
style: style,
85+
size: sizeAtStyle(this.baseSize, style),
86+
});
87+
}
6888
};
6989

7090
/**
71-
* Create a new options object with the given size.
91+
* Return an options object with a cramped version of the current style. If
92+
* the current style is cramped, returns `this`.
7293
*/
73-
Options.prototype.withSize = function(size) {
74-
return this.extend({
75-
size: size,
76-
});
94+
Options.prototype.havingCrampedStyle = function() {
95+
return this.havingStyle(this.style.cramp());
96+
};
97+
98+
/**
99+
* Return an options object with the given size and baseSize. If
100+
* `this.size === size && this.baseSize === size`, returns `this`.
101+
*/
102+
Options.prototype.havingSize = function(size) {
103+
if (this.size === size && this.baseSize === size) {
104+
return this;
105+
} else {
106+
// Ensure style is at least `\textstyle`.
107+
let style = this.style;
108+
if (style.size > 1) {
109+
style = style.cramped ? Style.TEXT.cramp() : Style.TEXT;
110+
}
111+
112+
return this.extend({
113+
style: style,
114+
size: size,
115+
baseSize: size,
116+
});
117+
}
118+
};
119+
120+
/**
121+
* Like `this.havingSize(5).havingStyle(style)`.
122+
*/
123+
Options.prototype.havingBaseStyle = function(style) {
124+
const wantSize = sizeAtStyle(5, style);
125+
if (this.size === wantSize && this.baseSize === 5 && this.style === style) {
126+
return this;
127+
} else {
128+
return this.extend({
129+
style: style,
130+
size: wantSize,
131+
baseSize: 5,
132+
});
133+
}
77134
};
78135

79136
/**
@@ -104,11 +161,15 @@ Options.prototype.withFont = function(font) {
104161
};
105162

106163
/**
107-
* Create a new options object with the same style, size, and color. This is
108-
* used so that parent style and size changes are handled correctly.
164+
* Return the CSS sizing classes required to switch from enclosing options
165+
* `oldOptions` to `this`. Returns an array of classes.
109166
*/
110-
Options.prototype.reset = function() {
111-
return this.extend({});
167+
Options.prototype.sizingClasses = function(oldOptions) {
168+
if (oldOptions.size !== this.size) {
169+
return ["sizing", "reset-size" + oldOptions.size, "size" + this.size];
170+
} else {
171+
return [];
172+
}
112173
};
113174

114175
/**

src/Parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ Parser.prototype.parseImplicitGroup = function() {
478478
const body = this.parseExpression(false);
479479
return new ParseNode("sizing", {
480480
// Figure out what size to use based on the list of functions above
481-
size: "size" + (utils.indexOf(sizeFuncs, func) + 1),
481+
size: utils.indexOf(sizeFuncs, func) + 1,
482482
value: body,
483483
}, this.mode);
484484
} else if (utils.contains(styleFuncs, func)) {

src/Style.js

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,12 @@ for (let i = 0; i < 3; i++) {
2222

2323
/**
2424
* The main style class. Contains a unique id for the style, a size (which is
25-
* the same for cramped and uncramped version of a style), a cramped flag, and a
26-
* size multiplier, which gives the size difference between a style and
27-
* textstyle.
25+
* the same for cramped and uncramped version of a style), and a cramped flag.
2826
*/
29-
function Style(id, size, multiplier, cramped) {
27+
function Style(id, size, cramped) {
3028
this.id = id;
3129
this.size = size;
3230
this.cramped = cramped;
33-
this.sizeMultiplier = multiplier;
3431
this.metrics = metrics[size > 0 ? size - 1 : 0];
3532
}
3633

@@ -72,20 +69,6 @@ Style.prototype.cramp = function() {
7269
return styles[cramp[this.id]];
7370
};
7471

75-
/**
76-
* HTML class name, like "displaystyle cramped"
77-
*/
78-
Style.prototype.cls = function() {
79-
return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped");
80-
};
81-
82-
/**
83-
* HTML Reset class name, like "reset-textstyle"
84-
*/
85-
Style.prototype.reset = function() {
86-
return resetNames[this.size];
87-
};
88-
8972
/**
9073
* Return if this style is tightly spaced (scriptstyle/scriptscriptstyle)
9174
*/
@@ -103,32 +86,16 @@ const Sc = 5;
10386
const SS = 6;
10487
const SSc = 7;
10588

106-
// String names for the different sizes
107-
const sizeNames = [
108-
"displaystyle textstyle",
109-
"textstyle",
110-
"scriptstyle",
111-
"scriptscriptstyle",
112-
];
113-
114-
// Reset names for the different sizes
115-
const resetNames = [
116-
"reset-textstyle",
117-
"reset-textstyle",
118-
"reset-scriptstyle",
119-
"reset-scriptscriptstyle",
120-
];
121-
12289
// Instances of the different styles
12390
const styles = [
124-
new Style(D, 0, 1.0, false),
125-
new Style(Dc, 0, 1.0, true),
126-
new Style(T, 1, 1.0, false),
127-
new Style(Tc, 1, 1.0, true),
128-
new Style(S, 2, 0.7, false),
129-
new Style(Sc, 2, 0.7, true),
130-
new Style(SS, 3, 0.5, false),
131-
new Style(SSc, 3, 0.5, true),
91+
new Style(D, 0, false),
92+
new Style(Dc, 0, true),
93+
new Style(T, 1, false),
94+
new Style(Tc, 1, true),
95+
new Style(S, 2, false),
96+
new Style(Sc, 2, true),
97+
new Style(SS, 3, false),
98+
new Style(SSc, 3, true),
13299
];
133100

134101
// Lookup tables for switching from one style to another

src/buildCommon.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const makeSymbol = function(value, fontFamily, mode, options, classes) {
6363
}
6464

6565
if (options) {
66+
symbolNode.maxFontSize = options.sizeMultiplier;
6667
if (options.style.isTight()) {
6768
symbolNode.classes.push("mtight");
6869
}
@@ -239,11 +240,10 @@ const makeFragment = function(children) {
239240
*/
240241
const makeFontSizer = function(options, fontSize) {
241242
const fontSizeInner = makeSpan([], [new domTree.symbolNode("\u200b")]);
242-
fontSizeInner.style.fontSize =
243-
(fontSize / options.style.sizeMultiplier) + "em";
243+
fontSizeInner.style.fontSize = fontSize + "em";
244244

245245
const fontSizer = makeSpan(
246-
["fontsize-ensurer", "reset-" + options.size, "size5"],
246+
["fontsize-ensurer", "reset-size" + options.size, "size5"],
247247
[fontSizeInner]);
248248

249249
return fontSizer;
@@ -378,20 +378,6 @@ const makeVList = function(children, positionType, positionData, options) {
378378
return vlist;
379379
};
380380

381-
// A table of size -> font size for the different sizing functions
382-
const sizingMultiplier = {
383-
size1: 0.5,
384-
size2: 0.7,
385-
size3: 0.8,
386-
size4: 0.9,
387-
size5: 1.0,
388-
size6: 1.2,
389-
size7: 1.44,
390-
size8: 1.73,
391-
size9: 2.07,
392-
size10: 2.49,
393-
};
394-
395381
// A map of spacing functions to their attributes, like size and corresponding
396382
// CSS class
397383
const spacingFunctions = {
@@ -486,6 +472,5 @@ module.exports = {
486472
makeVList: makeVList,
487473
makeOrd: makeOrd,
488474
prependChildren: prependChildren,
489-
sizingMultiplier: sizingMultiplier,
490475
spacingFunctions: spacingFunctions,
491476
};

0 commit comments

Comments
 (0)