postext 1.28.6 → 1.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/arabicLocale.test.js +10 -2
- package/dist/__tests__/arabicLocale.test.js.map +1 -1
- package/dist/__tests__/bundle/balloonPlacementPin.test.js +5 -1
- package/dist/__tests__/bundle/balloonPlacementPin.test.js.map +1 -1
- package/dist/__tests__/bundle/cjkRulesPin.test.js +15 -9
- package/dist/__tests__/bundle/cjkRulesPin.test.js.map +1 -1
- package/dist/__tests__/bundle/designOverflowPin.test.js +3 -3
- package/dist/__tests__/bundle/designOverflowPin.test.js.map +1 -1
- package/dist/__tests__/bundle/flowColumnsPin.test.js +4 -1
- package/dist/__tests__/bundle/flowColumnsPin.test.js.map +1 -1
- package/dist/__tests__/bundle/gridBalancingPin.test.js +6 -2
- package/dist/__tests__/bundle/gridBalancingPin.test.js.map +1 -1
- package/dist/__tests__/bundle/gridBreakRulesPin.test.js +23 -17
- package/dist/__tests__/bundle/gridBreakRulesPin.test.js.map +1 -1
- package/dist/__tests__/bundle/indexGroupSplitPin.test.js +5 -2
- package/dist/__tests__/bundle/indexGroupSplitPin.test.js.map +1 -1
- package/dist/__tests__/bundle/looseOrderPin.test.js +5 -1
- package/dist/__tests__/bundle/looseOrderPin.test.js.map +1 -1
- package/dist/__tests__/bundle/refLabelBreaksPin.test.js +5 -1
- package/dist/__tests__/bundle/refLabelBreaksPin.test.js.map +1 -1
- package/dist/__tests__/bundle/scriptContextPin.test.js +7 -2
- package/dist/__tests__/bundle/scriptContextPin.test.js.map +1 -1
- package/dist/__tests__/bundle/shapingLanguagePin.test.d.ts +2 -0
- package/dist/__tests__/bundle/shapingLanguagePin.test.d.ts.map +1 -0
- package/dist/__tests__/bundle/shapingLanguagePin.test.js +96 -0
- package/dist/__tests__/bundle/shapingLanguagePin.test.js.map +1 -0
- package/dist/__tests__/bundle/taskBoxPin.test.d.ts +2 -0
- package/dist/__tests__/bundle/taskBoxPin.test.d.ts.map +1 -0
- package/dist/__tests__/bundle/taskBoxPin.test.js +94 -0
- package/dist/__tests__/bundle/taskBoxPin.test.js.map +1 -0
- package/dist/__tests__/bundle/trailingStepPin.test.js +5 -2
- package/dist/__tests__/bundle/trailingStepPin.test.js.map +1 -1
- package/dist/__tests__/bundle/wideFloatStretchPin.test.js +5 -2
- package/dist/__tests__/bundle/wideFloatStretchPin.test.js.map +1 -1
- package/dist/__tests__/bundle.test.js +5 -5
- package/dist/__tests__/bundle.test.js.map +1 -1
- package/dist/__tests__/chineseLocale.test.js +7 -1
- package/dist/__tests__/chineseLocale.test.js.map +1 -1
- package/dist/__tests__/cjk/japaneseHtml.test.js +19 -3
- package/dist/__tests__/cjk/japaneseHtml.test.js.map +1 -1
- package/dist/__tests__/cjk/japaneseSegmentLanguage.test.js +24 -4
- package/dist/__tests__/cjk/japaneseSegmentLanguage.test.js.map +1 -1
- package/dist/__tests__/codeBlocks.test.js +1 -1
- package/dist/__tests__/exports.test.js +14 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/hardBreaks.test.js +1 -1
- package/dist/__tests__/headingInlineMarks.test.js +1 -1
- package/dist/__tests__/mathSizeMigration.test.js +4 -4
- package/dist/__tests__/mathSizeMigration.test.js.map +1 -1
- package/dist/__tests__/pipeline/calloutInlineResourceGap.test.js +1 -1
- package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js +3 -3
- package/dist/__tests__/pipeline/calloutSplitMinLinesChild.test.js.map +1 -1
- package/dist/__tests__/pipeline/colonListRoom.test.js +3 -3
- package/dist/__tests__/pipeline/colonListRoom.test.js.map +1 -1
- package/dist/__tests__/pipeline/compoundBreaks.test.js +6 -6
- package/dist/__tests__/pipeline/compoundBreaks.test.js.map +1 -1
- package/dist/__tests__/pipeline/dashBreaks.test.js +5 -5
- package/dist/__tests__/pipeline/dashBreaks.test.js.map +1 -1
- package/dist/__tests__/pipeline/headingSplitOrphans.test.js +3 -3
- package/dist/__tests__/pipeline/headingSplitOrphans.test.js.map +1 -1
- package/dist/__tests__/pipeline/inlineFigureSpace.test.js +5 -5
- package/dist/__tests__/pipeline/inlineFigureSpace.test.js.map +1 -1
- package/dist/__tests__/pipeline/paragraphContainerTail.test.js +7 -7
- package/dist/__tests__/pipeline/paragraphContainerTail.test.js.map +1 -1
- package/dist/__tests__/pipeline/raggedOptimal.test.js +6 -6
- package/dist/__tests__/pipeline/raggedOptimal.test.js.map +1 -1
- package/dist/__tests__/rtl/htmlRtl.test.js +15 -2
- package/dist/__tests__/rtl/htmlRtl.test.js.map +1 -1
- package/dist/__tests__/shapingLanguage.test.d.ts +2 -0
- package/dist/__tests__/shapingLanguage.test.d.ts.map +1 -0
- package/dist/__tests__/shapingLanguage.test.js +352 -0
- package/dist/__tests__/shapingLanguage.test.js.map +1 -0
- package/dist/__tests__/taskBox.test.d.ts +2 -0
- package/dist/__tests__/taskBox.test.d.ts.map +1 -0
- package/dist/__tests__/taskBox.test.js +291 -0
- package/dist/__tests__/taskBox.test.js.map +1 -0
- package/dist/__tests__/verseLines.test.js +7 -7
- package/dist/__tests__/verseLines.test.js.map +1 -1
- package/dist/bundle/configVersion.d.ts +53 -7
- package/dist/bundle/configVersion.d.ts.map +1 -1
- package/dist/bundle/configVersion.js +84 -6
- package/dist/bundle/configVersion.js.map +1 -1
- package/dist/bundle/index.d.ts +2 -1
- package/dist/bundle/index.d.ts.map +1 -1
- package/dist/bundle/index.js +2 -1
- package/dist/bundle/index.js.map +1 -1
- package/dist/canvas-backend/blockRender.d.ts.map +1 -1
- package/dist/canvas-backend/blockRender.js +41 -2
- package/dist/canvas-backend/blockRender.js.map +1 -1
- package/dist/canvas-backend/index.d.ts.map +1 -1
- package/dist/canvas-backend/index.js +10 -5
- package/dist/canvas-backend/index.js.map +1 -1
- package/dist/canvas-backend/trackedText.d.ts.map +1 -1
- package/dist/canvas-backend/trackedText.js +9 -5
- package/dist/canvas-backend/trackedText.js.map +1 -1
- package/dist/canvas-backend/wordRuns.d.ts +6 -0
- package/dist/canvas-backend/wordRuns.d.ts.map +1 -1
- package/dist/canvas-backend/wordRuns.js +31 -0
- package/dist/canvas-backend/wordRuns.js.map +1 -1
- package/dist/comics/lettering/letter.d.ts.map +1 -1
- package/dist/comics/lettering/letter.js +2 -1
- package/dist/comics/lettering/letter.js.map +1 -1
- package/dist/contrast.d.ts.map +1 -1
- package/dist/contrast.js +5 -0
- package/dist/contrast.js.map +1 -1
- package/dist/defaults/bodyText.d.ts.map +1 -1
- package/dist/defaults/bodyText.js +10 -0
- package/dist/defaults/bodyText.js.map +1 -1
- package/dist/defaults/unorderedLists.d.ts.map +1 -1
- package/dist/defaults/unorderedLists.js +7 -0
- package/dist/defaults/unorderedLists.js.map +1 -1
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +45 -5
- package/dist/html-backend.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/locale.d.ts +37 -14
- package/dist/locale.d.ts.map +1 -1
- package/dist/locale.js +40 -12
- package/dist/locale.js.map +1 -1
- package/dist/measure/cache.js +2 -2
- package/dist/measure/cache.js.map +1 -1
- package/dist/measure/canvas.d.ts +20 -0
- package/dist/measure/canvas.d.ts.map +1 -1
- package/dist/measure/canvas.js +131 -50
- package/dist/measure/canvas.js.map +1 -1
- package/dist/measure/cjkAnnotate.js +2 -2
- package/dist/measure/cjkAnnotate.js.map +1 -1
- package/dist/measure/cjkCompose.d.ts.map +1 -1
- package/dist/measure/cjkCompose.js +225 -209
- package/dist/measure/cjkCompose.js.map +1 -1
- package/dist/measure/plain.d.ts.map +1 -1
- package/dist/measure/plain.js +4 -2
- package/dist/measure/plain.js.map +1 -1
- package/dist/measure/pretextLanguage.d.ts +12 -0
- package/dist/measure/pretextLanguage.d.ts.map +1 -0
- package/dist/measure/pretextLanguage.js +117 -0
- package/dist/measure/pretextLanguage.js.map +1 -0
- package/dist/measure/rich.d.ts +4 -0
- package/dist/measure/rich.d.ts.map +1 -1
- package/dist/measure/rich.js +164 -137
- package/dist/measure/rich.js.map +1 -1
- package/dist/measure/rubyLift.d.ts.map +1 -1
- package/dist/measure/rubyLift.js +4 -3
- package/dist/measure/rubyLift.js.map +1 -1
- package/dist/measure/spanLanguage.d.ts +11 -0
- package/dist/measure/spanLanguage.d.ts.map +1 -0
- package/dist/measure/spanLanguage.js +19 -0
- package/dist/measure/spanLanguage.js.map +1 -0
- package/dist/measure/vertical.d.ts.map +1 -1
- package/dist/measure/vertical.js +4 -3
- package/dist/measure/vertical.js.map +1 -1
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +15 -2
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/calloutLayout.d.ts.map +1 -1
- package/dist/pipeline/calloutLayout.js +10 -4
- package/dist/pipeline/calloutLayout.js.map +1 -1
- package/dist/pipeline/dropCap.js +2 -2
- package/dist/pipeline/dropCap.js.map +1 -1
- package/dist/pipeline/lists.d.ts +8 -0
- package/dist/pipeline/lists.d.ts.map +1 -1
- package/dist/pipeline/lists.js +16 -1
- package/dist/pipeline/lists.js.map +1 -1
- package/dist/pipeline/toc.d.ts.map +1 -1
- package/dist/pipeline/toc.js +5 -3
- package/dist/pipeline/toc.js.map +1 -1
- package/dist/taskBox.d.ts +55 -0
- package/dist/taskBox.d.ts.map +1 -0
- package/dist/taskBox.js +79 -0
- package/dist/taskBox.js.map +1 -0
- package/dist/types.d.ts +54 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +14 -0
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js.map +1 -1
- package/package.json +1 -1
package/dist/measure/rich.js
CHANGED
|
@@ -6,7 +6,8 @@ import { getHyphenationRulesKey, hyphenateByDictionary, hyphenateText, softHyphe
|
|
|
6
6
|
import { richTokensToItems, computeBreakpoints, reconstructRichLines, } from '../knuthPlass';
|
|
7
7
|
import { SOFT_HYPHEN } from './types';
|
|
8
8
|
import { lineStartAt, lineWidthAt, lineWidthFunction, markInsetLines, uniformMeasureFrom } from './types';
|
|
9
|
-
import { cleanSoftHyphens, measureTextWidth, normalSpaceWidthFor } from './canvas';
|
|
9
|
+
import { cleanSoftHyphens, getMeasureLanguage, measureTextWidth, normalSpaceWidthFor, setMeasureLanguage, withMeasureLanguage } from './canvas';
|
|
10
|
+
import { spanShapingLanguage } from './spanLanguage';
|
|
10
11
|
import { isRuntLastLine } from './runts';
|
|
11
12
|
import { hasOverfullLine } from './plain';
|
|
12
13
|
import { justifiedSpaceRatios } from './spaceRatios';
|
|
@@ -127,6 +128,8 @@ function scriptOf(t) {
|
|
|
127
128
|
// The pieces of a word with no letter are shaped in its script (#725).
|
|
128
129
|
...(t.scriptContext ? { scriptContext: t.scriptContext } : {}),
|
|
129
130
|
...(t.scriptRtl ? { scriptRtl: true } : {}),
|
|
131
|
+
// And the pieces of a word of a run in another language in it (#764).
|
|
132
|
+
...(t.lang !== undefined ? { lang: t.lang } : {}),
|
|
130
133
|
};
|
|
131
134
|
}
|
|
132
135
|
/** The script fields of the piece `[from, to)` of `token`, a head or a
|
|
@@ -154,6 +157,12 @@ function rtlWordWidth(text, font) {
|
|
|
154
157
|
/** How a token's text is measured: in its script when it is a word with
|
|
155
158
|
* no letter given one (#725), else as any text (`textWidth`). */
|
|
156
159
|
function tokenTextWidth(token, font) {
|
|
160
|
+
// A word of a run in another language is measured in it (#764).
|
|
161
|
+
if (token.lang !== undefined) {
|
|
162
|
+
const { lang, ...rest } = token;
|
|
163
|
+
const inner = tokenTextWidth(rest, font);
|
|
164
|
+
return (text) => withMeasureLanguage(lang, () => inner(text));
|
|
165
|
+
}
|
|
157
166
|
const script = token.scriptContext;
|
|
158
167
|
if (script)
|
|
159
168
|
return (text) => (text === '' ? 0 : scriptTextWidth(text, font, script, token.scriptRtl === true));
|
|
@@ -1140,147 +1149,165 @@ keepAll = false) {
|
|
|
1140
1149
|
};
|
|
1141
1150
|
};
|
|
1142
1151
|
for (const span of spans) {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
//
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
+
// A run that names its language (`:ltr[…]{lang=en}`) is measured in it,
|
|
1153
|
+
// in a document shaped in its own (#764); its tokens say so, for what
|
|
1154
|
+
// is measured of them later (a cut, a piece).
|
|
1155
|
+
const ownLanguage = spanShapingLanguage(span);
|
|
1156
|
+
const spanTokensFrom = tokens.length;
|
|
1157
|
+
const languageBefore = getMeasureLanguage();
|
|
1158
|
+
if (ownLanguage !== undefined)
|
|
1159
|
+
setMeasureLanguage(ownLanguage);
|
|
1160
|
+
try {
|
|
1161
|
+
const spanBase = spanStart;
|
|
1162
|
+
spanStart += span.text.length;
|
|
1163
|
+
// A tab (#622): its width is set where its line gets to it.
|
|
1164
|
+
// Tab characters in a row are one character of the text that goes on
|
|
1165
|
+
// that many stops: the later ones have no text.
|
|
1166
|
+
if (span.tab) {
|
|
1167
|
+
const tab = span.tab.px ? { stop: span.tab.px } : {};
|
|
1168
|
+
for (let k = 0; k < Math.max(1, span.tab.count ?? 1); k++) {
|
|
1169
|
+
tokens.push({ text: k === 0 ? span.text : '', bold: span.bold, italic: span.italic, kind: 'space', width: 0, tab });
|
|
1170
|
+
}
|
|
1171
|
+
continue;
|
|
1152
1172
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
tokens.push(...label);
|
|
1159
|
-
continue;
|
|
1160
|
-
}
|
|
1161
|
-
const atomic = atomicSpanToken(span, normalFont, boldFont, italicFont, boldItalicFont, letterSpacingPx);
|
|
1162
|
-
if (atomic) {
|
|
1163
|
-
tokens.push(atomic);
|
|
1164
|
-
continue;
|
|
1165
|
-
}
|
|
1166
|
-
const font = spanFont(span);
|
|
1167
|
-
// A fixed space is set as text: no break, no justification glue.
|
|
1168
|
-
if (span.fixedSpace) {
|
|
1169
|
-
if (span.text.length > 0) {
|
|
1170
|
-
tokens.push({ text: span.text, bold: span.bold, italic: span.italic, kind: 'text', width: textWidth(span.text, font, false) + track(span.text) });
|
|
1173
|
+
// A reference whose label is words: one token per word and space (#702).
|
|
1174
|
+
const label = refLabelTokens(span, normalFont, boldFont, italicFont, boldItalicFont, letterSpacingPx);
|
|
1175
|
+
if (label) {
|
|
1176
|
+
tokens.push(...label);
|
|
1177
|
+
continue;
|
|
1171
1178
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
const marks = spanMarks(span, getMeasureWritingMode() === 'vertical-rl');
|
|
1176
|
-
const scriptFields = {
|
|
1177
|
-
...scriptFieldsOf(span),
|
|
1178
|
-
...(marks ? { cjkMarks: marks } : {}),
|
|
1179
|
-
...(span.inserted ? { inserted: true } : {}),
|
|
1180
|
-
};
|
|
1181
|
-
// Small capitals: the words keep their case (hyphenation reads it) and
|
|
1182
|
-
// measure as they will be painted.
|
|
1183
|
-
const sc = !!span.smallCaps;
|
|
1184
|
-
const scFields = sc ? { smallCaps: true } : {};
|
|
1185
|
-
// Split on word boundaries while preserving spaces. Each word is
|
|
1186
|
-
// hyphenated on its own (the dictionary works word by word anyway), so
|
|
1187
|
-
// its soft hyphens can be told apart from the ones the source carries.
|
|
1188
|
-
// A no-break space is no boundary: it stays inside the word it glues
|
|
1189
|
-
// ("37 °C"), which the line then never breaks at (EF-66).
|
|
1190
|
-
const matched = span.text.match(WORDS_AND_SPACES_RE);
|
|
1191
|
-
if (!matched)
|
|
1192
|
-
continue;
|
|
1193
|
-
const levelled = bidi ? cutAtLevels(matched, spanBase, bidi) : matched;
|
|
1194
|
-
// In a Chinese or Japanese document a word with no letter is cut
|
|
1195
|
-
// around its doubled ellipses and dashes: the document's own marks,
|
|
1196
|
-
// set as Han text whatever sentence cites them (`cutDoubledMarks`).
|
|
1197
|
-
const parts = contextOf && scriptState.hanMarks && faceReadsScript(font)
|
|
1198
|
-
? levelled.flatMap((part) => (isBreakingSpaceRun(part) ? undefined : cutDoubledMarks(part)) ?? [part])
|
|
1199
|
-
: levelled;
|
|
1200
|
-
let partStart = spanBase;
|
|
1201
|
-
for (const part of parts) {
|
|
1202
|
-
const partAt = partStart;
|
|
1203
|
-
partStart += part.length;
|
|
1204
|
-
const isSpace = isBreakingSpaceRun(part);
|
|
1205
|
-
if (!isSpace && URL_LIKE_RE.test(part.replace(/\u00AD/g, '')) && !(letterSpacingPx !== 0 && joiningScriptIn(part))) {
|
|
1206
|
-
const clean = part.replace(/\u00AD/g, '');
|
|
1207
|
-
// An address is one script run: its pieces with no letter
|
|
1208
|
-
// (`10.1006/`, `.2001`) are measured in the script they have in
|
|
1209
|
-
// it, and an address with no letter at all in its context's (#725).
|
|
1210
|
-
const inScript = scriptMeasure(contextOf?.(clean, partAt, part.length, font), font, sc);
|
|
1211
|
-
const spacing = inScript.untracked ? 0 : letterSpacingPx;
|
|
1212
|
-
const upTo = inScript.upTo ?? ((text) => textWidth(text, font, sc));
|
|
1213
|
-
const breakPoints = urlBreakIndices(clean).map((charIndex) => ({
|
|
1214
|
-
charIndex,
|
|
1215
|
-
widthBefore: upTo(clean.slice(0, charIndex)) + (spacing === 0 ? 0 : spacing * graphemeCount(clean.slice(0, charIndex))),
|
|
1216
|
-
}));
|
|
1217
|
-
tokens.push({
|
|
1218
|
-
text: clean,
|
|
1219
|
-
bold: span.bold,
|
|
1220
|
-
italic: span.italic,
|
|
1221
|
-
captionLabel: span.captionLabel,
|
|
1222
|
-
...scriptFields,
|
|
1223
|
-
...scFields,
|
|
1224
|
-
...inScript.fields,
|
|
1225
|
-
kind: 'text',
|
|
1226
|
-
width: (inScript.whole ? inScript.whole(clean) : textWidth(clean, font, sc)) + (inScript.untracked ? 0 : track(clean)),
|
|
1227
|
-
...(breakPoints.length > 0 ? { breakPoints, hyphenWidth: 0, bareBreaks: true } : {}),
|
|
1228
|
-
});
|
|
1179
|
+
const atomic = atomicSpanToken(span, normalFont, boldFont, italicFont, boldItalicFont, letterSpacingPx);
|
|
1180
|
+
if (atomic) {
|
|
1181
|
+
tokens.push(atomic);
|
|
1229
1182
|
continue;
|
|
1230
1183
|
}
|
|
1231
|
-
const
|
|
1232
|
-
//
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
//
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
//
|
|
1247
|
-
//
|
|
1248
|
-
const
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1184
|
+
const font = spanFont(span);
|
|
1185
|
+
// A fixed space is set as text: no break, no justification glue.
|
|
1186
|
+
if (span.fixedSpace) {
|
|
1187
|
+
if (span.text.length > 0) {
|
|
1188
|
+
tokens.push({ text: span.text, bold: span.bold, italic: span.italic, kind: 'text', width: textWidth(span.text, font, false) + track(span.text) });
|
|
1189
|
+
}
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
// Chinese marks (#193) ride on the span's words like its script.
|
|
1193
|
+
const marks = spanMarks(span, getMeasureWritingMode() === 'vertical-rl');
|
|
1194
|
+
const scriptFields = {
|
|
1195
|
+
...scriptFieldsOf(span),
|
|
1196
|
+
...(marks ? { cjkMarks: marks } : {}),
|
|
1197
|
+
...(span.inserted ? { inserted: true } : {}),
|
|
1198
|
+
};
|
|
1199
|
+
// Small capitals: the words keep their case (hyphenation reads it) and
|
|
1200
|
+
// measure as they will be painted.
|
|
1201
|
+
const sc = !!span.smallCaps;
|
|
1202
|
+
const scFields = sc ? { smallCaps: true } : {};
|
|
1203
|
+
// Split on word boundaries while preserving spaces. Each word is
|
|
1204
|
+
// hyphenated on its own (the dictionary works word by word anyway), so
|
|
1205
|
+
// its soft hyphens can be told apart from the ones the source carries.
|
|
1206
|
+
// A no-break space is no boundary: it stays inside the word it glues
|
|
1207
|
+
// ("37 °C"), which the line then never breaks at (EF-66).
|
|
1208
|
+
const matched = span.text.match(WORDS_AND_SPACES_RE);
|
|
1209
|
+
if (!matched)
|
|
1210
|
+
continue;
|
|
1211
|
+
const levelled = bidi ? cutAtLevels(matched, spanBase, bidi) : matched;
|
|
1212
|
+
// In a Chinese or Japanese document a word with no letter is cut
|
|
1213
|
+
// around its doubled ellipses and dashes: the document's own marks,
|
|
1214
|
+
// set as Han text whatever sentence cites them (`cutDoubledMarks`).
|
|
1215
|
+
const parts = contextOf && scriptState.hanMarks && faceReadsScript(font)
|
|
1216
|
+
? levelled.flatMap((part) => (isBreakingSpaceRun(part) ? undefined : cutDoubledMarks(part)) ?? [part])
|
|
1217
|
+
: levelled;
|
|
1218
|
+
let partStart = spanBase;
|
|
1219
|
+
for (const part of parts) {
|
|
1220
|
+
const partAt = partStart;
|
|
1221
|
+
partStart += part.length;
|
|
1222
|
+
const isSpace = isBreakingSpaceRun(part);
|
|
1223
|
+
if (!isSpace && URL_LIKE_RE.test(part.replace(/\u00AD/g, '')) && !(letterSpacingPx !== 0 && joiningScriptIn(part))) {
|
|
1224
|
+
const clean = part.replace(/\u00AD/g, '');
|
|
1225
|
+
// An address is one script run: its pieces with no letter
|
|
1226
|
+
// (`10.1006/`, `.2001`) are measured in the script they have in
|
|
1227
|
+
// it, and an address with no letter at all in its context's (#725).
|
|
1228
|
+
const inScript = scriptMeasure(contextOf?.(clean, partAt, part.length, font), font, sc);
|
|
1229
|
+
const spacing = inScript.untracked ? 0 : letterSpacingPx;
|
|
1230
|
+
const upTo = inScript.upTo ?? ((text) => textWidth(text, font, sc));
|
|
1231
|
+
const breakPoints = urlBreakIndices(clean).map((charIndex) => ({
|
|
1232
|
+
charIndex,
|
|
1233
|
+
widthBefore: upTo(clean.slice(0, charIndex)) + (spacing === 0 ? 0 : spacing * graphemeCount(clean.slice(0, charIndex))),
|
|
1234
|
+
}));
|
|
1235
|
+
tokens.push({
|
|
1236
|
+
text: clean,
|
|
1237
|
+
bold: span.bold,
|
|
1238
|
+
italic: span.italic,
|
|
1239
|
+
captionLabel: span.captionLabel,
|
|
1240
|
+
...scriptFields,
|
|
1241
|
+
...scFields,
|
|
1242
|
+
...inScript.fields,
|
|
1243
|
+
kind: 'text',
|
|
1244
|
+
width: (inScript.whole ? inScript.whole(clean) : textWidth(clean, font, sc)) + (inScript.untracked ? 0 : track(clean)),
|
|
1245
|
+
...(breakPoints.length > 0 ? { breakPoints, hyphenWidth: 0, bareBreaks: true } : {}),
|
|
1246
|
+
});
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
const { clean, soft } = isSpace ? { clean: part, soft: [] } : syllabify(part, shouldHyphenate && inCompound?.[partAt] !== true);
|
|
1250
|
+
// The characters this word touches, for a dash that opens it
|
|
1251
|
+
// ("**riddles.**—I", "*\"no\"*—and"): the end of a word run just
|
|
1252
|
+
// before, no space in between.
|
|
1253
|
+
const before = tokens[tokens.length - 1];
|
|
1254
|
+
const touching = dashBreaks && before && before.kind === 'text' && !before.mathRender && !before.swatch && !before.chip
|
|
1255
|
+
? before.text.slice(-2)
|
|
1256
|
+
: undefined;
|
|
1257
|
+
// A word that holds CJK characters: measured unit by unit, once each.
|
|
1258
|
+
const cjk = cjkText && !isSpace && hasCJK(clean) ? cjkWordBreaks(clean, font, sc, letterSpacingPx, undefined, keepAll) : undefined;
|
|
1259
|
+
// A word of a joining script is measured untracked, its break points
|
|
1260
|
+
// (after a hyphen or a dash it carries) too.
|
|
1261
|
+
// A word with no letter: measured in the script of its context, its
|
|
1262
|
+
// pieces at a break too (#725). In the Arabic script it takes no
|
|
1263
|
+
// tracking, as the Arabic words around it take none.
|
|
1264
|
+
// A word with a letter: its pieces with no letter (`COVID-` | `19`)
|
|
1265
|
+
// in the script they have inside it.
|
|
1266
|
+
const measured = scriptMeasure(contextOf && !isSpace && !cjk ? contextOf(clean, partAt, part.length, font) : undefined, font, sc);
|
|
1267
|
+
const untracked = measured.untracked;
|
|
1268
|
+
const wordSpacing = isSpace ? letterSpacingPx : untracked ? 0 : wordLetterSpacing(clean, letterSpacingPx);
|
|
1269
|
+
const inScript = measured.whole;
|
|
1270
|
+
const contextFields = measured.fields;
|
|
1271
|
+
const breakPoints = isSpace ? [] : wordBreakPoints(clean, soft, font, wordSpacing, sc, dashBreaks, touching, cjk, measured.upTo);
|
|
1272
|
+
if (breakPoints.length > 0) {
|
|
1273
|
+
tokens.push({
|
|
1274
|
+
text: clean,
|
|
1275
|
+
bold: span.bold,
|
|
1276
|
+
italic: span.italic,
|
|
1277
|
+
captionLabel: span.captionLabel,
|
|
1278
|
+
...scriptFields,
|
|
1279
|
+
...scFields,
|
|
1280
|
+
...contextFields,
|
|
1281
|
+
kind: 'text',
|
|
1282
|
+
width: cjk ? cjk.width : (inScript ? inScript(clean) : textWidth(clean, font, sc)) + (untracked ? 0 : track(clean)),
|
|
1283
|
+
breakPoints,
|
|
1284
|
+
hyphenWidth: measureTextWidth('-', font) + letterSpacingPx,
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
else {
|
|
1288
|
+
// A word of a joining script loses the soft hyphens it may not
|
|
1289
|
+
// break at (`syllabify`); any other keeps its text as written.
|
|
1290
|
+
const word = clean !== part && !isSpace && joiningScriptIn(part) ? clean : part;
|
|
1291
|
+
tokens.push({
|
|
1292
|
+
text: word,
|
|
1293
|
+
bold: span.bold,
|
|
1294
|
+
italic: span.italic,
|
|
1295
|
+
captionLabel: span.captionLabel,
|
|
1296
|
+
...scriptFields,
|
|
1297
|
+
...(isSpace ? {} : scFields),
|
|
1298
|
+
...contextFields,
|
|
1299
|
+
kind: isSpace ? 'space' : 'text',
|
|
1300
|
+
width: (inScript ? inScript(word) : textWidth(word, font, sc && !isSpace)) + (untracked ? 0 : track(word)),
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1268
1303
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
italic: span.italic,
|
|
1277
|
-
captionLabel: span.captionLabel,
|
|
1278
|
-
...scriptFields,
|
|
1279
|
-
...(isSpace ? {} : scFields),
|
|
1280
|
-
...contextFields,
|
|
1281
|
-
kind: isSpace ? 'space' : 'text',
|
|
1282
|
-
width: (inScript ? inScript(word) : textWidth(word, font, sc && !isSpace)) + (untracked ? 0 : track(word)),
|
|
1283
|
-
});
|
|
1304
|
+
}
|
|
1305
|
+
finally {
|
|
1306
|
+
if (ownLanguage !== undefined) {
|
|
1307
|
+
setMeasureLanguage(languageBefore);
|
|
1308
|
+
if (ownLanguage !== languageBefore)
|
|
1309
|
+
for (let k = spanTokensFrom; k < tokens.length; k++)
|
|
1310
|
+
tokens[k].lang = ownLanguage;
|
|
1284
1311
|
}
|
|
1285
1312
|
}
|
|
1286
1313
|
}
|