Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions ts/output/chtml/FontData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
StyleJsonSheet,
} from '../../util/StyleJson.js';
import { em } from '../../util/lengths.js';
import { VFUZZ, HFUZZ } from '../common/FontData.js';

export * from '../common/FontData.js';

Expand Down Expand Up @@ -479,6 +480,7 @@ export class ChtmlFontData extends FontData<
HDW: ChtmlCharData
): number {
if (!n) return 0;
let fuzz = 0;
const [h, d, w] = this.getChar(v, n);
const css: StyleJsonData = { width: this.em0(w) };
if (part !== 'ext') {
Expand All @@ -494,18 +496,25 @@ export class ChtmlFontData extends FontData<
css.margin = `${this.em(y)} ${dw} ${this.em(-y)}`;
} else {
//
// Adjust the height and depth for a little overlap.
// Set the line-height to have the extenders touch,
// (plus a little extra for Safari, whose line-height is
// not accurate), and shift the extender stack to overlap
// the ends.
// and shift the extender stack to overlap the ends.
//
css['line-height'] = this.em0(h + d + 0.005);
styles[`mjx-stretchy-v${c} > mjx-${part} > mjx-spacer`] = {
'margin-top': this.em(-d),
};
fuzz = VFUZZ;
const lh = Math.max(VFUZZ, h + d - VFUZZ);
css['line-height'] = this.em0(lh);
//
// Adjust the top margin to make sure we have overlap with the top part
//
const D = h - lh / 2 - VFUZZ;
if (D) {
styles[`mjx-stretchy-v${c} > mjx-ext > mjx-spacer`] = {
'margin-top': this.em(D),
};
}
}
styles[`mjx-stretchy-v${c} > mjx-${part}`] = css;
return h + d;
return Math.max(0, h + d - fuzz);
}

/*******************************************************/
Expand Down Expand Up @@ -556,7 +565,7 @@ export class ChtmlFontData extends FontData<
}
if (data.ext) {
styles[`mjx-stretchy-h${c} > mjx-ext > mjx-spacer`]['letter-spacing'] =
this.em(-data.ext);
this.em(-data.ext - HFUZZ);
}
}

Expand Down
36 changes: 30 additions & 6 deletions ts/output/chtml/Wrappers/mo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
ChtmlDelimiterData,
ChtmlFontData,
ChtmlFontDataClass,
VFUZZ,
HFUZZ,
} from '../FontData.js';
import { CharDataArray } from '../../common/FontData.js';
import {
Expand Down Expand Up @@ -273,7 +275,7 @@ export const ChtmlMo = (function <N, T, D>(): ChtmlMoClass<N, T, D> {
// The ext parameter should be 0, but line-height in Safari
// is not accurate, so this produces extra extenders to compensate
//
this.createAssembly(parts, stretch, stretchv, dom, h + d, 0.05, '\n');
this.createAssembly(parts, stretch, stretchv, dom, h + d, VFUZZ, '\n');
//
// Vertical needs an extra (empty) element to get vertical position right
// in some browsers (e.g., Safari)
Expand All @@ -282,7 +284,8 @@ export const ChtmlMo = (function <N, T, D>(): ChtmlMoClass<N, T, D> {
styles.height = this.em(h + d);
styles.verticalAlign = this.em(-d);
} else {
this.createAssembly(parts, stretch, stretchv, dom, w, delim.ext || 0);
const ext = (delim.ext || 0) + HFUZZ;
this.createAssembly(parts, stretch, stretchv, dom, w, ext);
styles.width = this.em(w);
}
//
Expand Down Expand Up @@ -340,10 +343,12 @@ export const ChtmlMo = (function <N, T, D>(): ChtmlMoClass<N, T, D> {
// Set up the beginning, extension, and end pieces
//
this.createPart('mjx-beg', parts[0], sn[0], sv[0], dom);
this.createPart('mjx-ext', parts[1], sn[1], sv[1], dom, WH1, WHx, nl);
/* prettier-ignore */
this.createPart('mjx-ext', parts[1], sn[1], sv[1], dom, WH1, WHx, nl, WHb, WHm / 2 || WHe);
if (parts[3]) {
this.createPart('mjx-mid', parts[3], sn[3], sv[3], dom);
this.createPart('mjx-ext', parts[1], sn[1], sv[1], dom, WH2, WHx, nl);
/* prettier-ignore */
this.createPart('mjx-ext', parts[1], sn[1], sv[1], dom, WH2, WHx, nl, WHm / 2, WHe);
}
this.createPart('mjx-end', parts[2], sn[2], sv[2], dom);
}
Expand All @@ -359,6 +364,8 @@ export const ChtmlMo = (function <N, T, D>(): ChtmlMoClass<N, T, D> {
* @param {number} W The extension width
* @param {number} Wx The width of the extender character
* @param {string} nl Character to use between extenders
* @param {number} Wb The beginning width
* @param {number} We The ending width
*/
protected createPart(
part: string,
Expand All @@ -368,7 +375,9 @@ export const ChtmlMo = (function <N, T, D>(): ChtmlMoClass<N, T, D> {
dom: N[],
W: number = 0,
Wx: number = 0,
nl: string = ''
nl: string = '',
Wb: number = 0,
We: number = 0
) {
if (n) {
const options = data[3];
Expand All @@ -379,13 +388,28 @@ export const ChtmlMo = (function <N, T, D>(): ChtmlMoClass<N, T, D> {
const c = options.c || String.fromCodePoint(n);
let nodes = [] as (N | T)[];
if (part === 'mjx-ext' && (Wx || options.dx)) {
//
// If the top and bottom must overlap, adjust the border sizes and remove the clipping
//
if (W < 0 && nl) {
dom.push(
this.html(part, {
...(font ? { class: font } : {}),
style: {
'border-width': `${this.em(Wb + W / 2)} 0 ${this.em(We + W / 2)}`,
'clip-path': 'none',
},
})
);
return;
}
//
// Some combining characters are listed as width 0,
// so get "real" width from dx and take off some
// for the right bearing.
//
if (!Wx) {
Wx = Math.max(0.06, 2 * options.dx - 0.06);
Wx = Math.max(HFUZZ, 2 * options.dx - HFUZZ);
}
const n = Math.min(Math.ceil(W / Wx) + 1, 500);
if (options.cmb) {
Expand Down
5 changes: 5 additions & 0 deletions ts/output/common/FontData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import { retryAfter } from '../../util/Retries.js';
import { DIRECTION } from './Direction.js';
export { DIRECTION } from './Direction.js';

/*****************************************************************/

export const VFUZZ = 0.07; // overlap for vertical stretchy glyphs
export const HFUZZ = 0.07; // overlap for horizontal stretchy glyphs

/****************************************************************************/

/**
Expand Down
70 changes: 40 additions & 30 deletions ts/output/svg/Wrappers/mo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
SvgDelimiterData,
SvgFontData,
SvgFontDataClass,
VFUZZ,
HFUZZ,
} from '../FontData.js';
import {
CommonMo,
Expand All @@ -41,11 +43,6 @@ import { MmlMo } from '../../../core/MmlTree/MmlNodes/mo.js';
import { BBox } from '../../../util/BBox.js';
import { DIRECTION, SvgCharData } from '../FontData.js';

/*****************************************************************/

const VFUZZ = 0.1; // overlap for vertical stretchy glyphs
const HFUZZ = 0.1; // overlap for horizontal stretchy glyphs

/*****************************************************************/
/**
* The SvgMo interface for the SVG Mo wrapper
Expand Down Expand Up @@ -269,7 +266,7 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
/**
* @param {number} n The number of the character to look up
* @param {string} variant The variant for the character to look up
* @returns {SvgCharData} The full CharData object, with CharOptions guaranteed to be defined
* @returns {SvgCharData} The full CharData object, with CharOptions guaranteed to be defined
*/
protected getChar(n: number, variant: string): SvgCharData {
const char = this.font.getChar(variant, n) || [0, 0, 0, null];
Expand All @@ -287,7 +284,7 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
* @param {number} x The x position of the glyph
* @param {number} y The y position of the glyph
* @param {N} parent The container for the glyph
* @returns {number} The width of the character placed
* @returns {number} The width of the character placed
*/
protected addGlyph(
n: number,
Expand Down Expand Up @@ -315,7 +312,7 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
* @param {string} v The variant for the top glyph
* @param {number} H The height of the stretched delimiter
* @param {number} W The width of the stretched delimiter
* @returns {number} The total height of the top glyph
* @returns {number} The total height of the top glyph
*/
protected addTop(n: number, v: string, H: number, W: number): number {
if (!n) return 0;
Expand All @@ -333,27 +330,40 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
* @param {number} B The height of the bottom glyph in the delimiter
* @param {number} W The width of the stretched delimiter
*/
/* prettier-ignore */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prettier-ignore here was probably a bit overkill at the time. But I don't like the double use of // either.
We should just use prettier-ignore-start|end instead. See below.

protected addExtV(n: number, v: string, H: number, D: number, T: number, B: number, W: number) {
protected addExtV(
n: number,
v: string,
H: number,
D: number,
T: number,
B: number,
W: number
) {
if (!n) return;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add // prettier-ignore-start here

T = Math.max(0, T - VFUZZ); // A little overlap on top
B = Math.max(0, B - VFUZZ); // A little overlap on bottom
T = Math.max(0, T - VFUZZ); // A little overlap on top
B = Math.max(0, B - VFUZZ); // A little overlap on bottom
const adaptor = this.adaptor;
const [h, d, w] = this.getChar(n, v);
const Y = H + D - T - B; // The height of the extender
const s = 1.5 * Y / (h + d); // Scale height by 1.5 to avoid bad ends
// (glyphs with rounded or anti-aliased ends don't stretch well,
// so this makes for sharper ends)
const y = (s * (h - d) - Y) / 2; // The bottom point to clip the extender
const Y = H + D - T - B; // The height of the extender
const s = (1.5 * Y) / (h + d); // Scale height by 1.5 to avoid bad ends
// (glyphs with rounded or anti-aliased ends don't stretch well,
// so this makes for sharper ends)
const y = (s * (h - d) - Y) / 2; // The bottom point to clip the extender
if (Y <= 0) return;
const svg = this.svg('svg', {
width: this.fixed(w), height: this.fixed(Y),
y: this.fixed(B - D), x: this.fixed((W - w) / 2),
viewBox: [0, y, w, Y].map(x => this.fixed(x)).join(' ')
width: this.fixed(w),
height: this.fixed(Y),
y: this.fixed(B - D),
x: this.fixed((W - w) / 2),
viewBox: [0, y, w, Y].map((x) => this.fixed(x)).join(' '),
});
this.addGlyph(n, v, 0, 0, svg);
const glyph = adaptor.lastChild(svg);
adaptor.setAttribute(glyph as N, 'transform', `scale(1,${this.jax.fixed(s)})`);
adaptor.setAttribute(
glyph as N,
'transform',
`scale(1,${this.jax.fixed(s)})`
);
if (this.dom[0]) {
adaptor.append(this.dom[0], svg);
}
Expand All @@ -367,7 +377,7 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
* @param {string} v The variant for the bottom glyph
* @param {number} D The depth of the stretched delimiter
* @param {number} W The width of the stretched delimiter
* @returns {number} The total height of the bottom glyph
* @returns {number} The total height of the bottom glyph
*/
protected addBot(n: number, v: string, D: number, W: number): number {
if (!n) return 0;
Expand Down Expand Up @@ -395,7 +405,7 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
/**
* @param {number} n The character number for the left glyph of the stretchy character
* @param {string} v The variant for the left glyph
* @returns {number} The width of the left glyph
* @returns {number} The width of the left glyph
*/
protected addLeft(n: number, v: string): number {
return n ? this.addGlyph(n, v, 0, 0) : 0;
Expand All @@ -418,14 +428,14 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
x: number = 0
) {
if (!n) return;
R = Math.max(0, R - HFUZZ); // A little less than the width of the right glyph
L = Math.max(0, L - HFUZZ); // A little less than the width of the left glyph
R = Math.max(0, R - HFUZZ); // A little less than the width of the right glyph
L = Math.max(0, L - HFUZZ); // A little less than the width of the left glyph
const adaptor = this.adaptor;
const [h, d, w] = this.getChar(n, v);
const X = W - L - R; // The width of the extender
const Y = h + d + 2 * VFUZZ; // The height (plus some fuzz) of the extender
const s = 1.5 * (X / w); // Scale the width so that left- and right-bearing won't hurt us
const D = -(d + VFUZZ); // The bottom position of the glyph
const X = W - L - R; // The width of the extender
const Y = h + d + 2 * VFUZZ; // The height (plus some fuzz) of the extender
const s = 1.5 * (X / w); // Scale the width so that left- and right-bearing won't hurt us
const D = -(d + VFUZZ); // The bottom position of the glyph
if (X <= 0) return;
const svg = this.svg('svg', {
width: this.fixed(X),
Expand Down Expand Up @@ -453,7 +463,7 @@ export const SvgMo = (function <N, T, D>(): SvgMoClass<N, T, D> {
* @param {number} n The character number for the right glyph of the stretchy character
* @param {string} v The variant for the right glyph
* @param {number} W The width of the stretched character
* @returns {number} The width of the right glyph
* @returns {number} The width of the right glyph
*/
protected addRight(n: number, v: string, W: number): number {
if (!n) return 0;
Expand Down
Loading