@d3plus/core 3.1.2 → 3.1.3
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/README.md +5092 -5092
- package/package.json +8 -8
- package/umd/d3plus-core.full.js +248 -1977
- package/umd/d3plus-core.full.js.map +1 -1
- package/umd/d3plus-core.full.min.js +753 -804
- package/umd/d3plus-core.js +1 -1
- package/umd/d3plus-core.min.js +1 -1
package/umd/d3plus-core.full.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
@d3plus/core v3.1.
|
|
2
|
+
@d3plus/core v3.1.2
|
|
3
3
|
Data visualization made easy. A javascript library that extends the popular D3.js to enable fast and beautiful visualizations.
|
|
4
4
|
Copyright (c) 2026 D3plus - https://d3plus.org
|
|
5
5
|
@license MIT
|
|
@@ -6442,7 +6442,7 @@
|
|
|
6442
6442
|
}
|
|
6443
6443
|
return 'L';
|
|
6444
6444
|
}
|
|
6445
|
-
function computeBidiLevels
|
|
6445
|
+
function computeBidiLevels(str) {
|
|
6446
6446
|
const len = str.length;
|
|
6447
6447
|
if (len === 0) return null;
|
|
6448
6448
|
// eslint-disable-next-line unicorn/no-new-array
|
|
@@ -6557,8 +6557,8 @@
|
|
|
6557
6557
|
}
|
|
6558
6558
|
return levels;
|
|
6559
6559
|
}
|
|
6560
|
-
function computeSegmentLevels
|
|
6561
|
-
const bidiLevels = computeBidiLevels
|
|
6560
|
+
function computeSegmentLevels(normalized, segStarts) {
|
|
6561
|
+
const bidiLevels = computeBidiLevels(normalized);
|
|
6562
6562
|
if (bidiLevels === null) return null;
|
|
6563
6563
|
const segLevels = new Int8Array(segStarts.length);
|
|
6564
6564
|
for(let i = 0; i < segStarts.length; i++){
|
|
@@ -6567,9 +6567,9 @@
|
|
|
6567
6567
|
return segLevels;
|
|
6568
6568
|
}
|
|
6569
6569
|
|
|
6570
|
-
const collapsibleWhitespaceRunRe
|
|
6571
|
-
const needsWhitespaceNormalizationRe
|
|
6572
|
-
function getWhiteSpaceProfile
|
|
6570
|
+
const collapsibleWhitespaceRunRe = /[ \t\n\r\f]+/g;
|
|
6571
|
+
const needsWhitespaceNormalizationRe = /[\t\n\r\f]| {2,}|^ | $/;
|
|
6572
|
+
function getWhiteSpaceProfile(whiteSpace) {
|
|
6573
6573
|
const mode = whiteSpace ?? 'normal';
|
|
6574
6574
|
return mode === 'pre-wrap' ? {
|
|
6575
6575
|
mode,
|
|
@@ -6581,9 +6581,9 @@
|
|
|
6581
6581
|
preserveHardBreaks: false
|
|
6582
6582
|
};
|
|
6583
6583
|
}
|
|
6584
|
-
function normalizeWhitespaceNormal
|
|
6585
|
-
if (!needsWhitespaceNormalizationRe
|
|
6586
|
-
let normalized = text.replace(collapsibleWhitespaceRunRe
|
|
6584
|
+
function normalizeWhitespaceNormal(text) {
|
|
6585
|
+
if (!needsWhitespaceNormalizationRe.test(text)) return text;
|
|
6586
|
+
let normalized = text.replace(collapsibleWhitespaceRunRe, ' ');
|
|
6587
6587
|
if (normalized.charCodeAt(0) === 0x20) {
|
|
6588
6588
|
normalized = normalized.slice(1);
|
|
6589
6589
|
}
|
|
@@ -6592,30 +6592,30 @@
|
|
|
6592
6592
|
}
|
|
6593
6593
|
return normalized;
|
|
6594
6594
|
}
|
|
6595
|
-
function normalizeWhitespacePreWrap
|
|
6595
|
+
function normalizeWhitespacePreWrap(text) {
|
|
6596
6596
|
if (!/[\r\f]/.test(text)) return text.replace(/\r\n/g, '\n');
|
|
6597
6597
|
return text.replace(/\r\n/g, '\n').replace(/[\r\f]/g, '\n');
|
|
6598
6598
|
}
|
|
6599
|
-
let sharedWordSegmenter
|
|
6600
|
-
let segmenterLocale
|
|
6601
|
-
function getSharedWordSegmenter
|
|
6602
|
-
if (sharedWordSegmenter
|
|
6603
|
-
sharedWordSegmenter
|
|
6599
|
+
let sharedWordSegmenter = null;
|
|
6600
|
+
let segmenterLocale;
|
|
6601
|
+
function getSharedWordSegmenter() {
|
|
6602
|
+
if (sharedWordSegmenter === null) {
|
|
6603
|
+
sharedWordSegmenter = new Intl.Segmenter(segmenterLocale, {
|
|
6604
6604
|
granularity: 'word'
|
|
6605
6605
|
});
|
|
6606
6606
|
}
|
|
6607
|
-
return sharedWordSegmenter
|
|
6607
|
+
return sharedWordSegmenter;
|
|
6608
6608
|
}
|
|
6609
|
-
const arabicScriptRe
|
|
6610
|
-
const combiningMarkRe
|
|
6611
|
-
const decimalDigitRe
|
|
6612
|
-
function containsArabicScript
|
|
6613
|
-
return arabicScriptRe
|
|
6609
|
+
const arabicScriptRe = /\p{Script=Arabic}/u;
|
|
6610
|
+
const combiningMarkRe = /\p{M}/u;
|
|
6611
|
+
const decimalDigitRe = /\p{Nd}/u;
|
|
6612
|
+
function containsArabicScript(text) {
|
|
6613
|
+
return arabicScriptRe.test(text);
|
|
6614
6614
|
}
|
|
6615
6615
|
function isCJKCodePoint(codePoint) {
|
|
6616
6616
|
return codePoint >= 0x4E00 && codePoint <= 0x9FFF || codePoint >= 0x3400 && codePoint <= 0x4DBF || codePoint >= 0x20000 && codePoint <= 0x2A6DF || codePoint >= 0x2A700 && codePoint <= 0x2B73F || codePoint >= 0x2B740 && codePoint <= 0x2B81F || codePoint >= 0x2B820 && codePoint <= 0x2CEAF || codePoint >= 0x2CEB0 && codePoint <= 0x2EBEF || codePoint >= 0x2EBF0 && codePoint <= 0x2EE5D || codePoint >= 0x2F800 && codePoint <= 0x2FA1F || codePoint >= 0x30000 && codePoint <= 0x3134F || codePoint >= 0x31350 && codePoint <= 0x323AF || codePoint >= 0x323B0 && codePoint <= 0x33479 || codePoint >= 0xF900 && codePoint <= 0xFAFF || codePoint >= 0x3000 && codePoint <= 0x303F || codePoint >= 0x3040 && codePoint <= 0x309F || codePoint >= 0x30A0 && codePoint <= 0x30FF || codePoint >= 0xAC00 && codePoint <= 0xD7AF || codePoint >= 0xFF00 && codePoint <= 0xFFEF;
|
|
6617
6617
|
}
|
|
6618
|
-
function isCJK
|
|
6618
|
+
function isCJK(s) {
|
|
6619
6619
|
for(let i = 0; i < s.length; i++){
|
|
6620
6620
|
const first = s.charCodeAt(i);
|
|
6621
6621
|
if (first < 0x3000) continue;
|
|
@@ -6634,7 +6634,7 @@
|
|
|
6634
6634
|
}
|
|
6635
6635
|
function endsWithLineStartProhibitedText(text) {
|
|
6636
6636
|
const last = getLastCodePoint(text);
|
|
6637
|
-
return last !== null && (kinsokuStart
|
|
6637
|
+
return last !== null && (kinsokuStart.has(last) || leftStickyPunctuation.has(last));
|
|
6638
6638
|
}
|
|
6639
6639
|
const keepAllGlueChars = new Set([
|
|
6640
6640
|
'\u00A0',
|
|
@@ -6643,7 +6643,7 @@
|
|
|
6643
6643
|
'\uFEFF'
|
|
6644
6644
|
]);
|
|
6645
6645
|
function containsCJKText(text) {
|
|
6646
|
-
return isCJK
|
|
6646
|
+
return isCJK(text);
|
|
6647
6647
|
}
|
|
6648
6648
|
function endsWithKeepAllGlueText(text) {
|
|
6649
6649
|
const last = getLastCodePoint(text);
|
|
@@ -6652,7 +6652,7 @@
|
|
|
6652
6652
|
function canContinueKeepAllTextRun(previousText) {
|
|
6653
6653
|
return !endsWithLineStartProhibitedText(previousText) && !endsWithKeepAllGlueText(previousText);
|
|
6654
6654
|
}
|
|
6655
|
-
const kinsokuStart
|
|
6655
|
+
const kinsokuStart = new Set([
|
|
6656
6656
|
'\uFF0C',
|
|
6657
6657
|
'\uFF0E',
|
|
6658
6658
|
'\uFF01',
|
|
@@ -6680,7 +6680,7 @@
|
|
|
6680
6680
|
'\u30FD',
|
|
6681
6681
|
'\u30FE'
|
|
6682
6682
|
]);
|
|
6683
|
-
const kinsokuEnd
|
|
6683
|
+
const kinsokuEnd = new Set([
|
|
6684
6684
|
'"',
|
|
6685
6685
|
'(',
|
|
6686
6686
|
'[',
|
|
@@ -6700,11 +6700,11 @@
|
|
|
6700
6700
|
'\u3018',
|
|
6701
6701
|
'\u301A'
|
|
6702
6702
|
]);
|
|
6703
|
-
const forwardStickyGlue
|
|
6703
|
+
const forwardStickyGlue = new Set([
|
|
6704
6704
|
"'",
|
|
6705
6705
|
'’'
|
|
6706
6706
|
]);
|
|
6707
|
-
const leftStickyPunctuation
|
|
6707
|
+
const leftStickyPunctuation = new Set([
|
|
6708
6708
|
'.',
|
|
6709
6709
|
',',
|
|
6710
6710
|
'!',
|
|
@@ -6732,16 +6732,16 @@
|
|
|
6732
6732
|
'›',
|
|
6733
6733
|
'…'
|
|
6734
6734
|
]);
|
|
6735
|
-
const arabicNoSpaceTrailingPunctuation
|
|
6735
|
+
const arabicNoSpaceTrailingPunctuation = new Set([
|
|
6736
6736
|
':',
|
|
6737
6737
|
'.',
|
|
6738
6738
|
'\u060C',
|
|
6739
6739
|
'\u061B'
|
|
6740
6740
|
]);
|
|
6741
|
-
const myanmarMedialGlue
|
|
6741
|
+
const myanmarMedialGlue = new Set([
|
|
6742
6742
|
'\u104F'
|
|
6743
6743
|
]);
|
|
6744
|
-
const closingQuoteChars
|
|
6744
|
+
const closingQuoteChars = new Set([
|
|
6745
6745
|
'”',
|
|
6746
6746
|
'’',
|
|
6747
6747
|
'»',
|
|
@@ -6754,37 +6754,37 @@
|
|
|
6754
6754
|
'\u3015',
|
|
6755
6755
|
'\uFF09'
|
|
6756
6756
|
]);
|
|
6757
|
-
function isLeftStickyPunctuationSegment
|
|
6758
|
-
if (isEscapedQuoteClusterSegment
|
|
6757
|
+
function isLeftStickyPunctuationSegment(segment) {
|
|
6758
|
+
if (isEscapedQuoteClusterSegment(segment)) return true;
|
|
6759
6759
|
let sawPunctuation = false;
|
|
6760
6760
|
for (const ch of segment){
|
|
6761
|
-
if (leftStickyPunctuation
|
|
6761
|
+
if (leftStickyPunctuation.has(ch)) {
|
|
6762
6762
|
sawPunctuation = true;
|
|
6763
6763
|
continue;
|
|
6764
6764
|
}
|
|
6765
|
-
if (sawPunctuation && combiningMarkRe
|
|
6765
|
+
if (sawPunctuation && combiningMarkRe.test(ch)) continue;
|
|
6766
6766
|
return false;
|
|
6767
6767
|
}
|
|
6768
6768
|
return sawPunctuation;
|
|
6769
6769
|
}
|
|
6770
|
-
function isCJKLineStartProhibitedSegment
|
|
6770
|
+
function isCJKLineStartProhibitedSegment(segment) {
|
|
6771
6771
|
for (const ch of segment){
|
|
6772
|
-
if (!kinsokuStart
|
|
6772
|
+
if (!kinsokuStart.has(ch) && !leftStickyPunctuation.has(ch)) return false;
|
|
6773
6773
|
}
|
|
6774
6774
|
return segment.length > 0;
|
|
6775
6775
|
}
|
|
6776
|
-
function isForwardStickyClusterSegment
|
|
6777
|
-
if (isEscapedQuoteClusterSegment
|
|
6776
|
+
function isForwardStickyClusterSegment(segment) {
|
|
6777
|
+
if (isEscapedQuoteClusterSegment(segment)) return true;
|
|
6778
6778
|
for (const ch of segment){
|
|
6779
|
-
if (!kinsokuEnd
|
|
6779
|
+
if (!kinsokuEnd.has(ch) && !forwardStickyGlue.has(ch) && !combiningMarkRe.test(ch)) return false;
|
|
6780
6780
|
}
|
|
6781
6781
|
return segment.length > 0;
|
|
6782
6782
|
}
|
|
6783
|
-
function isEscapedQuoteClusterSegment
|
|
6783
|
+
function isEscapedQuoteClusterSegment(segment) {
|
|
6784
6784
|
let sawQuote = false;
|
|
6785
6785
|
for (const ch of segment){
|
|
6786
|
-
if (ch === '\\' || combiningMarkRe
|
|
6787
|
-
if (kinsokuEnd
|
|
6786
|
+
if (ch === '\\' || combiningMarkRe.test(ch)) continue;
|
|
6787
|
+
if (kinsokuEnd.has(ch) || leftStickyPunctuation.has(ch) || forwardStickyGlue.has(ch)) {
|
|
6788
6788
|
sawQuote = true;
|
|
6789
6789
|
continue;
|
|
6790
6790
|
}
|
|
@@ -6807,16 +6807,16 @@
|
|
|
6807
6807
|
const start = previousCodePointStart(text, text.length);
|
|
6808
6808
|
return text.slice(start);
|
|
6809
6809
|
}
|
|
6810
|
-
function splitTrailingForwardStickyCluster
|
|
6810
|
+
function splitTrailingForwardStickyCluster(text) {
|
|
6811
6811
|
const chars = Array.from(text);
|
|
6812
6812
|
let splitIndex = chars.length;
|
|
6813
6813
|
while(splitIndex > 0){
|
|
6814
6814
|
const ch = chars[splitIndex - 1];
|
|
6815
|
-
if (combiningMarkRe
|
|
6815
|
+
if (combiningMarkRe.test(ch)) {
|
|
6816
6816
|
splitIndex--;
|
|
6817
6817
|
continue;
|
|
6818
6818
|
}
|
|
6819
|
-
if (kinsokuEnd
|
|
6819
|
+
if (kinsokuEnd.has(ch) || forwardStickyGlue.has(ch)) {
|
|
6820
6820
|
splitIndex--;
|
|
6821
6821
|
continue;
|
|
6822
6822
|
}
|
|
@@ -6842,13 +6842,13 @@
|
|
|
6842
6842
|
return materialized;
|
|
6843
6843
|
}
|
|
6844
6844
|
function hasArabicNoSpacePunctuation(containsArabic, lastCodePoint) {
|
|
6845
|
-
return containsArabic && lastCodePoint !== null && arabicNoSpaceTrailingPunctuation
|
|
6845
|
+
return containsArabic && lastCodePoint !== null && arabicNoSpaceTrailingPunctuation.has(lastCodePoint);
|
|
6846
6846
|
}
|
|
6847
|
-
function endsWithMyanmarMedialGlue
|
|
6847
|
+
function endsWithMyanmarMedialGlue(segment) {
|
|
6848
6848
|
const lastCodePoint = getLastCodePoint(segment);
|
|
6849
|
-
return lastCodePoint !== null && myanmarMedialGlue
|
|
6849
|
+
return lastCodePoint !== null && myanmarMedialGlue.has(lastCodePoint);
|
|
6850
6850
|
}
|
|
6851
|
-
function splitLeadingSpaceAndMarks
|
|
6851
|
+
function splitLeadingSpaceAndMarks(segment) {
|
|
6852
6852
|
if (segment.length < 2 || segment[0] !== ' ') return null;
|
|
6853
6853
|
const marks = segment.slice(1);
|
|
6854
6854
|
if (/^\p{M}+$/u.test(marks)) {
|
|
@@ -6859,18 +6859,18 @@
|
|
|
6859
6859
|
}
|
|
6860
6860
|
return null;
|
|
6861
6861
|
}
|
|
6862
|
-
function endsWithClosingQuote
|
|
6862
|
+
function endsWithClosingQuote(text) {
|
|
6863
6863
|
let end = text.length;
|
|
6864
6864
|
while(end > 0){
|
|
6865
6865
|
const start = previousCodePointStart(text, end);
|
|
6866
6866
|
const ch = text.slice(start, end);
|
|
6867
|
-
if (closingQuoteChars
|
|
6868
|
-
if (!leftStickyPunctuation
|
|
6867
|
+
if (closingQuoteChars.has(ch)) return true;
|
|
6868
|
+
if (!leftStickyPunctuation.has(ch)) return false;
|
|
6869
6869
|
end = start;
|
|
6870
6870
|
}
|
|
6871
6871
|
return false;
|
|
6872
6872
|
}
|
|
6873
|
-
function classifySegmentBreakChar
|
|
6873
|
+
function classifySegmentBreakChar(ch, whiteSpaceProfile) {
|
|
6874
6874
|
if (whiteSpaceProfile.preserveOrdinarySpaces || whiteSpaceProfile.preserveHardBreaks) {
|
|
6875
6875
|
if (ch === ' ') return 'preserved-space';
|
|
6876
6876
|
if (ch === '\t') return 'tab';
|
|
@@ -6897,7 +6897,7 @@
|
|
|
6897
6897
|
parts.push(tail);
|
|
6898
6898
|
return joinTextParts(parts);
|
|
6899
6899
|
}
|
|
6900
|
-
function splitSegmentByBreakKind
|
|
6900
|
+
function splitSegmentByBreakKind(segment, isWordLike, start, whiteSpaceProfile) {
|
|
6901
6901
|
if (!breakCharRe.test(segment)) {
|
|
6902
6902
|
return [
|
|
6903
6903
|
{
|
|
@@ -6915,7 +6915,7 @@
|
|
|
6915
6915
|
let currentWordLike = false;
|
|
6916
6916
|
let offset = 0;
|
|
6917
6917
|
for (const ch of segment){
|
|
6918
|
-
const kind = classifySegmentBreakChar
|
|
6918
|
+
const kind = classifySegmentBreakChar(ch, whiteSpaceProfile);
|
|
6919
6919
|
const wordLike = kind === 'text' && isWordLike;
|
|
6920
6920
|
if (currentKind !== null && kind === currentKind && wordLike === currentWordLike) {
|
|
6921
6921
|
currentTextParts.push(ch);
|
|
@@ -6948,30 +6948,30 @@
|
|
|
6948
6948
|
}
|
|
6949
6949
|
return pieces;
|
|
6950
6950
|
}
|
|
6951
|
-
function isTextRunBoundary
|
|
6951
|
+
function isTextRunBoundary(kind) {
|
|
6952
6952
|
return kind === 'space' || kind === 'preserved-space' || kind === 'zero-width-break' || kind === 'hard-break';
|
|
6953
6953
|
}
|
|
6954
|
-
const urlSchemeSegmentRe
|
|
6955
|
-
function isUrlLikeRunStart
|
|
6954
|
+
const urlSchemeSegmentRe = /^[A-Za-z][A-Za-z0-9+.-]*:$/;
|
|
6955
|
+
function isUrlLikeRunStart(segmentation, index) {
|
|
6956
6956
|
const text = segmentation.texts[index];
|
|
6957
6957
|
if (text.startsWith('www.')) return true;
|
|
6958
|
-
return urlSchemeSegmentRe
|
|
6958
|
+
return urlSchemeSegmentRe.test(text) && index + 1 < segmentation.len && segmentation.kinds[index + 1] === 'text' && segmentation.texts[index + 1] === '//';
|
|
6959
6959
|
}
|
|
6960
|
-
function isUrlQueryBoundarySegment
|
|
6960
|
+
function isUrlQueryBoundarySegment(text) {
|
|
6961
6961
|
return text.includes('?') && (text.includes('://') || text.startsWith('www.'));
|
|
6962
6962
|
}
|
|
6963
|
-
function mergeUrlLikeRuns
|
|
6963
|
+
function mergeUrlLikeRuns(segmentation) {
|
|
6964
6964
|
const texts = segmentation.texts.slice();
|
|
6965
6965
|
const isWordLike = segmentation.isWordLike.slice();
|
|
6966
6966
|
const kinds = segmentation.kinds.slice();
|
|
6967
6967
|
const starts = segmentation.starts.slice();
|
|
6968
6968
|
for(let i = 0; i < segmentation.len; i++){
|
|
6969
|
-
if (kinds[i] !== 'text' || !isUrlLikeRunStart
|
|
6969
|
+
if (kinds[i] !== 'text' || !isUrlLikeRunStart(segmentation, i)) continue;
|
|
6970
6970
|
const mergedParts = [
|
|
6971
6971
|
texts[i]
|
|
6972
6972
|
];
|
|
6973
6973
|
let j = i + 1;
|
|
6974
|
-
while(j < segmentation.len && !isTextRunBoundary
|
|
6974
|
+
while(j < segmentation.len && !isTextRunBoundary(kinds[j])){
|
|
6975
6975
|
mergedParts.push(texts[j]);
|
|
6976
6976
|
isWordLike[i] = true;
|
|
6977
6977
|
const endsQueryPrefix = texts[j].includes('?');
|
|
@@ -7006,7 +7006,7 @@
|
|
|
7006
7006
|
starts
|
|
7007
7007
|
};
|
|
7008
7008
|
}
|
|
7009
|
-
function mergeUrlQueryRuns
|
|
7009
|
+
function mergeUrlQueryRuns(segmentation) {
|
|
7010
7010
|
const texts = [];
|
|
7011
7011
|
const isWordLike = [];
|
|
7012
7012
|
const kinds = [];
|
|
@@ -7017,15 +7017,15 @@
|
|
|
7017
7017
|
isWordLike.push(segmentation.isWordLike[i]);
|
|
7018
7018
|
kinds.push(segmentation.kinds[i]);
|
|
7019
7019
|
starts.push(segmentation.starts[i]);
|
|
7020
|
-
if (!isUrlQueryBoundarySegment
|
|
7020
|
+
if (!isUrlQueryBoundarySegment(text)) continue;
|
|
7021
7021
|
const nextIndex = i + 1;
|
|
7022
|
-
if (nextIndex >= segmentation.len || isTextRunBoundary
|
|
7022
|
+
if (nextIndex >= segmentation.len || isTextRunBoundary(segmentation.kinds[nextIndex])) {
|
|
7023
7023
|
continue;
|
|
7024
7024
|
}
|
|
7025
7025
|
const queryParts = [];
|
|
7026
7026
|
const queryStart = segmentation.starts[nextIndex];
|
|
7027
7027
|
let j = nextIndex;
|
|
7028
|
-
while(j < segmentation.len && !isTextRunBoundary
|
|
7028
|
+
while(j < segmentation.len && !isTextRunBoundary(segmentation.kinds[j])){
|
|
7029
7029
|
queryParts.push(segmentation.texts[j]);
|
|
7030
7030
|
j++;
|
|
7031
7031
|
}
|
|
@@ -7045,7 +7045,7 @@
|
|
|
7045
7045
|
starts
|
|
7046
7046
|
};
|
|
7047
7047
|
}
|
|
7048
|
-
const numericJoinerChars
|
|
7048
|
+
const numericJoinerChars = new Set([
|
|
7049
7049
|
':',
|
|
7050
7050
|
'-',
|
|
7051
7051
|
'/',
|
|
@@ -7056,23 +7056,23 @@
|
|
|
7056
7056
|
'\u2013',
|
|
7057
7057
|
'\u2014'
|
|
7058
7058
|
]);
|
|
7059
|
-
const asciiPunctuationChainSegmentRe
|
|
7060
|
-
const asciiPunctuationChainTrailingJoinersRe
|
|
7061
|
-
function segmentContainsDecimalDigit
|
|
7059
|
+
const asciiPunctuationChainSegmentRe = /^[A-Za-z0-9_]+[,:;]*$/;
|
|
7060
|
+
const asciiPunctuationChainTrailingJoinersRe = /[,:;]+$/;
|
|
7061
|
+
function segmentContainsDecimalDigit(text) {
|
|
7062
7062
|
for (const ch of text){
|
|
7063
|
-
if (decimalDigitRe
|
|
7063
|
+
if (decimalDigitRe.test(ch)) return true;
|
|
7064
7064
|
}
|
|
7065
7065
|
return false;
|
|
7066
7066
|
}
|
|
7067
|
-
function isNumericRunSegment
|
|
7067
|
+
function isNumericRunSegment(text) {
|
|
7068
7068
|
if (text.length === 0) return false;
|
|
7069
7069
|
for (const ch of text){
|
|
7070
|
-
if (decimalDigitRe
|
|
7070
|
+
if (decimalDigitRe.test(ch) || numericJoinerChars.has(ch)) continue;
|
|
7071
7071
|
return false;
|
|
7072
7072
|
}
|
|
7073
7073
|
return true;
|
|
7074
7074
|
}
|
|
7075
|
-
function mergeNumericRuns
|
|
7075
|
+
function mergeNumericRuns(segmentation) {
|
|
7076
7076
|
const texts = [];
|
|
7077
7077
|
const isWordLike = [];
|
|
7078
7078
|
const kinds = [];
|
|
@@ -7080,12 +7080,12 @@
|
|
|
7080
7080
|
for(let i = 0; i < segmentation.len; i++){
|
|
7081
7081
|
const text = segmentation.texts[i];
|
|
7082
7082
|
const kind = segmentation.kinds[i];
|
|
7083
|
-
if (kind === 'text' && isNumericRunSegment
|
|
7083
|
+
if (kind === 'text' && isNumericRunSegment(text) && segmentContainsDecimalDigit(text)) {
|
|
7084
7084
|
const mergedParts = [
|
|
7085
7085
|
text
|
|
7086
7086
|
];
|
|
7087
7087
|
let j = i + 1;
|
|
7088
|
-
while(j < segmentation.len && segmentation.kinds[j] === 'text' && isNumericRunSegment
|
|
7088
|
+
while(j < segmentation.len && segmentation.kinds[j] === 'text' && isNumericRunSegment(segmentation.texts[j])){
|
|
7089
7089
|
mergedParts.push(segmentation.texts[j]);
|
|
7090
7090
|
j++;
|
|
7091
7091
|
}
|
|
@@ -7109,7 +7109,7 @@
|
|
|
7109
7109
|
starts
|
|
7110
7110
|
};
|
|
7111
7111
|
}
|
|
7112
|
-
function mergeAsciiPunctuationChains
|
|
7112
|
+
function mergeAsciiPunctuationChains(segmentation) {
|
|
7113
7113
|
const texts = [];
|
|
7114
7114
|
const isWordLike = [];
|
|
7115
7115
|
const kinds = [];
|
|
@@ -7118,16 +7118,16 @@
|
|
|
7118
7118
|
const text = segmentation.texts[i];
|
|
7119
7119
|
const kind = segmentation.kinds[i];
|
|
7120
7120
|
const wordLike = segmentation.isWordLike[i];
|
|
7121
|
-
if (kind === 'text' && wordLike && asciiPunctuationChainSegmentRe
|
|
7121
|
+
if (kind === 'text' && wordLike && asciiPunctuationChainSegmentRe.test(text)) {
|
|
7122
7122
|
const mergedParts = [
|
|
7123
7123
|
text
|
|
7124
7124
|
];
|
|
7125
|
-
let endsWithJoiners = asciiPunctuationChainTrailingJoinersRe
|
|
7125
|
+
let endsWithJoiners = asciiPunctuationChainTrailingJoinersRe.test(text);
|
|
7126
7126
|
let j = i + 1;
|
|
7127
|
-
while(endsWithJoiners && j < segmentation.len && segmentation.kinds[j] === 'text' && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe
|
|
7127
|
+
while(endsWithJoiners && j < segmentation.len && segmentation.kinds[j] === 'text' && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe.test(segmentation.texts[j])){
|
|
7128
7128
|
const nextText = segmentation.texts[j];
|
|
7129
7129
|
mergedParts.push(nextText);
|
|
7130
|
-
endsWithJoiners = asciiPunctuationChainTrailingJoinersRe
|
|
7130
|
+
endsWithJoiners = asciiPunctuationChainTrailingJoinersRe.test(nextText);
|
|
7131
7131
|
j++;
|
|
7132
7132
|
}
|
|
7133
7133
|
texts.push(joinTextParts(mergedParts));
|
|
@@ -7150,7 +7150,7 @@
|
|
|
7150
7150
|
starts
|
|
7151
7151
|
};
|
|
7152
7152
|
}
|
|
7153
|
-
function splitHyphenatedNumericRuns
|
|
7153
|
+
function splitHyphenatedNumericRuns(segmentation) {
|
|
7154
7154
|
const texts = [];
|
|
7155
7155
|
const isWordLike = [];
|
|
7156
7156
|
const kinds = [];
|
|
@@ -7163,7 +7163,7 @@
|
|
|
7163
7163
|
for(let j = 0; j < parts.length; j++){
|
|
7164
7164
|
const part = parts[j];
|
|
7165
7165
|
if (!shouldSplit) break;
|
|
7166
|
-
if (part.length === 0 || !segmentContainsDecimalDigit
|
|
7166
|
+
if (part.length === 0 || !segmentContainsDecimalDigit(part) || !isNumericRunSegment(part)) {
|
|
7167
7167
|
shouldSplit = false;
|
|
7168
7168
|
}
|
|
7169
7169
|
}
|
|
@@ -7194,7 +7194,7 @@
|
|
|
7194
7194
|
starts
|
|
7195
7195
|
};
|
|
7196
7196
|
}
|
|
7197
|
-
function mergeGlueConnectedTextRuns
|
|
7197
|
+
function mergeGlueConnectedTextRuns(segmentation) {
|
|
7198
7198
|
const texts = [];
|
|
7199
7199
|
const isWordLike = [];
|
|
7200
7200
|
const kinds = [];
|
|
@@ -7265,15 +7265,15 @@
|
|
|
7265
7265
|
starts
|
|
7266
7266
|
};
|
|
7267
7267
|
}
|
|
7268
|
-
function carryTrailingForwardStickyAcrossCJKBoundary
|
|
7268
|
+
function carryTrailingForwardStickyAcrossCJKBoundary(segmentation) {
|
|
7269
7269
|
const texts = segmentation.texts.slice();
|
|
7270
7270
|
const isWordLike = segmentation.isWordLike.slice();
|
|
7271
7271
|
const kinds = segmentation.kinds.slice();
|
|
7272
7272
|
const starts = segmentation.starts.slice();
|
|
7273
7273
|
for(let i = 0; i < texts.length - 1; i++){
|
|
7274
7274
|
if (kinds[i] !== 'text' || kinds[i + 1] !== 'text') continue;
|
|
7275
|
-
if (!isCJK
|
|
7276
|
-
const split = splitTrailingForwardStickyCluster
|
|
7275
|
+
if (!isCJK(texts[i]) || !isCJK(texts[i + 1])) continue;
|
|
7276
|
+
const split = splitTrailingForwardStickyCluster(texts[i]);
|
|
7277
7277
|
if (split === null) continue;
|
|
7278
7278
|
texts[i] = split.head;
|
|
7279
7279
|
texts[i + 1] = split.tail + texts[i + 1];
|
|
@@ -7287,8 +7287,8 @@
|
|
|
7287
7287
|
starts
|
|
7288
7288
|
};
|
|
7289
7289
|
}
|
|
7290
|
-
function buildMergedSegmentation
|
|
7291
|
-
const wordSegmenter = getSharedWordSegmenter
|
|
7290
|
+
function buildMergedSegmentation(normalized, profile, whiteSpaceProfile) {
|
|
7291
|
+
const wordSegmenter = getSharedWordSegmenter();
|
|
7292
7292
|
let mergedLen = 0;
|
|
7293
7293
|
const mergedTexts = [];
|
|
7294
7294
|
const mergedTextParts = [];
|
|
@@ -7305,14 +7305,14 @@
|
|
|
7305
7305
|
const mergedEndsWithMyanmarMedialGlue = [];
|
|
7306
7306
|
const mergedHasArabicNoSpacePunctuation = [];
|
|
7307
7307
|
for (const s of wordSegmenter.segment(normalized)){
|
|
7308
|
-
for (const piece of splitSegmentByBreakKind
|
|
7308
|
+
for (const piece of splitSegmentByBreakKind(s.segment, s.isWordLike ?? false, s.index, whiteSpaceProfile)){
|
|
7309
7309
|
const isText = piece.kind === 'text';
|
|
7310
7310
|
const repeatableSingleCharRunChar = getRepeatableSingleCharRunChar(piece.text, piece.isWordLike, piece.kind);
|
|
7311
|
-
const pieceContainsCJK = isCJK
|
|
7312
|
-
const pieceContainsArabicScript = containsArabicScript
|
|
7311
|
+
const pieceContainsCJK = isCJK(piece.text);
|
|
7312
|
+
const pieceContainsArabicScript = containsArabicScript(piece.text);
|
|
7313
7313
|
const pieceLastCodePoint = getLastCodePoint(piece.text);
|
|
7314
|
-
const pieceEndsWithClosingQuote = endsWithClosingQuote
|
|
7315
|
-
const pieceEndsWithMyanmarMedialGlue = endsWithMyanmarMedialGlue
|
|
7314
|
+
const pieceEndsWithClosingQuote = endsWithClosingQuote(piece.text);
|
|
7315
|
+
const pieceEndsWithMyanmarMedialGlue = endsWithMyanmarMedialGlue(piece.text);
|
|
7316
7316
|
const prevIndex = mergedLen - 1;
|
|
7317
7317
|
function appendPieceToPrevious() {
|
|
7318
7318
|
if (mergedSingleCharRunChars[prevIndex] !== null) {
|
|
@@ -7333,7 +7333,7 @@
|
|
|
7333
7333
|
// that depend on the immediately preceding text run.
|
|
7334
7334
|
if (profile.carryCJKAfterClosingQuote && isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && pieceContainsCJK && mergedContainsCJK[prevIndex] && mergedEndsWithClosingQuote[prevIndex]) {
|
|
7335
7335
|
appendPieceToPrevious();
|
|
7336
|
-
} else if (isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && isCJKLineStartProhibitedSegment
|
|
7336
|
+
} else if (isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && isCJKLineStartProhibitedSegment(piece.text) && mergedContainsCJK[prevIndex]) {
|
|
7337
7337
|
appendPieceToPrevious();
|
|
7338
7338
|
} else if (isText && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && mergedEndsWithMyanmarMedialGlue[prevIndex]) {
|
|
7339
7339
|
appendPieceToPrevious();
|
|
@@ -7342,7 +7342,7 @@
|
|
|
7342
7342
|
mergedWordLike[prevIndex] = true;
|
|
7343
7343
|
} else if (repeatableSingleCharRunChar !== null && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && mergedSingleCharRunChars[prevIndex] === repeatableSingleCharRunChar) {
|
|
7344
7344
|
mergedSingleCharRunLengths[prevIndex] = (mergedSingleCharRunLengths[prevIndex] ?? 1) + 1;
|
|
7345
|
-
} else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && (isLeftStickyPunctuationSegment
|
|
7345
|
+
} else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[prevIndex] === 'text' && (isLeftStickyPunctuationSegment(piece.text) || piece.text === '-' && mergedWordLike[prevIndex])) {
|
|
7346
7346
|
appendPieceToPrevious();
|
|
7347
7347
|
} else {
|
|
7348
7348
|
mergedTexts[mergedLen] = piece.text;
|
|
@@ -7374,7 +7374,7 @@
|
|
|
7374
7374
|
// quote glue, forward-sticky carry, compaction, then the broader URL/numeric
|
|
7375
7375
|
// and Arabic-leading-mark fixes.
|
|
7376
7376
|
for(let i = 1; i < mergedLen; i++){
|
|
7377
|
-
if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isEscapedQuoteClusterSegment
|
|
7377
|
+
if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isEscapedQuoteClusterSegment(mergedTexts[i]) && mergedKinds[i - 1] === 'text') {
|
|
7378
7378
|
mergedTexts[i - 1] += mergedTexts[i];
|
|
7379
7379
|
mergedWordLike[i - 1] = mergedWordLike[i - 1] || mergedWordLike[i];
|
|
7380
7380
|
mergedTexts[i] = '';
|
|
@@ -7387,7 +7387,7 @@
|
|
|
7387
7387
|
for(let i = mergedLen - 1; i >= 0; i--){
|
|
7388
7388
|
const text = mergedTexts[i];
|
|
7389
7389
|
if (text.length === 0) continue;
|
|
7390
|
-
if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isForwardStickyClusterSegment
|
|
7390
|
+
if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isForwardStickyClusterSegment(text) && nextLiveIndex >= 0 && mergedKinds[nextLiveIndex] === 'text') {
|
|
7391
7391
|
const prefixParts = forwardStickyPrefixParts[nextLiveIndex] ?? [];
|
|
7392
7392
|
prefixParts.push(text);
|
|
7393
7393
|
forwardStickyPrefixParts[nextLiveIndex] = prefixParts;
|
|
@@ -7418,18 +7418,18 @@
|
|
|
7418
7418
|
mergedWordLike.length = compactLen;
|
|
7419
7419
|
mergedKinds.length = compactLen;
|
|
7420
7420
|
mergedStarts.length = compactLen;
|
|
7421
|
-
const compacted = mergeGlueConnectedTextRuns
|
|
7421
|
+
const compacted = mergeGlueConnectedTextRuns({
|
|
7422
7422
|
len: compactLen,
|
|
7423
7423
|
texts: mergedTexts,
|
|
7424
7424
|
isWordLike: mergedWordLike,
|
|
7425
7425
|
kinds: mergedKinds,
|
|
7426
7426
|
starts: mergedStarts
|
|
7427
7427
|
});
|
|
7428
|
-
const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary
|
|
7428
|
+
const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary(mergeAsciiPunctuationChains(splitHyphenatedNumericRuns(mergeNumericRuns(mergeUrlQueryRuns(mergeUrlLikeRuns(compacted))))));
|
|
7429
7429
|
for(let i = 0; i < withMergedUrls.len - 1; i++){
|
|
7430
|
-
const split = splitLeadingSpaceAndMarks
|
|
7430
|
+
const split = splitLeadingSpaceAndMarks(withMergedUrls.texts[i]);
|
|
7431
7431
|
if (split === null) continue;
|
|
7432
|
-
if (withMergedUrls.kinds[i] !== 'space' && withMergedUrls.kinds[i] !== 'preserved-space' || withMergedUrls.kinds[i + 1] !== 'text' || !containsArabicScript
|
|
7432
|
+
if (withMergedUrls.kinds[i] !== 'space' && withMergedUrls.kinds[i] !== 'preserved-space' || withMergedUrls.kinds[i + 1] !== 'text' || !containsArabicScript(withMergedUrls.texts[i + 1])) {
|
|
7433
7433
|
continue;
|
|
7434
7434
|
}
|
|
7435
7435
|
withMergedUrls.texts[i] = split.space;
|
|
@@ -7440,7 +7440,7 @@
|
|
|
7440
7440
|
}
|
|
7441
7441
|
return withMergedUrls;
|
|
7442
7442
|
}
|
|
7443
|
-
function compileAnalysisChunks
|
|
7443
|
+
function compileAnalysisChunks(segmentation, whiteSpaceProfile) {
|
|
7444
7444
|
if (segmentation.len === 0) return [];
|
|
7445
7445
|
if (!whiteSpaceProfile.preserveHardBreaks) {
|
|
7446
7446
|
return [
|
|
@@ -7530,9 +7530,9 @@
|
|
|
7530
7530
|
starts
|
|
7531
7531
|
};
|
|
7532
7532
|
}
|
|
7533
|
-
function analyzeText
|
|
7534
|
-
const whiteSpaceProfile = getWhiteSpaceProfile
|
|
7535
|
-
const normalized = whiteSpaceProfile.mode === 'pre-wrap' ? normalizeWhitespacePreWrap
|
|
7533
|
+
function analyzeText(text, profile, whiteSpace = 'normal', wordBreak = 'normal') {
|
|
7534
|
+
const whiteSpaceProfile = getWhiteSpaceProfile(whiteSpace);
|
|
7535
|
+
const normalized = whiteSpaceProfile.mode === 'pre-wrap' ? normalizeWhitespacePreWrap(text) : normalizeWhitespaceNormal(text);
|
|
7536
7536
|
if (normalized.length === 0) {
|
|
7537
7537
|
return {
|
|
7538
7538
|
normalized,
|
|
@@ -7544,103 +7544,103 @@
|
|
|
7544
7544
|
starts: []
|
|
7545
7545
|
};
|
|
7546
7546
|
}
|
|
7547
|
-
const segmentation = wordBreak === 'keep-all' ? mergeKeepAllTextSegments(buildMergedSegmentation
|
|
7547
|
+
const segmentation = wordBreak === 'keep-all' ? mergeKeepAllTextSegments(buildMergedSegmentation(normalized, profile, whiteSpaceProfile)) : buildMergedSegmentation(normalized, profile, whiteSpaceProfile);
|
|
7548
7548
|
return {
|
|
7549
7549
|
normalized,
|
|
7550
|
-
chunks: compileAnalysisChunks
|
|
7550
|
+
chunks: compileAnalysisChunks(segmentation, whiteSpaceProfile),
|
|
7551
7551
|
...segmentation
|
|
7552
7552
|
};
|
|
7553
7553
|
}
|
|
7554
7554
|
|
|
7555
|
-
let measureContext
|
|
7556
|
-
const segmentMetricCaches
|
|
7557
|
-
let cachedEngineProfile
|
|
7555
|
+
let measureContext = null;
|
|
7556
|
+
const segmentMetricCaches = new Map();
|
|
7557
|
+
let cachedEngineProfile = null;
|
|
7558
7558
|
// Safari's prefix-fit policy is useful for ordinary word-sized runs, but letting
|
|
7559
7559
|
// it measure every growing prefix of a giant segment recreates a pathological
|
|
7560
7560
|
// superlinear prepare-time path. Past this size, switch to the cheaper
|
|
7561
7561
|
// pair-context model and keep the public behavior linear.
|
|
7562
7562
|
const MAX_PREFIX_FIT_GRAPHEMES = 96;
|
|
7563
|
-
const emojiPresentationRe
|
|
7564
|
-
const maybeEmojiRe
|
|
7565
|
-
let sharedGraphemeSegmenter$
|
|
7566
|
-
const emojiCorrectionCache
|
|
7567
|
-
function getMeasureContext
|
|
7568
|
-
if (measureContext
|
|
7563
|
+
const emojiPresentationRe = /\p{Emoji_Presentation}/u;
|
|
7564
|
+
const maybeEmojiRe = /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Regional_Indicator}\uFE0F\u20E3]/u;
|
|
7565
|
+
let sharedGraphemeSegmenter$1 = null;
|
|
7566
|
+
const emojiCorrectionCache = new Map();
|
|
7567
|
+
function getMeasureContext() {
|
|
7568
|
+
if (measureContext !== null) return measureContext;
|
|
7569
7569
|
if (typeof OffscreenCanvas !== 'undefined') {
|
|
7570
|
-
measureContext
|
|
7571
|
-
return measureContext
|
|
7570
|
+
measureContext = new OffscreenCanvas(1, 1).getContext('2d');
|
|
7571
|
+
return measureContext;
|
|
7572
7572
|
}
|
|
7573
7573
|
if (typeof document !== 'undefined') {
|
|
7574
|
-
measureContext
|
|
7575
|
-
return measureContext
|
|
7574
|
+
measureContext = document.createElement('canvas').getContext('2d');
|
|
7575
|
+
return measureContext;
|
|
7576
7576
|
}
|
|
7577
7577
|
throw new Error('Text measurement requires OffscreenCanvas or a DOM canvas context.');
|
|
7578
7578
|
}
|
|
7579
|
-
function getSegmentMetricCache
|
|
7580
|
-
let cache = segmentMetricCaches
|
|
7579
|
+
function getSegmentMetricCache(font) {
|
|
7580
|
+
let cache = segmentMetricCaches.get(font);
|
|
7581
7581
|
if (!cache) {
|
|
7582
7582
|
cache = new Map();
|
|
7583
|
-
segmentMetricCaches
|
|
7583
|
+
segmentMetricCaches.set(font, cache);
|
|
7584
7584
|
}
|
|
7585
7585
|
return cache;
|
|
7586
7586
|
}
|
|
7587
|
-
function getSegmentMetrics
|
|
7587
|
+
function getSegmentMetrics(seg, cache) {
|
|
7588
7588
|
let metrics = cache.get(seg);
|
|
7589
7589
|
if (metrics === undefined) {
|
|
7590
|
-
const ctx = getMeasureContext
|
|
7590
|
+
const ctx = getMeasureContext();
|
|
7591
7591
|
metrics = {
|
|
7592
7592
|
width: ctx.measureText(seg).width,
|
|
7593
|
-
containsCJK: isCJK
|
|
7593
|
+
containsCJK: isCJK(seg)
|
|
7594
7594
|
};
|
|
7595
7595
|
cache.set(seg, metrics);
|
|
7596
7596
|
}
|
|
7597
7597
|
return metrics;
|
|
7598
7598
|
}
|
|
7599
|
-
function getEngineProfile
|
|
7600
|
-
if (cachedEngineProfile
|
|
7599
|
+
function getEngineProfile() {
|
|
7600
|
+
if (cachedEngineProfile !== null) return cachedEngineProfile;
|
|
7601
7601
|
if (typeof navigator === 'undefined') {
|
|
7602
|
-
cachedEngineProfile
|
|
7602
|
+
cachedEngineProfile = {
|
|
7603
7603
|
lineFitEpsilon: 0.005,
|
|
7604
7604
|
carryCJKAfterClosingQuote: false,
|
|
7605
7605
|
preferPrefixWidthsForBreakableRuns: false,
|
|
7606
7606
|
preferEarlySoftHyphenBreak: false
|
|
7607
7607
|
};
|
|
7608
|
-
return cachedEngineProfile
|
|
7608
|
+
return cachedEngineProfile;
|
|
7609
7609
|
}
|
|
7610
7610
|
const ua = navigator.userAgent;
|
|
7611
7611
|
const vendor = navigator.vendor;
|
|
7612
7612
|
const isSafari = vendor === 'Apple Computer, Inc.' && ua.includes('Safari/') && !ua.includes('Chrome/') && !ua.includes('Chromium/') && !ua.includes('CriOS/') && !ua.includes('FxiOS/') && !ua.includes('EdgiOS/');
|
|
7613
7613
|
const isChromium = ua.includes('Chrome/') || ua.includes('Chromium/') || ua.includes('CriOS/') || ua.includes('Edg/');
|
|
7614
|
-
cachedEngineProfile
|
|
7614
|
+
cachedEngineProfile = {
|
|
7615
7615
|
lineFitEpsilon: isSafari ? 1 / 64 : 0.005,
|
|
7616
7616
|
carryCJKAfterClosingQuote: isChromium,
|
|
7617
7617
|
preferPrefixWidthsForBreakableRuns: isSafari,
|
|
7618
7618
|
preferEarlySoftHyphenBreak: isSafari
|
|
7619
7619
|
};
|
|
7620
|
-
return cachedEngineProfile
|
|
7620
|
+
return cachedEngineProfile;
|
|
7621
7621
|
}
|
|
7622
|
-
function parseFontSize
|
|
7622
|
+
function parseFontSize(font) {
|
|
7623
7623
|
const m = font.match(/(\d+(?:\.\d+)?)\s*px/);
|
|
7624
7624
|
return m ? parseFloat(m[1]) : 16;
|
|
7625
7625
|
}
|
|
7626
|
-
function getSharedGraphemeSegmenter$
|
|
7627
|
-
if (sharedGraphemeSegmenter$
|
|
7628
|
-
sharedGraphemeSegmenter$
|
|
7626
|
+
function getSharedGraphemeSegmenter$1() {
|
|
7627
|
+
if (sharedGraphemeSegmenter$1 === null) {
|
|
7628
|
+
sharedGraphemeSegmenter$1 = new Intl.Segmenter(undefined, {
|
|
7629
7629
|
granularity: 'grapheme'
|
|
7630
7630
|
});
|
|
7631
7631
|
}
|
|
7632
|
-
return sharedGraphemeSegmenter$
|
|
7632
|
+
return sharedGraphemeSegmenter$1;
|
|
7633
7633
|
}
|
|
7634
|
-
function isEmojiGrapheme
|
|
7635
|
-
return emojiPresentationRe
|
|
7634
|
+
function isEmojiGrapheme(g) {
|
|
7635
|
+
return emojiPresentationRe.test(g) || g.includes('\uFE0F');
|
|
7636
7636
|
}
|
|
7637
|
-
function textMayContainEmoji
|
|
7638
|
-
return maybeEmojiRe
|
|
7637
|
+
function textMayContainEmoji(text) {
|
|
7638
|
+
return maybeEmojiRe.test(text);
|
|
7639
7639
|
}
|
|
7640
|
-
function getEmojiCorrection
|
|
7641
|
-
let correction = emojiCorrectionCache
|
|
7640
|
+
function getEmojiCorrection(font, fontSize) {
|
|
7641
|
+
let correction = emojiCorrectionCache.get(font);
|
|
7642
7642
|
if (correction !== undefined) return correction;
|
|
7643
|
-
const ctx = getMeasureContext
|
|
7643
|
+
const ctx = getMeasureContext();
|
|
7644
7644
|
ctx.font = font;
|
|
7645
7645
|
const canvasW = ctx.measureText('\u{1F600}').width;
|
|
7646
7646
|
correction = 0;
|
|
@@ -7658,30 +7658,30 @@
|
|
|
7658
7658
|
correction = canvasW - domW;
|
|
7659
7659
|
}
|
|
7660
7660
|
}
|
|
7661
|
-
emojiCorrectionCache
|
|
7661
|
+
emojiCorrectionCache.set(font, correction);
|
|
7662
7662
|
return correction;
|
|
7663
7663
|
}
|
|
7664
|
-
function countEmojiGraphemes
|
|
7664
|
+
function countEmojiGraphemes(text) {
|
|
7665
7665
|
let count = 0;
|
|
7666
|
-
const graphemeSegmenter = getSharedGraphemeSegmenter$
|
|
7666
|
+
const graphemeSegmenter = getSharedGraphemeSegmenter$1();
|
|
7667
7667
|
for (const g of graphemeSegmenter.segment(text)){
|
|
7668
|
-
if (isEmojiGrapheme
|
|
7668
|
+
if (isEmojiGrapheme(g.segment)) count++;
|
|
7669
7669
|
}
|
|
7670
7670
|
return count;
|
|
7671
7671
|
}
|
|
7672
|
-
function getEmojiCount
|
|
7672
|
+
function getEmojiCount(seg, metrics) {
|
|
7673
7673
|
if (metrics.emojiCount === undefined) {
|
|
7674
|
-
metrics.emojiCount = countEmojiGraphemes
|
|
7674
|
+
metrics.emojiCount = countEmojiGraphemes(seg);
|
|
7675
7675
|
}
|
|
7676
7676
|
return metrics.emojiCount;
|
|
7677
7677
|
}
|
|
7678
|
-
function getCorrectedSegmentWidth
|
|
7678
|
+
function getCorrectedSegmentWidth(seg, metrics, emojiCorrection) {
|
|
7679
7679
|
if (emojiCorrection === 0) return metrics.width;
|
|
7680
|
-
return metrics.width - getEmojiCount
|
|
7680
|
+
return metrics.width - getEmojiCount(seg, metrics) * emojiCorrection;
|
|
7681
7681
|
}
|
|
7682
7682
|
function getSegmentBreakableFitAdvances(seg, metrics, cache, emojiCorrection, mode) {
|
|
7683
7683
|
if (metrics.breakableFitAdvances !== undefined) return metrics.breakableFitAdvances;
|
|
7684
|
-
const graphemeSegmenter = getSharedGraphemeSegmenter$
|
|
7684
|
+
const graphemeSegmenter = getSharedGraphemeSegmenter$1();
|
|
7685
7685
|
const graphemes = [];
|
|
7686
7686
|
for (const gs of graphemeSegmenter.segment(seg)){
|
|
7687
7687
|
graphemes.push(gs.segment);
|
|
@@ -7693,8 +7693,8 @@
|
|
|
7693
7693
|
if (mode === 'sum-graphemes') {
|
|
7694
7694
|
const advances = [];
|
|
7695
7695
|
for (const grapheme of graphemes){
|
|
7696
|
-
const graphemeMetrics = getSegmentMetrics
|
|
7697
|
-
advances.push(getCorrectedSegmentWidth
|
|
7696
|
+
const graphemeMetrics = getSegmentMetrics(grapheme, cache);
|
|
7697
|
+
advances.push(getCorrectedSegmentWidth(grapheme, graphemeMetrics, emojiCorrection));
|
|
7698
7698
|
}
|
|
7699
7699
|
metrics.breakableFitAdvances = advances;
|
|
7700
7700
|
return metrics.breakableFitAdvances;
|
|
@@ -7704,14 +7704,14 @@
|
|
|
7704
7704
|
let previousGrapheme = null;
|
|
7705
7705
|
let previousWidth = 0;
|
|
7706
7706
|
for (const grapheme of graphemes){
|
|
7707
|
-
const graphemeMetrics = getSegmentMetrics
|
|
7708
|
-
const currentWidth = getCorrectedSegmentWidth
|
|
7707
|
+
const graphemeMetrics = getSegmentMetrics(grapheme, cache);
|
|
7708
|
+
const currentWidth = getCorrectedSegmentWidth(grapheme, graphemeMetrics, emojiCorrection);
|
|
7709
7709
|
if (previousGrapheme === null) {
|
|
7710
7710
|
advances.push(currentWidth);
|
|
7711
7711
|
} else {
|
|
7712
7712
|
const pair = previousGrapheme + grapheme;
|
|
7713
|
-
const pairMetrics = getSegmentMetrics
|
|
7714
|
-
advances.push(getCorrectedSegmentWidth
|
|
7713
|
+
const pairMetrics = getSegmentMetrics(pair, cache);
|
|
7714
|
+
advances.push(getCorrectedSegmentWidth(pair, pairMetrics, emojiCorrection) - previousWidth);
|
|
7715
7715
|
}
|
|
7716
7716
|
previousGrapheme = grapheme;
|
|
7717
7717
|
previousWidth = currentWidth;
|
|
@@ -7724,20 +7724,20 @@
|
|
|
7724
7724
|
let prefixWidth = 0;
|
|
7725
7725
|
for (const grapheme of graphemes){
|
|
7726
7726
|
prefix += grapheme;
|
|
7727
|
-
const prefixMetrics = getSegmentMetrics
|
|
7728
|
-
const nextPrefixWidth = getCorrectedSegmentWidth
|
|
7727
|
+
const prefixMetrics = getSegmentMetrics(prefix, cache);
|
|
7728
|
+
const nextPrefixWidth = getCorrectedSegmentWidth(prefix, prefixMetrics, emojiCorrection);
|
|
7729
7729
|
advances.push(nextPrefixWidth - prefixWidth);
|
|
7730
7730
|
prefixWidth = nextPrefixWidth;
|
|
7731
7731
|
}
|
|
7732
7732
|
metrics.breakableFitAdvances = advances;
|
|
7733
7733
|
return metrics.breakableFitAdvances;
|
|
7734
7734
|
}
|
|
7735
|
-
function getFontMeasurementState
|
|
7736
|
-
const ctx = getMeasureContext
|
|
7735
|
+
function getFontMeasurementState(font, needsEmojiCorrection) {
|
|
7736
|
+
const ctx = getMeasureContext();
|
|
7737
7737
|
ctx.font = font;
|
|
7738
|
-
const cache = getSegmentMetricCache
|
|
7739
|
-
const fontSize = parseFontSize
|
|
7740
|
-
const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection
|
|
7738
|
+
const cache = getSegmentMetricCache(font);
|
|
7739
|
+
const fontSize = parseFontSize(font);
|
|
7740
|
+
const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection(font, fontSize) : 0;
|
|
7741
7741
|
return {
|
|
7742
7742
|
cache,
|
|
7743
7743
|
fontSize,
|
|
@@ -7777,7 +7777,7 @@
|
|
|
7777
7777
|
function walkPreparedLinesSimple(prepared, maxWidth, onLine) {
|
|
7778
7778
|
const { widths, kinds, breakableFitAdvances } = prepared;
|
|
7779
7779
|
if (widths.length === 0) return 0;
|
|
7780
|
-
const engineProfile = getEngineProfile
|
|
7780
|
+
const engineProfile = getEngineProfile();
|
|
7781
7781
|
const lineFitEpsilon = engineProfile.lineFitEpsilon;
|
|
7782
7782
|
const fitLimit = maxWidth + lineFitEpsilon;
|
|
7783
7783
|
let lineCount = 0;
|
|
@@ -7914,7 +7914,7 @@
|
|
|
7914
7914
|
}
|
|
7915
7915
|
const { widths, lineEndFitAdvances, lineEndPaintAdvances, kinds, breakableFitAdvances, discretionaryHyphenWidth, tabStopAdvance, chunks } = prepared;
|
|
7916
7916
|
if (widths.length === 0 || chunks.length === 0) return 0;
|
|
7917
|
-
const engineProfile = getEngineProfile
|
|
7917
|
+
const engineProfile = getEngineProfile();
|
|
7918
7918
|
const lineFitEpsilon = engineProfile.lineFitEpsilon;
|
|
7919
7919
|
const fitLimit = maxWidth + lineFitEpsilon;
|
|
7920
7920
|
let lineCount = 0;
|
|
@@ -8153,20 +8153,20 @@
|
|
|
8153
8153
|
// See RESEARCH.md "Discovery: system-ui font resolution mismatch".
|
|
8154
8154
|
//
|
|
8155
8155
|
// Based on Sebastian Markbage's text-layout research (github.com/chenglou/text-layout).
|
|
8156
|
-
let sharedGraphemeSegmenter
|
|
8156
|
+
let sharedGraphemeSegmenter = null;
|
|
8157
8157
|
// Rich-path only. Reuses grapheme splits while materializing multiple lines
|
|
8158
8158
|
// from the same prepared handle, without pushing that cache into the API.
|
|
8159
8159
|
let sharedLineTextCaches = new WeakMap();
|
|
8160
|
-
function getSharedGraphemeSegmenter
|
|
8161
|
-
if (sharedGraphemeSegmenter
|
|
8162
|
-
sharedGraphemeSegmenter
|
|
8160
|
+
function getSharedGraphemeSegmenter() {
|
|
8161
|
+
if (sharedGraphemeSegmenter === null) {
|
|
8162
|
+
sharedGraphemeSegmenter = new Intl.Segmenter(undefined, {
|
|
8163
8163
|
granularity: 'grapheme'
|
|
8164
8164
|
});
|
|
8165
8165
|
}
|
|
8166
|
-
return sharedGraphemeSegmenter
|
|
8166
|
+
return sharedGraphemeSegmenter;
|
|
8167
8167
|
}
|
|
8168
8168
|
// --- Public API ---
|
|
8169
|
-
function createEmptyPrepared
|
|
8169
|
+
function createEmptyPrepared(includeSegments) {
|
|
8170
8170
|
{
|
|
8171
8171
|
return {
|
|
8172
8172
|
widths: [],
|
|
@@ -8207,28 +8207,28 @@
|
|
|
8207
8207
|
];
|
|
8208
8208
|
unitStart = start;
|
|
8209
8209
|
unitContainsCJK = graphemeContainsCJK;
|
|
8210
|
-
unitEndsWithClosingQuote = endsWithClosingQuote
|
|
8211
|
-
unitIsSingleKinsokuEnd = kinsokuEnd
|
|
8210
|
+
unitEndsWithClosingQuote = endsWithClosingQuote(grapheme);
|
|
8211
|
+
unitIsSingleKinsokuEnd = kinsokuEnd.has(grapheme);
|
|
8212
8212
|
}
|
|
8213
8213
|
function appendToUnit(grapheme, graphemeContainsCJK) {
|
|
8214
8214
|
unitParts.push(grapheme);
|
|
8215
8215
|
unitContainsCJK = unitContainsCJK || graphemeContainsCJK;
|
|
8216
|
-
const graphemeEndsWithClosingQuote = endsWithClosingQuote
|
|
8217
|
-
if (grapheme.length === 1 && leftStickyPunctuation
|
|
8216
|
+
const graphemeEndsWithClosingQuote = endsWithClosingQuote(grapheme);
|
|
8217
|
+
if (grapheme.length === 1 && leftStickyPunctuation.has(grapheme)) {
|
|
8218
8218
|
unitEndsWithClosingQuote = unitEndsWithClosingQuote || graphemeEndsWithClosingQuote;
|
|
8219
8219
|
} else {
|
|
8220
8220
|
unitEndsWithClosingQuote = graphemeEndsWithClosingQuote;
|
|
8221
8221
|
}
|
|
8222
8222
|
unitIsSingleKinsokuEnd = false;
|
|
8223
8223
|
}
|
|
8224
|
-
for (const gs of getSharedGraphemeSegmenter
|
|
8224
|
+
for (const gs of getSharedGraphemeSegmenter().segment(segText)){
|
|
8225
8225
|
const grapheme = gs.segment;
|
|
8226
|
-
const graphemeContainsCJK = isCJK
|
|
8226
|
+
const graphemeContainsCJK = isCJK(grapheme);
|
|
8227
8227
|
if (unitParts.length === 0) {
|
|
8228
8228
|
startUnit(grapheme, gs.index, graphemeContainsCJK);
|
|
8229
8229
|
continue;
|
|
8230
8230
|
}
|
|
8231
|
-
if (unitIsSingleKinsokuEnd || kinsokuStart
|
|
8231
|
+
if (unitIsSingleKinsokuEnd || kinsokuStart.has(grapheme) || leftStickyPunctuation.has(grapheme) || engineProfile.carryCJKAfterClosingQuote && graphemeContainsCJK && unitEndsWithClosingQuote) {
|
|
8232
8232
|
appendToUnit(grapheme, graphemeContainsCJK);
|
|
8233
8233
|
continue;
|
|
8234
8234
|
}
|
|
@@ -8242,13 +8242,49 @@
|
|
|
8242
8242
|
pushUnit();
|
|
8243
8243
|
return units;
|
|
8244
8244
|
}
|
|
8245
|
-
function
|
|
8246
|
-
|
|
8247
|
-
const
|
|
8248
|
-
|
|
8249
|
-
|
|
8245
|
+
function mergeKeepAllTextUnits(units) {
|
|
8246
|
+
if (units.length <= 1) return units;
|
|
8247
|
+
const merged = [];
|
|
8248
|
+
let currentTextParts = [
|
|
8249
|
+
units[0].text
|
|
8250
|
+
];
|
|
8251
|
+
let currentStart = units[0].start;
|
|
8252
|
+
let currentContainsCJK = isCJK(units[0].text);
|
|
8253
|
+
let currentCanContinue = canContinueKeepAllTextRun(units[0].text);
|
|
8254
|
+
function flushCurrent() {
|
|
8255
|
+
merged.push({
|
|
8256
|
+
text: currentTextParts.length === 1 ? currentTextParts[0] : currentTextParts.join(''),
|
|
8257
|
+
start: currentStart
|
|
8258
|
+
});
|
|
8259
|
+
}
|
|
8260
|
+
for(let i = 1; i < units.length; i++){
|
|
8261
|
+
const next = units[i];
|
|
8262
|
+
const nextContainsCJK = isCJK(next.text);
|
|
8263
|
+
const nextCanContinue = canContinueKeepAllTextRun(next.text);
|
|
8264
|
+
if (currentContainsCJK && currentCanContinue) {
|
|
8265
|
+
currentTextParts.push(next.text);
|
|
8266
|
+
currentContainsCJK = currentContainsCJK || nextContainsCJK;
|
|
8267
|
+
currentCanContinue = nextCanContinue;
|
|
8268
|
+
continue;
|
|
8269
|
+
}
|
|
8270
|
+
flushCurrent();
|
|
8271
|
+
currentTextParts = [
|
|
8272
|
+
next.text
|
|
8273
|
+
];
|
|
8274
|
+
currentStart = next.start;
|
|
8275
|
+
currentContainsCJK = nextContainsCJK;
|
|
8276
|
+
currentCanContinue = nextCanContinue;
|
|
8277
|
+
}
|
|
8278
|
+
flushCurrent();
|
|
8279
|
+
return merged;
|
|
8280
|
+
}
|
|
8281
|
+
function measureAnalysis(analysis, font, includeSegments, wordBreak) {
|
|
8282
|
+
const engineProfile = getEngineProfile();
|
|
8283
|
+
const { cache, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
|
|
8284
|
+
const discretionaryHyphenWidth = getCorrectedSegmentWidth('-', getSegmentMetrics('-', cache), emojiCorrection);
|
|
8285
|
+
const spaceWidth = getCorrectedSegmentWidth(' ', getSegmentMetrics(' ', cache), emojiCorrection);
|
|
8250
8286
|
const tabStopAdvance = spaceWidth * 8;
|
|
8251
|
-
if (analysis.len === 0) return createEmptyPrepared
|
|
8287
|
+
if (analysis.len === 0) return createEmptyPrepared();
|
|
8252
8288
|
const widths = [];
|
|
8253
8289
|
const lineEndFitAdvances = [];
|
|
8254
8290
|
const lineEndPaintAdvances = [];
|
|
@@ -8273,13 +8309,13 @@
|
|
|
8273
8309
|
if (segments !== null) segments.push(text);
|
|
8274
8310
|
}
|
|
8275
8311
|
function pushMeasuredTextSegment(text, kind, start, wordLike, allowOverflowBreaks) {
|
|
8276
|
-
const textMetrics = getSegmentMetrics
|
|
8277
|
-
const width = getCorrectedSegmentWidth
|
|
8312
|
+
const textMetrics = getSegmentMetrics(text, cache);
|
|
8313
|
+
const width = getCorrectedSegmentWidth(text, textMetrics, emojiCorrection);
|
|
8278
8314
|
const lineEndFitAdvance = kind === 'space' || kind === 'preserved-space' || kind === 'zero-width-break' ? 0 : width;
|
|
8279
8315
|
const lineEndPaintAdvance = kind === 'space' || kind === 'zero-width-break' ? 0 : width;
|
|
8280
8316
|
if (allowOverflowBreaks && wordLike && text.length > 1) {
|
|
8281
8317
|
let fitMode = 'sum-graphemes';
|
|
8282
|
-
if (isNumericRunSegment
|
|
8318
|
+
if (isNumericRunSegment(text)) {
|
|
8283
8319
|
fitMode = 'pair-context';
|
|
8284
8320
|
} else if (engineProfile.preferPrefixWidthsForBreakableRuns) {
|
|
8285
8321
|
fitMode = 'segment-prefixes';
|
|
@@ -8308,20 +8344,20 @@
|
|
|
8308
8344
|
pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null);
|
|
8309
8345
|
continue;
|
|
8310
8346
|
}
|
|
8311
|
-
const segMetrics = getSegmentMetrics
|
|
8347
|
+
const segMetrics = getSegmentMetrics(segText, cache);
|
|
8312
8348
|
if (segKind === 'text' && segMetrics.containsCJK) {
|
|
8313
8349
|
const baseUnits = buildBaseCjkUnits(segText, engineProfile);
|
|
8314
|
-
const measuredUnits = baseUnits;
|
|
8350
|
+
const measuredUnits = wordBreak === 'keep-all' ? mergeKeepAllTextUnits(baseUnits) : baseUnits;
|
|
8315
8351
|
for(let i = 0; i < measuredUnits.length; i++){
|
|
8316
8352
|
const unit = measuredUnits[i];
|
|
8317
|
-
pushMeasuredTextSegment(unit.text, 'text', segStart + unit.start, segWordLike, !isCJK
|
|
8353
|
+
pushMeasuredTextSegment(unit.text, 'text', segStart + unit.start, segWordLike, wordBreak === 'keep-all' || !isCJK(unit.text));
|
|
8318
8354
|
}
|
|
8319
8355
|
continue;
|
|
8320
8356
|
}
|
|
8321
8357
|
pushMeasuredTextSegment(segText, segKind, segStart, segWordLike, true);
|
|
8322
8358
|
}
|
|
8323
|
-
const chunks = mapAnalysisChunksToPreparedChunks
|
|
8324
|
-
const segLevels = segStarts === null ? null : computeSegmentLevels
|
|
8359
|
+
const chunks = mapAnalysisChunksToPreparedChunks(analysis.chunks, preparedStartByAnalysisIndex, widths.length);
|
|
8360
|
+
const segLevels = segStarts === null ? null : computeSegmentLevels(analysis.normalized, segStarts);
|
|
8325
8361
|
if (segments !== null) {
|
|
8326
8362
|
return {
|
|
8327
8363
|
widths,
|
|
@@ -8350,7 +8386,7 @@
|
|
|
8350
8386
|
chunks
|
|
8351
8387
|
};
|
|
8352
8388
|
}
|
|
8353
|
-
function mapAnalysisChunksToPreparedChunks
|
|
8389
|
+
function mapAnalysisChunksToPreparedChunks(chunks, preparedStartByAnalysisIndex, preparedEndSegmentIndex) {
|
|
8354
8390
|
const preparedChunks = [];
|
|
8355
8391
|
for(let i = 0; i < chunks.length; i++){
|
|
8356
8392
|
const chunk = chunks[i];
|
|
@@ -8365,15 +8401,15 @@
|
|
|
8365
8401
|
}
|
|
8366
8402
|
return preparedChunks;
|
|
8367
8403
|
}
|
|
8368
|
-
function prepareInternal
|
|
8369
|
-
const wordBreak = 'normal';
|
|
8370
|
-
const analysis = analyzeText
|
|
8371
|
-
return measureAnalysis
|
|
8404
|
+
function prepareInternal(text, font, includeSegments, options) {
|
|
8405
|
+
const wordBreak = options?.wordBreak ?? 'normal';
|
|
8406
|
+
const analysis = analyzeText(text, getEngineProfile(), options?.whiteSpace, wordBreak);
|
|
8407
|
+
return measureAnalysis(analysis, font, includeSegments, wordBreak);
|
|
8372
8408
|
}
|
|
8373
8409
|
// Rich variant used by callers that need enough information to render the
|
|
8374
8410
|
// laid-out lines themselves.
|
|
8375
|
-
function prepareWithSegments
|
|
8376
|
-
return prepareInternal
|
|
8411
|
+
function prepareWithSegments(text, font, options) {
|
|
8412
|
+
return prepareInternal(text, font, true, options);
|
|
8377
8413
|
}
|
|
8378
8414
|
function getInternalPrepared(prepared) {
|
|
8379
8415
|
return prepared;
|
|
@@ -8382,7 +8418,7 @@
|
|
|
8382
8418
|
let graphemes = cache.get(segmentIndex);
|
|
8383
8419
|
if (graphemes !== undefined) return graphemes;
|
|
8384
8420
|
graphemes = [];
|
|
8385
|
-
const graphemeSegmenter = getSharedGraphemeSegmenter
|
|
8421
|
+
const graphemeSegmenter = getSharedGraphemeSegmenter();
|
|
8386
8422
|
for (const gs of graphemeSegmenter.segment(segments[segmentIndex])){
|
|
8387
8423
|
graphemes.push(gs.segment);
|
|
8388
8424
|
}
|
|
@@ -8490,7 +8526,7 @@
|
|
|
8490
8526
|
* @param {String} font CSS font shorthand
|
|
8491
8527
|
*/ function measureWidth(text, font) {
|
|
8492
8528
|
if (!text) return 0;
|
|
8493
|
-
var prepared = prepareWithSegments
|
|
8529
|
+
var prepared = prepareWithSegments(text, font);
|
|
8494
8530
|
var result = layoutWithLines(prepared, Infinity, 20);
|
|
8495
8531
|
return result.lines.length ? result.lines[0].width : 0;
|
|
8496
8532
|
}
|
|
@@ -26487,1771 +26523,6 @@
|
|
|
26487
26523
|
});
|
|
26488
26524
|
}
|
|
26489
26525
|
|
|
26490
|
-
// Simplified bidi metadata helper for the rich prepareWithSegments() path,
|
|
26491
|
-
// forked from pdf.js via Sebastian's text-layout. It classifies characters
|
|
26492
|
-
// into bidi types, computes embedding levels, and maps them onto prepared
|
|
26493
|
-
// segments for custom rendering. The line-breaking engine does not consume
|
|
26494
|
-
// these levels.
|
|
26495
|
-
const baseTypes = [
|
|
26496
|
-
'BN',
|
|
26497
|
-
'BN',
|
|
26498
|
-
'BN',
|
|
26499
|
-
'BN',
|
|
26500
|
-
'BN',
|
|
26501
|
-
'BN',
|
|
26502
|
-
'BN',
|
|
26503
|
-
'BN',
|
|
26504
|
-
'BN',
|
|
26505
|
-
'S',
|
|
26506
|
-
'B',
|
|
26507
|
-
'S',
|
|
26508
|
-
'WS',
|
|
26509
|
-
'B',
|
|
26510
|
-
'BN',
|
|
26511
|
-
'BN',
|
|
26512
|
-
'BN',
|
|
26513
|
-
'BN',
|
|
26514
|
-
'BN',
|
|
26515
|
-
'BN',
|
|
26516
|
-
'BN',
|
|
26517
|
-
'BN',
|
|
26518
|
-
'BN',
|
|
26519
|
-
'BN',
|
|
26520
|
-
'BN',
|
|
26521
|
-
'BN',
|
|
26522
|
-
'BN',
|
|
26523
|
-
'BN',
|
|
26524
|
-
'B',
|
|
26525
|
-
'B',
|
|
26526
|
-
'B',
|
|
26527
|
-
'S',
|
|
26528
|
-
'WS',
|
|
26529
|
-
'ON',
|
|
26530
|
-
'ON',
|
|
26531
|
-
'ET',
|
|
26532
|
-
'ET',
|
|
26533
|
-
'ET',
|
|
26534
|
-
'ON',
|
|
26535
|
-
'ON',
|
|
26536
|
-
'ON',
|
|
26537
|
-
'ON',
|
|
26538
|
-
'ON',
|
|
26539
|
-
'ON',
|
|
26540
|
-
'CS',
|
|
26541
|
-
'ON',
|
|
26542
|
-
'CS',
|
|
26543
|
-
'ON',
|
|
26544
|
-
'EN',
|
|
26545
|
-
'EN',
|
|
26546
|
-
'EN',
|
|
26547
|
-
'EN',
|
|
26548
|
-
'EN',
|
|
26549
|
-
'EN',
|
|
26550
|
-
'EN',
|
|
26551
|
-
'EN',
|
|
26552
|
-
'EN',
|
|
26553
|
-
'EN',
|
|
26554
|
-
'ON',
|
|
26555
|
-
'ON',
|
|
26556
|
-
'ON',
|
|
26557
|
-
'ON',
|
|
26558
|
-
'ON',
|
|
26559
|
-
'ON',
|
|
26560
|
-
'ON',
|
|
26561
|
-
'L',
|
|
26562
|
-
'L',
|
|
26563
|
-
'L',
|
|
26564
|
-
'L',
|
|
26565
|
-
'L',
|
|
26566
|
-
'L',
|
|
26567
|
-
'L',
|
|
26568
|
-
'L',
|
|
26569
|
-
'L',
|
|
26570
|
-
'L',
|
|
26571
|
-
'L',
|
|
26572
|
-
'L',
|
|
26573
|
-
'L',
|
|
26574
|
-
'L',
|
|
26575
|
-
'L',
|
|
26576
|
-
'L',
|
|
26577
|
-
'L',
|
|
26578
|
-
'L',
|
|
26579
|
-
'L',
|
|
26580
|
-
'L',
|
|
26581
|
-
'L',
|
|
26582
|
-
'L',
|
|
26583
|
-
'L',
|
|
26584
|
-
'L',
|
|
26585
|
-
'L',
|
|
26586
|
-
'L',
|
|
26587
|
-
'ON',
|
|
26588
|
-
'ON',
|
|
26589
|
-
'ON',
|
|
26590
|
-
'ON',
|
|
26591
|
-
'ON',
|
|
26592
|
-
'ON',
|
|
26593
|
-
'L',
|
|
26594
|
-
'L',
|
|
26595
|
-
'L',
|
|
26596
|
-
'L',
|
|
26597
|
-
'L',
|
|
26598
|
-
'L',
|
|
26599
|
-
'L',
|
|
26600
|
-
'L',
|
|
26601
|
-
'L',
|
|
26602
|
-
'L',
|
|
26603
|
-
'L',
|
|
26604
|
-
'L',
|
|
26605
|
-
'L',
|
|
26606
|
-
'L',
|
|
26607
|
-
'L',
|
|
26608
|
-
'L',
|
|
26609
|
-
'L',
|
|
26610
|
-
'L',
|
|
26611
|
-
'L',
|
|
26612
|
-
'L',
|
|
26613
|
-
'L',
|
|
26614
|
-
'L',
|
|
26615
|
-
'L',
|
|
26616
|
-
'L',
|
|
26617
|
-
'L',
|
|
26618
|
-
'L',
|
|
26619
|
-
'ON',
|
|
26620
|
-
'ON',
|
|
26621
|
-
'ON',
|
|
26622
|
-
'ON',
|
|
26623
|
-
'BN',
|
|
26624
|
-
'BN',
|
|
26625
|
-
'BN',
|
|
26626
|
-
'BN',
|
|
26627
|
-
'BN',
|
|
26628
|
-
'BN',
|
|
26629
|
-
'B',
|
|
26630
|
-
'BN',
|
|
26631
|
-
'BN',
|
|
26632
|
-
'BN',
|
|
26633
|
-
'BN',
|
|
26634
|
-
'BN',
|
|
26635
|
-
'BN',
|
|
26636
|
-
'BN',
|
|
26637
|
-
'BN',
|
|
26638
|
-
'BN',
|
|
26639
|
-
'BN',
|
|
26640
|
-
'BN',
|
|
26641
|
-
'BN',
|
|
26642
|
-
'BN',
|
|
26643
|
-
'BN',
|
|
26644
|
-
'BN',
|
|
26645
|
-
'BN',
|
|
26646
|
-
'BN',
|
|
26647
|
-
'BN',
|
|
26648
|
-
'BN',
|
|
26649
|
-
'BN',
|
|
26650
|
-
'BN',
|
|
26651
|
-
'BN',
|
|
26652
|
-
'BN',
|
|
26653
|
-
'BN',
|
|
26654
|
-
'BN',
|
|
26655
|
-
'BN',
|
|
26656
|
-
'CS',
|
|
26657
|
-
'ON',
|
|
26658
|
-
'ET',
|
|
26659
|
-
'ET',
|
|
26660
|
-
'ET',
|
|
26661
|
-
'ET',
|
|
26662
|
-
'ON',
|
|
26663
|
-
'ON',
|
|
26664
|
-
'ON',
|
|
26665
|
-
'ON',
|
|
26666
|
-
'L',
|
|
26667
|
-
'ON',
|
|
26668
|
-
'ON',
|
|
26669
|
-
'ON',
|
|
26670
|
-
'ON',
|
|
26671
|
-
'ON',
|
|
26672
|
-
'ET',
|
|
26673
|
-
'ET',
|
|
26674
|
-
'EN',
|
|
26675
|
-
'EN',
|
|
26676
|
-
'ON',
|
|
26677
|
-
'L',
|
|
26678
|
-
'ON',
|
|
26679
|
-
'ON',
|
|
26680
|
-
'ON',
|
|
26681
|
-
'EN',
|
|
26682
|
-
'L',
|
|
26683
|
-
'ON',
|
|
26684
|
-
'ON',
|
|
26685
|
-
'ON',
|
|
26686
|
-
'ON',
|
|
26687
|
-
'ON',
|
|
26688
|
-
'L',
|
|
26689
|
-
'L',
|
|
26690
|
-
'L',
|
|
26691
|
-
'L',
|
|
26692
|
-
'L',
|
|
26693
|
-
'L',
|
|
26694
|
-
'L',
|
|
26695
|
-
'L',
|
|
26696
|
-
'L',
|
|
26697
|
-
'L',
|
|
26698
|
-
'L',
|
|
26699
|
-
'L',
|
|
26700
|
-
'L',
|
|
26701
|
-
'L',
|
|
26702
|
-
'L',
|
|
26703
|
-
'L',
|
|
26704
|
-
'L',
|
|
26705
|
-
'L',
|
|
26706
|
-
'L',
|
|
26707
|
-
'L',
|
|
26708
|
-
'L',
|
|
26709
|
-
'L',
|
|
26710
|
-
'L',
|
|
26711
|
-
'ON',
|
|
26712
|
-
'L',
|
|
26713
|
-
'L',
|
|
26714
|
-
'L',
|
|
26715
|
-
'L',
|
|
26716
|
-
'L',
|
|
26717
|
-
'L',
|
|
26718
|
-
'L',
|
|
26719
|
-
'L',
|
|
26720
|
-
'L',
|
|
26721
|
-
'L',
|
|
26722
|
-
'L',
|
|
26723
|
-
'L',
|
|
26724
|
-
'L',
|
|
26725
|
-
'L',
|
|
26726
|
-
'L',
|
|
26727
|
-
'L',
|
|
26728
|
-
'L',
|
|
26729
|
-
'L',
|
|
26730
|
-
'L',
|
|
26731
|
-
'L',
|
|
26732
|
-
'L',
|
|
26733
|
-
'L',
|
|
26734
|
-
'L',
|
|
26735
|
-
'L',
|
|
26736
|
-
'L',
|
|
26737
|
-
'L',
|
|
26738
|
-
'L',
|
|
26739
|
-
'L',
|
|
26740
|
-
'L',
|
|
26741
|
-
'L',
|
|
26742
|
-
'L',
|
|
26743
|
-
'ON',
|
|
26744
|
-
'L',
|
|
26745
|
-
'L',
|
|
26746
|
-
'L',
|
|
26747
|
-
'L',
|
|
26748
|
-
'L',
|
|
26749
|
-
'L',
|
|
26750
|
-
'L',
|
|
26751
|
-
'L'
|
|
26752
|
-
];
|
|
26753
|
-
const arabicTypes = [
|
|
26754
|
-
'AL',
|
|
26755
|
-
'AL',
|
|
26756
|
-
'AL',
|
|
26757
|
-
'AL',
|
|
26758
|
-
'AL',
|
|
26759
|
-
'AL',
|
|
26760
|
-
'AL',
|
|
26761
|
-
'AL',
|
|
26762
|
-
'AL',
|
|
26763
|
-
'AL',
|
|
26764
|
-
'AL',
|
|
26765
|
-
'AL',
|
|
26766
|
-
'CS',
|
|
26767
|
-
'AL',
|
|
26768
|
-
'ON',
|
|
26769
|
-
'ON',
|
|
26770
|
-
'NSM',
|
|
26771
|
-
'NSM',
|
|
26772
|
-
'NSM',
|
|
26773
|
-
'NSM',
|
|
26774
|
-
'NSM',
|
|
26775
|
-
'NSM',
|
|
26776
|
-
'AL',
|
|
26777
|
-
'AL',
|
|
26778
|
-
'AL',
|
|
26779
|
-
'AL',
|
|
26780
|
-
'AL',
|
|
26781
|
-
'AL',
|
|
26782
|
-
'AL',
|
|
26783
|
-
'AL',
|
|
26784
|
-
'AL',
|
|
26785
|
-
'AL',
|
|
26786
|
-
'AL',
|
|
26787
|
-
'AL',
|
|
26788
|
-
'AL',
|
|
26789
|
-
'AL',
|
|
26790
|
-
'AL',
|
|
26791
|
-
'AL',
|
|
26792
|
-
'AL',
|
|
26793
|
-
'AL',
|
|
26794
|
-
'AL',
|
|
26795
|
-
'AL',
|
|
26796
|
-
'AL',
|
|
26797
|
-
'AL',
|
|
26798
|
-
'AL',
|
|
26799
|
-
'AL',
|
|
26800
|
-
'AL',
|
|
26801
|
-
'AL',
|
|
26802
|
-
'AL',
|
|
26803
|
-
'AL',
|
|
26804
|
-
'AL',
|
|
26805
|
-
'AL',
|
|
26806
|
-
'AL',
|
|
26807
|
-
'AL',
|
|
26808
|
-
'AL',
|
|
26809
|
-
'AL',
|
|
26810
|
-
'AL',
|
|
26811
|
-
'AL',
|
|
26812
|
-
'AL',
|
|
26813
|
-
'AL',
|
|
26814
|
-
'AL',
|
|
26815
|
-
'AL',
|
|
26816
|
-
'AL',
|
|
26817
|
-
'AL',
|
|
26818
|
-
'AL',
|
|
26819
|
-
'AL',
|
|
26820
|
-
'AL',
|
|
26821
|
-
'AL',
|
|
26822
|
-
'AL',
|
|
26823
|
-
'AL',
|
|
26824
|
-
'AL',
|
|
26825
|
-
'AL',
|
|
26826
|
-
'AL',
|
|
26827
|
-
'AL',
|
|
26828
|
-
'AL',
|
|
26829
|
-
'NSM',
|
|
26830
|
-
'NSM',
|
|
26831
|
-
'NSM',
|
|
26832
|
-
'NSM',
|
|
26833
|
-
'NSM',
|
|
26834
|
-
'NSM',
|
|
26835
|
-
'NSM',
|
|
26836
|
-
'NSM',
|
|
26837
|
-
'NSM',
|
|
26838
|
-
'NSM',
|
|
26839
|
-
'NSM',
|
|
26840
|
-
'NSM',
|
|
26841
|
-
'NSM',
|
|
26842
|
-
'NSM',
|
|
26843
|
-
'AL',
|
|
26844
|
-
'AL',
|
|
26845
|
-
'AL',
|
|
26846
|
-
'AL',
|
|
26847
|
-
'AL',
|
|
26848
|
-
'AL',
|
|
26849
|
-
'AL',
|
|
26850
|
-
'AN',
|
|
26851
|
-
'AN',
|
|
26852
|
-
'AN',
|
|
26853
|
-
'AN',
|
|
26854
|
-
'AN',
|
|
26855
|
-
'AN',
|
|
26856
|
-
'AN',
|
|
26857
|
-
'AN',
|
|
26858
|
-
'AN',
|
|
26859
|
-
'AN',
|
|
26860
|
-
'ET',
|
|
26861
|
-
'AN',
|
|
26862
|
-
'AN',
|
|
26863
|
-
'AL',
|
|
26864
|
-
'AL',
|
|
26865
|
-
'AL',
|
|
26866
|
-
'NSM',
|
|
26867
|
-
'AL',
|
|
26868
|
-
'AL',
|
|
26869
|
-
'AL',
|
|
26870
|
-
'AL',
|
|
26871
|
-
'AL',
|
|
26872
|
-
'AL',
|
|
26873
|
-
'AL',
|
|
26874
|
-
'AL',
|
|
26875
|
-
'AL',
|
|
26876
|
-
'AL',
|
|
26877
|
-
'AL',
|
|
26878
|
-
'AL',
|
|
26879
|
-
'AL',
|
|
26880
|
-
'AL',
|
|
26881
|
-
'AL',
|
|
26882
|
-
'AL',
|
|
26883
|
-
'AL',
|
|
26884
|
-
'AL',
|
|
26885
|
-
'AL',
|
|
26886
|
-
'AL',
|
|
26887
|
-
'AL',
|
|
26888
|
-
'AL',
|
|
26889
|
-
'AL',
|
|
26890
|
-
'AL',
|
|
26891
|
-
'AL',
|
|
26892
|
-
'AL',
|
|
26893
|
-
'AL',
|
|
26894
|
-
'AL',
|
|
26895
|
-
'AL',
|
|
26896
|
-
'AL',
|
|
26897
|
-
'AL',
|
|
26898
|
-
'AL',
|
|
26899
|
-
'AL',
|
|
26900
|
-
'AL',
|
|
26901
|
-
'AL',
|
|
26902
|
-
'AL',
|
|
26903
|
-
'AL',
|
|
26904
|
-
'AL',
|
|
26905
|
-
'AL',
|
|
26906
|
-
'AL',
|
|
26907
|
-
'AL',
|
|
26908
|
-
'AL',
|
|
26909
|
-
'AL',
|
|
26910
|
-
'AL',
|
|
26911
|
-
'AL',
|
|
26912
|
-
'AL',
|
|
26913
|
-
'AL',
|
|
26914
|
-
'AL',
|
|
26915
|
-
'AL',
|
|
26916
|
-
'AL',
|
|
26917
|
-
'AL',
|
|
26918
|
-
'AL',
|
|
26919
|
-
'AL',
|
|
26920
|
-
'AL',
|
|
26921
|
-
'AL',
|
|
26922
|
-
'AL',
|
|
26923
|
-
'AL',
|
|
26924
|
-
'AL',
|
|
26925
|
-
'AL',
|
|
26926
|
-
'AL',
|
|
26927
|
-
'AL',
|
|
26928
|
-
'AL',
|
|
26929
|
-
'AL',
|
|
26930
|
-
'AL',
|
|
26931
|
-
'AL',
|
|
26932
|
-
'AL',
|
|
26933
|
-
'AL',
|
|
26934
|
-
'AL',
|
|
26935
|
-
'AL',
|
|
26936
|
-
'AL',
|
|
26937
|
-
'AL',
|
|
26938
|
-
'AL',
|
|
26939
|
-
'AL',
|
|
26940
|
-
'AL',
|
|
26941
|
-
'AL',
|
|
26942
|
-
'AL',
|
|
26943
|
-
'AL',
|
|
26944
|
-
'AL',
|
|
26945
|
-
'AL',
|
|
26946
|
-
'AL',
|
|
26947
|
-
'AL',
|
|
26948
|
-
'AL',
|
|
26949
|
-
'AL',
|
|
26950
|
-
'AL',
|
|
26951
|
-
'AL',
|
|
26952
|
-
'AL',
|
|
26953
|
-
'AL',
|
|
26954
|
-
'AL',
|
|
26955
|
-
'AL',
|
|
26956
|
-
'AL',
|
|
26957
|
-
'AL',
|
|
26958
|
-
'AL',
|
|
26959
|
-
'AL',
|
|
26960
|
-
'AL',
|
|
26961
|
-
'AL',
|
|
26962
|
-
'AL',
|
|
26963
|
-
'AL',
|
|
26964
|
-
'AL',
|
|
26965
|
-
'AL',
|
|
26966
|
-
'AL',
|
|
26967
|
-
'AL',
|
|
26968
|
-
'NSM',
|
|
26969
|
-
'NSM',
|
|
26970
|
-
'NSM',
|
|
26971
|
-
'NSM',
|
|
26972
|
-
'NSM',
|
|
26973
|
-
'NSM',
|
|
26974
|
-
'NSM',
|
|
26975
|
-
'NSM',
|
|
26976
|
-
'NSM',
|
|
26977
|
-
'NSM',
|
|
26978
|
-
'NSM',
|
|
26979
|
-
'NSM',
|
|
26980
|
-
'NSM',
|
|
26981
|
-
'NSM',
|
|
26982
|
-
'NSM',
|
|
26983
|
-
'NSM',
|
|
26984
|
-
'NSM',
|
|
26985
|
-
'NSM',
|
|
26986
|
-
'NSM',
|
|
26987
|
-
'ON',
|
|
26988
|
-
'NSM',
|
|
26989
|
-
'NSM',
|
|
26990
|
-
'NSM',
|
|
26991
|
-
'NSM',
|
|
26992
|
-
'AL',
|
|
26993
|
-
'AL',
|
|
26994
|
-
'AL',
|
|
26995
|
-
'AL',
|
|
26996
|
-
'AL',
|
|
26997
|
-
'AL',
|
|
26998
|
-
'AL',
|
|
26999
|
-
'AL',
|
|
27000
|
-
'AL',
|
|
27001
|
-
'AL',
|
|
27002
|
-
'AL',
|
|
27003
|
-
'AL',
|
|
27004
|
-
'AL',
|
|
27005
|
-
'AL',
|
|
27006
|
-
'AL',
|
|
27007
|
-
'AL',
|
|
27008
|
-
'AL',
|
|
27009
|
-
'AL'
|
|
27010
|
-
];
|
|
27011
|
-
function classifyChar(charCode) {
|
|
27012
|
-
if (charCode <= 0x00ff) return baseTypes[charCode];
|
|
27013
|
-
if (0x0590 <= charCode && charCode <= 0x05f4) return 'R';
|
|
27014
|
-
if (0x0600 <= charCode && charCode <= 0x06ff) return arabicTypes[charCode & 0xff];
|
|
27015
|
-
if (0x0700 <= charCode && charCode <= 0x08AC) return 'AL';
|
|
27016
|
-
return 'L';
|
|
27017
|
-
}
|
|
27018
|
-
function computeBidiLevels(str) {
|
|
27019
|
-
const len = str.length;
|
|
27020
|
-
if (len === 0) return null;
|
|
27021
|
-
// eslint-disable-next-line unicorn/no-new-array
|
|
27022
|
-
const types = new Array(len);
|
|
27023
|
-
let numBidi = 0;
|
|
27024
|
-
for(let i = 0; i < len; i++){
|
|
27025
|
-
const t = classifyChar(str.charCodeAt(i));
|
|
27026
|
-
if (t === 'R' || t === 'AL' || t === 'AN') numBidi++;
|
|
27027
|
-
types[i] = t;
|
|
27028
|
-
}
|
|
27029
|
-
if (numBidi === 0) return null;
|
|
27030
|
-
const startLevel = len / numBidi < 0.3 ? 0 : 1;
|
|
27031
|
-
const levels = new Int8Array(len);
|
|
27032
|
-
for(let i = 0; i < len; i++)levels[i] = startLevel;
|
|
27033
|
-
const e = startLevel & 1 ? 'R' : 'L';
|
|
27034
|
-
const sor = e;
|
|
27035
|
-
// W1-W7
|
|
27036
|
-
let lastType = sor;
|
|
27037
|
-
for(let i = 0; i < len; i++){
|
|
27038
|
-
if (types[i] === 'NSM') types[i] = lastType;
|
|
27039
|
-
else lastType = types[i];
|
|
27040
|
-
}
|
|
27041
|
-
lastType = sor;
|
|
27042
|
-
for(let i = 0; i < len; i++){
|
|
27043
|
-
const t = types[i];
|
|
27044
|
-
if (t === 'EN') types[i] = lastType === 'AL' ? 'AN' : 'EN';
|
|
27045
|
-
else if (t === 'R' || t === 'L' || t === 'AL') lastType = t;
|
|
27046
|
-
}
|
|
27047
|
-
for(let i = 0; i < len; i++){
|
|
27048
|
-
if (types[i] === 'AL') types[i] = 'R';
|
|
27049
|
-
}
|
|
27050
|
-
for(let i = 1; i < len - 1; i++){
|
|
27051
|
-
if (types[i] === 'ES' && types[i - 1] === 'EN' && types[i + 1] === 'EN') {
|
|
27052
|
-
types[i] = 'EN';
|
|
27053
|
-
}
|
|
27054
|
-
if (types[i] === 'CS' && (types[i - 1] === 'EN' || types[i - 1] === 'AN') && types[i + 1] === types[i - 1]) {
|
|
27055
|
-
types[i] = types[i - 1];
|
|
27056
|
-
}
|
|
27057
|
-
}
|
|
27058
|
-
for(let i = 0; i < len; i++){
|
|
27059
|
-
if (types[i] !== 'EN') continue;
|
|
27060
|
-
let j;
|
|
27061
|
-
for(j = i - 1; j >= 0 && types[j] === 'ET'; j--)types[j] = 'EN';
|
|
27062
|
-
for(j = i + 1; j < len && types[j] === 'ET'; j++)types[j] = 'EN';
|
|
27063
|
-
}
|
|
27064
|
-
for(let i = 0; i < len; i++){
|
|
27065
|
-
const t = types[i];
|
|
27066
|
-
if (t === 'WS' || t === 'ES' || t === 'ET' || t === 'CS') types[i] = 'ON';
|
|
27067
|
-
}
|
|
27068
|
-
lastType = sor;
|
|
27069
|
-
for(let i = 0; i < len; i++){
|
|
27070
|
-
const t = types[i];
|
|
27071
|
-
if (t === 'EN') types[i] = lastType === 'L' ? 'L' : 'EN';
|
|
27072
|
-
else if (t === 'R' || t === 'L') lastType = t;
|
|
27073
|
-
}
|
|
27074
|
-
// N1-N2
|
|
27075
|
-
for(let i = 0; i < len; i++){
|
|
27076
|
-
if (types[i] !== 'ON') continue;
|
|
27077
|
-
let end = i + 1;
|
|
27078
|
-
while(end < len && types[end] === 'ON')end++;
|
|
27079
|
-
const before = i > 0 ? types[i - 1] : sor;
|
|
27080
|
-
const after = end < len ? types[end] : sor;
|
|
27081
|
-
const bDir = before !== 'L' ? 'R' : 'L';
|
|
27082
|
-
const aDir = after !== 'L' ? 'R' : 'L';
|
|
27083
|
-
if (bDir === aDir) {
|
|
27084
|
-
for(let j = i; j < end; j++)types[j] = bDir;
|
|
27085
|
-
}
|
|
27086
|
-
i = end - 1;
|
|
27087
|
-
}
|
|
27088
|
-
for(let i = 0; i < len; i++){
|
|
27089
|
-
if (types[i] === 'ON') types[i] = e;
|
|
27090
|
-
}
|
|
27091
|
-
// I1-I2
|
|
27092
|
-
for(let i = 0; i < len; i++){
|
|
27093
|
-
const t = types[i];
|
|
27094
|
-
if ((levels[i] & 1) === 0) {
|
|
27095
|
-
if (t === 'R') levels[i]++;
|
|
27096
|
-
else if (t === 'AN' || t === 'EN') levels[i] += 2;
|
|
27097
|
-
} else if (t === 'L' || t === 'AN' || t === 'EN') {
|
|
27098
|
-
levels[i]++;
|
|
27099
|
-
}
|
|
27100
|
-
}
|
|
27101
|
-
return levels;
|
|
27102
|
-
}
|
|
27103
|
-
function computeSegmentLevels(normalized, segStarts) {
|
|
27104
|
-
const bidiLevels = computeBidiLevels(normalized);
|
|
27105
|
-
if (bidiLevels === null) return null;
|
|
27106
|
-
const segLevels = new Int8Array(segStarts.length);
|
|
27107
|
-
for(let i = 0; i < segStarts.length; i++){
|
|
27108
|
-
segLevels[i] = bidiLevels[segStarts[i]];
|
|
27109
|
-
}
|
|
27110
|
-
return segLevels;
|
|
27111
|
-
}
|
|
27112
|
-
|
|
27113
|
-
const collapsibleWhitespaceRunRe = /[ \t\n\r\f]+/g;
|
|
27114
|
-
const needsWhitespaceNormalizationRe = /[\t\n\r\f]| {2,}|^ | $/;
|
|
27115
|
-
function getWhiteSpaceProfile(whiteSpace) {
|
|
27116
|
-
const mode = whiteSpace ?? 'normal';
|
|
27117
|
-
return mode === 'pre-wrap' ? {
|
|
27118
|
-
mode,
|
|
27119
|
-
preserveOrdinarySpaces: true,
|
|
27120
|
-
preserveHardBreaks: true
|
|
27121
|
-
} : {
|
|
27122
|
-
mode,
|
|
27123
|
-
preserveOrdinarySpaces: false,
|
|
27124
|
-
preserveHardBreaks: false
|
|
27125
|
-
};
|
|
27126
|
-
}
|
|
27127
|
-
function normalizeWhitespaceNormal(text) {
|
|
27128
|
-
if (!needsWhitespaceNormalizationRe.test(text)) return text;
|
|
27129
|
-
let normalized = text.replace(collapsibleWhitespaceRunRe, ' ');
|
|
27130
|
-
if (normalized.charCodeAt(0) === 0x20) {
|
|
27131
|
-
normalized = normalized.slice(1);
|
|
27132
|
-
}
|
|
27133
|
-
if (normalized.length > 0 && normalized.charCodeAt(normalized.length - 1) === 0x20) {
|
|
27134
|
-
normalized = normalized.slice(0, -1);
|
|
27135
|
-
}
|
|
27136
|
-
return normalized;
|
|
27137
|
-
}
|
|
27138
|
-
function normalizeWhitespacePreWrap(text) {
|
|
27139
|
-
if (!/[\r\f]/.test(text)) return text.replace(/\r\n/g, '\n');
|
|
27140
|
-
return text.replace(/\r\n/g, '\n').replace(/[\r\f]/g, '\n');
|
|
27141
|
-
}
|
|
27142
|
-
let sharedWordSegmenter = null;
|
|
27143
|
-
let segmenterLocale;
|
|
27144
|
-
function getSharedWordSegmenter() {
|
|
27145
|
-
if (sharedWordSegmenter === null) {
|
|
27146
|
-
sharedWordSegmenter = new Intl.Segmenter(segmenterLocale, {
|
|
27147
|
-
granularity: 'word'
|
|
27148
|
-
});
|
|
27149
|
-
}
|
|
27150
|
-
return sharedWordSegmenter;
|
|
27151
|
-
}
|
|
27152
|
-
const arabicScriptRe = /\p{Script=Arabic}/u;
|
|
27153
|
-
const combiningMarkRe = /\p{M}/u;
|
|
27154
|
-
const decimalDigitRe = /\p{Nd}/u;
|
|
27155
|
-
function containsArabicScript(text) {
|
|
27156
|
-
return arabicScriptRe.test(text);
|
|
27157
|
-
}
|
|
27158
|
-
function isCJK(s) {
|
|
27159
|
-
for (const ch of s){
|
|
27160
|
-
const c = ch.codePointAt(0);
|
|
27161
|
-
if (c >= 0x4E00 && c <= 0x9FFF || c >= 0x3400 && c <= 0x4DBF || c >= 0x20000 && c <= 0x2A6DF || c >= 0x2A700 && c <= 0x2B73F || c >= 0x2B740 && c <= 0x2B81F || c >= 0x2B820 && c <= 0x2CEAF || c >= 0x2CEB0 && c <= 0x2EBEF || c >= 0x30000 && c <= 0x3134F || c >= 0xF900 && c <= 0xFAFF || c >= 0x2F800 && c <= 0x2FA1F || c >= 0x3000 && c <= 0x303F || c >= 0x3040 && c <= 0x309F || c >= 0x30A0 && c <= 0x30FF || c >= 0xAC00 && c <= 0xD7AF || c >= 0xFF00 && c <= 0xFFEF) {
|
|
27162
|
-
return true;
|
|
27163
|
-
}
|
|
27164
|
-
}
|
|
27165
|
-
return false;
|
|
27166
|
-
}
|
|
27167
|
-
const kinsokuStart = new Set([
|
|
27168
|
-
'\uFF0C',
|
|
27169
|
-
'\uFF0E',
|
|
27170
|
-
'\uFF01',
|
|
27171
|
-
'\uFF1A',
|
|
27172
|
-
'\uFF1B',
|
|
27173
|
-
'\uFF1F',
|
|
27174
|
-
'\u3001',
|
|
27175
|
-
'\u3002',
|
|
27176
|
-
'\u30FB',
|
|
27177
|
-
'\uFF09',
|
|
27178
|
-
'\u3015',
|
|
27179
|
-
'\u3009',
|
|
27180
|
-
'\u300B',
|
|
27181
|
-
'\u300D',
|
|
27182
|
-
'\u300F',
|
|
27183
|
-
'\u3011',
|
|
27184
|
-
'\u3017',
|
|
27185
|
-
'\u3019',
|
|
27186
|
-
'\u301B',
|
|
27187
|
-
'\u30FC',
|
|
27188
|
-
'\u3005',
|
|
27189
|
-
'\u303B',
|
|
27190
|
-
'\u309D',
|
|
27191
|
-
'\u309E',
|
|
27192
|
-
'\u30FD',
|
|
27193
|
-
'\u30FE'
|
|
27194
|
-
]);
|
|
27195
|
-
const kinsokuEnd = new Set([
|
|
27196
|
-
'"',
|
|
27197
|
-
'(',
|
|
27198
|
-
'[',
|
|
27199
|
-
'{',
|
|
27200
|
-
'“',
|
|
27201
|
-
'‘',
|
|
27202
|
-
'«',
|
|
27203
|
-
'‹',
|
|
27204
|
-
'\uFF08',
|
|
27205
|
-
'\u3014',
|
|
27206
|
-
'\u3008',
|
|
27207
|
-
'\u300A',
|
|
27208
|
-
'\u300C',
|
|
27209
|
-
'\u300E',
|
|
27210
|
-
'\u3010',
|
|
27211
|
-
'\u3016',
|
|
27212
|
-
'\u3018',
|
|
27213
|
-
'\u301A'
|
|
27214
|
-
]);
|
|
27215
|
-
const forwardStickyGlue = new Set([
|
|
27216
|
-
"'",
|
|
27217
|
-
'’'
|
|
27218
|
-
]);
|
|
27219
|
-
const leftStickyPunctuation = new Set([
|
|
27220
|
-
'.',
|
|
27221
|
-
',',
|
|
27222
|
-
'!',
|
|
27223
|
-
'?',
|
|
27224
|
-
':',
|
|
27225
|
-
';',
|
|
27226
|
-
'\u060C',
|
|
27227
|
-
'\u061B',
|
|
27228
|
-
'\u061F',
|
|
27229
|
-
'\u0964',
|
|
27230
|
-
'\u0965',
|
|
27231
|
-
'\u104A',
|
|
27232
|
-
'\u104B',
|
|
27233
|
-
'\u104C',
|
|
27234
|
-
'\u104D',
|
|
27235
|
-
'\u104F',
|
|
27236
|
-
')',
|
|
27237
|
-
']',
|
|
27238
|
-
'}',
|
|
27239
|
-
'%',
|
|
27240
|
-
'"',
|
|
27241
|
-
'”',
|
|
27242
|
-
'’',
|
|
27243
|
-
'»',
|
|
27244
|
-
'›',
|
|
27245
|
-
'…'
|
|
27246
|
-
]);
|
|
27247
|
-
const arabicNoSpaceTrailingPunctuation = new Set([
|
|
27248
|
-
':',
|
|
27249
|
-
'.',
|
|
27250
|
-
'\u060C',
|
|
27251
|
-
'\u061B'
|
|
27252
|
-
]);
|
|
27253
|
-
const myanmarMedialGlue = new Set([
|
|
27254
|
-
'\u104F'
|
|
27255
|
-
]);
|
|
27256
|
-
const closingQuoteChars = new Set([
|
|
27257
|
-
'”',
|
|
27258
|
-
'’',
|
|
27259
|
-
'»',
|
|
27260
|
-
'›',
|
|
27261
|
-
'\u300D',
|
|
27262
|
-
'\u300F',
|
|
27263
|
-
'\u3011',
|
|
27264
|
-
'\u300B',
|
|
27265
|
-
'\u3009',
|
|
27266
|
-
'\u3015',
|
|
27267
|
-
'\uFF09'
|
|
27268
|
-
]);
|
|
27269
|
-
function isLeftStickyPunctuationSegment(segment) {
|
|
27270
|
-
if (isEscapedQuoteClusterSegment(segment)) return true;
|
|
27271
|
-
let sawPunctuation = false;
|
|
27272
|
-
for (const ch of segment){
|
|
27273
|
-
if (leftStickyPunctuation.has(ch)) {
|
|
27274
|
-
sawPunctuation = true;
|
|
27275
|
-
continue;
|
|
27276
|
-
}
|
|
27277
|
-
if (sawPunctuation && combiningMarkRe.test(ch)) continue;
|
|
27278
|
-
return false;
|
|
27279
|
-
}
|
|
27280
|
-
return sawPunctuation;
|
|
27281
|
-
}
|
|
27282
|
-
function isCJKLineStartProhibitedSegment(segment) {
|
|
27283
|
-
for (const ch of segment){
|
|
27284
|
-
if (!kinsokuStart.has(ch) && !leftStickyPunctuation.has(ch)) return false;
|
|
27285
|
-
}
|
|
27286
|
-
return segment.length > 0;
|
|
27287
|
-
}
|
|
27288
|
-
function isForwardStickyClusterSegment(segment) {
|
|
27289
|
-
if (isEscapedQuoteClusterSegment(segment)) return true;
|
|
27290
|
-
for (const ch of segment){
|
|
27291
|
-
if (!kinsokuEnd.has(ch) && !forwardStickyGlue.has(ch) && !combiningMarkRe.test(ch)) return false;
|
|
27292
|
-
}
|
|
27293
|
-
return segment.length > 0;
|
|
27294
|
-
}
|
|
27295
|
-
function isEscapedQuoteClusterSegment(segment) {
|
|
27296
|
-
let sawQuote = false;
|
|
27297
|
-
for (const ch of segment){
|
|
27298
|
-
if (ch === '\\' || combiningMarkRe.test(ch)) continue;
|
|
27299
|
-
if (kinsokuEnd.has(ch) || leftStickyPunctuation.has(ch) || forwardStickyGlue.has(ch)) {
|
|
27300
|
-
sawQuote = true;
|
|
27301
|
-
continue;
|
|
27302
|
-
}
|
|
27303
|
-
return false;
|
|
27304
|
-
}
|
|
27305
|
-
return sawQuote;
|
|
27306
|
-
}
|
|
27307
|
-
function splitTrailingForwardStickyCluster(text) {
|
|
27308
|
-
const chars = Array.from(text);
|
|
27309
|
-
let splitIndex = chars.length;
|
|
27310
|
-
while(splitIndex > 0){
|
|
27311
|
-
const ch = chars[splitIndex - 1];
|
|
27312
|
-
if (combiningMarkRe.test(ch)) {
|
|
27313
|
-
splitIndex--;
|
|
27314
|
-
continue;
|
|
27315
|
-
}
|
|
27316
|
-
if (kinsokuEnd.has(ch) || forwardStickyGlue.has(ch)) {
|
|
27317
|
-
splitIndex--;
|
|
27318
|
-
continue;
|
|
27319
|
-
}
|
|
27320
|
-
break;
|
|
27321
|
-
}
|
|
27322
|
-
if (splitIndex <= 0 || splitIndex === chars.length) return null;
|
|
27323
|
-
return {
|
|
27324
|
-
head: chars.slice(0, splitIndex).join(''),
|
|
27325
|
-
tail: chars.slice(splitIndex).join('')
|
|
27326
|
-
};
|
|
27327
|
-
}
|
|
27328
|
-
function isRepeatedSingleCharRun(segment, ch) {
|
|
27329
|
-
if (segment.length === 0) return false;
|
|
27330
|
-
for (const part of segment){
|
|
27331
|
-
if (part !== ch) return false;
|
|
27332
|
-
}
|
|
27333
|
-
return true;
|
|
27334
|
-
}
|
|
27335
|
-
function endsWithArabicNoSpacePunctuation(segment) {
|
|
27336
|
-
if (!containsArabicScript(segment) || segment.length === 0) return false;
|
|
27337
|
-
return arabicNoSpaceTrailingPunctuation.has(segment[segment.length - 1]);
|
|
27338
|
-
}
|
|
27339
|
-
function endsWithMyanmarMedialGlue(segment) {
|
|
27340
|
-
if (segment.length === 0) return false;
|
|
27341
|
-
return myanmarMedialGlue.has(segment[segment.length - 1]);
|
|
27342
|
-
}
|
|
27343
|
-
function splitLeadingSpaceAndMarks(segment) {
|
|
27344
|
-
if (segment.length < 2 || segment[0] !== ' ') return null;
|
|
27345
|
-
const marks = segment.slice(1);
|
|
27346
|
-
if (/^\p{M}+$/u.test(marks)) {
|
|
27347
|
-
return {
|
|
27348
|
-
space: ' ',
|
|
27349
|
-
marks
|
|
27350
|
-
};
|
|
27351
|
-
}
|
|
27352
|
-
return null;
|
|
27353
|
-
}
|
|
27354
|
-
function endsWithClosingQuote(text) {
|
|
27355
|
-
for(let i = text.length - 1; i >= 0; i--){
|
|
27356
|
-
const ch = text[i];
|
|
27357
|
-
if (closingQuoteChars.has(ch)) return true;
|
|
27358
|
-
if (!leftStickyPunctuation.has(ch)) return false;
|
|
27359
|
-
}
|
|
27360
|
-
return false;
|
|
27361
|
-
}
|
|
27362
|
-
function classifySegmentBreakChar(ch, whiteSpaceProfile) {
|
|
27363
|
-
if (whiteSpaceProfile.preserveOrdinarySpaces || whiteSpaceProfile.preserveHardBreaks) {
|
|
27364
|
-
if (ch === ' ') return 'preserved-space';
|
|
27365
|
-
if (ch === '\t') return 'tab';
|
|
27366
|
-
if (whiteSpaceProfile.preserveHardBreaks && ch === '\n') return 'hard-break';
|
|
27367
|
-
}
|
|
27368
|
-
if (ch === ' ') return 'space';
|
|
27369
|
-
if (ch === '\u00A0' || ch === '\u202F' || ch === '\u2060' || ch === '\uFEFF') {
|
|
27370
|
-
return 'glue';
|
|
27371
|
-
}
|
|
27372
|
-
if (ch === '\u200B') return 'zero-width-break';
|
|
27373
|
-
if (ch === '\u00AD') return 'soft-hyphen';
|
|
27374
|
-
return 'text';
|
|
27375
|
-
}
|
|
27376
|
-
function splitSegmentByBreakKind(segment, isWordLike, start, whiteSpaceProfile) {
|
|
27377
|
-
const pieces = [];
|
|
27378
|
-
let currentKind = null;
|
|
27379
|
-
let currentText = '';
|
|
27380
|
-
let currentStart = start;
|
|
27381
|
-
let currentWordLike = false;
|
|
27382
|
-
let offset = 0;
|
|
27383
|
-
for (const ch of segment){
|
|
27384
|
-
const kind = classifySegmentBreakChar(ch, whiteSpaceProfile);
|
|
27385
|
-
const wordLike = kind === 'text' && isWordLike;
|
|
27386
|
-
if (currentKind !== null && kind === currentKind && wordLike === currentWordLike) {
|
|
27387
|
-
currentText += ch;
|
|
27388
|
-
offset += ch.length;
|
|
27389
|
-
continue;
|
|
27390
|
-
}
|
|
27391
|
-
if (currentKind !== null) {
|
|
27392
|
-
pieces.push({
|
|
27393
|
-
text: currentText,
|
|
27394
|
-
isWordLike: currentWordLike,
|
|
27395
|
-
kind: currentKind,
|
|
27396
|
-
start: currentStart
|
|
27397
|
-
});
|
|
27398
|
-
}
|
|
27399
|
-
currentKind = kind;
|
|
27400
|
-
currentText = ch;
|
|
27401
|
-
currentStart = start + offset;
|
|
27402
|
-
currentWordLike = wordLike;
|
|
27403
|
-
offset += ch.length;
|
|
27404
|
-
}
|
|
27405
|
-
if (currentKind !== null) {
|
|
27406
|
-
pieces.push({
|
|
27407
|
-
text: currentText,
|
|
27408
|
-
isWordLike: currentWordLike,
|
|
27409
|
-
kind: currentKind,
|
|
27410
|
-
start: currentStart
|
|
27411
|
-
});
|
|
27412
|
-
}
|
|
27413
|
-
return pieces;
|
|
27414
|
-
}
|
|
27415
|
-
function isTextRunBoundary(kind) {
|
|
27416
|
-
return kind === 'space' || kind === 'preserved-space' || kind === 'zero-width-break' || kind === 'hard-break';
|
|
27417
|
-
}
|
|
27418
|
-
const urlSchemeSegmentRe = /^[A-Za-z][A-Za-z0-9+.-]*:$/;
|
|
27419
|
-
function isUrlLikeRunStart(segmentation, index) {
|
|
27420
|
-
const text = segmentation.texts[index];
|
|
27421
|
-
if (text.startsWith('www.')) return true;
|
|
27422
|
-
return urlSchemeSegmentRe.test(text) && index + 1 < segmentation.len && segmentation.kinds[index + 1] === 'text' && segmentation.texts[index + 1] === '//';
|
|
27423
|
-
}
|
|
27424
|
-
function isUrlQueryBoundarySegment(text) {
|
|
27425
|
-
return text.includes('?') && (text.includes('://') || text.startsWith('www.'));
|
|
27426
|
-
}
|
|
27427
|
-
function mergeUrlLikeRuns(segmentation) {
|
|
27428
|
-
const texts = segmentation.texts.slice();
|
|
27429
|
-
const isWordLike = segmentation.isWordLike.slice();
|
|
27430
|
-
const kinds = segmentation.kinds.slice();
|
|
27431
|
-
const starts = segmentation.starts.slice();
|
|
27432
|
-
for(let i = 0; i < segmentation.len; i++){
|
|
27433
|
-
if (kinds[i] !== 'text' || !isUrlLikeRunStart(segmentation, i)) continue;
|
|
27434
|
-
let j = i + 1;
|
|
27435
|
-
while(j < segmentation.len && !isTextRunBoundary(kinds[j])){
|
|
27436
|
-
texts[i] += texts[j];
|
|
27437
|
-
isWordLike[i] = true;
|
|
27438
|
-
const endsQueryPrefix = texts[j].includes('?');
|
|
27439
|
-
kinds[j] = 'text';
|
|
27440
|
-
texts[j] = '';
|
|
27441
|
-
j++;
|
|
27442
|
-
if (endsQueryPrefix) break;
|
|
27443
|
-
}
|
|
27444
|
-
}
|
|
27445
|
-
let compactLen = 0;
|
|
27446
|
-
for(let read = 0; read < texts.length; read++){
|
|
27447
|
-
const text = texts[read];
|
|
27448
|
-
if (text.length === 0) continue;
|
|
27449
|
-
if (compactLen !== read) {
|
|
27450
|
-
texts[compactLen] = text;
|
|
27451
|
-
isWordLike[compactLen] = isWordLike[read];
|
|
27452
|
-
kinds[compactLen] = kinds[read];
|
|
27453
|
-
starts[compactLen] = starts[read];
|
|
27454
|
-
}
|
|
27455
|
-
compactLen++;
|
|
27456
|
-
}
|
|
27457
|
-
texts.length = compactLen;
|
|
27458
|
-
isWordLike.length = compactLen;
|
|
27459
|
-
kinds.length = compactLen;
|
|
27460
|
-
starts.length = compactLen;
|
|
27461
|
-
return {
|
|
27462
|
-
len: compactLen,
|
|
27463
|
-
texts,
|
|
27464
|
-
isWordLike,
|
|
27465
|
-
kinds,
|
|
27466
|
-
starts
|
|
27467
|
-
};
|
|
27468
|
-
}
|
|
27469
|
-
function mergeUrlQueryRuns(segmentation) {
|
|
27470
|
-
const texts = [];
|
|
27471
|
-
const isWordLike = [];
|
|
27472
|
-
const kinds = [];
|
|
27473
|
-
const starts = [];
|
|
27474
|
-
for(let i = 0; i < segmentation.len; i++){
|
|
27475
|
-
const text = segmentation.texts[i];
|
|
27476
|
-
texts.push(text);
|
|
27477
|
-
isWordLike.push(segmentation.isWordLike[i]);
|
|
27478
|
-
kinds.push(segmentation.kinds[i]);
|
|
27479
|
-
starts.push(segmentation.starts[i]);
|
|
27480
|
-
if (!isUrlQueryBoundarySegment(text)) continue;
|
|
27481
|
-
const nextIndex = i + 1;
|
|
27482
|
-
if (nextIndex >= segmentation.len || isTextRunBoundary(segmentation.kinds[nextIndex])) {
|
|
27483
|
-
continue;
|
|
27484
|
-
}
|
|
27485
|
-
let queryText = '';
|
|
27486
|
-
const queryStart = segmentation.starts[nextIndex];
|
|
27487
|
-
let j = nextIndex;
|
|
27488
|
-
while(j < segmentation.len && !isTextRunBoundary(segmentation.kinds[j])){
|
|
27489
|
-
queryText += segmentation.texts[j];
|
|
27490
|
-
j++;
|
|
27491
|
-
}
|
|
27492
|
-
if (queryText.length > 0) {
|
|
27493
|
-
texts.push(queryText);
|
|
27494
|
-
isWordLike.push(true);
|
|
27495
|
-
kinds.push('text');
|
|
27496
|
-
starts.push(queryStart);
|
|
27497
|
-
i = j - 1;
|
|
27498
|
-
}
|
|
27499
|
-
}
|
|
27500
|
-
return {
|
|
27501
|
-
len: texts.length,
|
|
27502
|
-
texts,
|
|
27503
|
-
isWordLike,
|
|
27504
|
-
kinds,
|
|
27505
|
-
starts
|
|
27506
|
-
};
|
|
27507
|
-
}
|
|
27508
|
-
const numericJoinerChars = new Set([
|
|
27509
|
-
':',
|
|
27510
|
-
'-',
|
|
27511
|
-
'/',
|
|
27512
|
-
'×',
|
|
27513
|
-
',',
|
|
27514
|
-
'.',
|
|
27515
|
-
'+',
|
|
27516
|
-
'\u2013',
|
|
27517
|
-
'\u2014'
|
|
27518
|
-
]);
|
|
27519
|
-
const asciiPunctuationChainSegmentRe = /^[A-Za-z0-9_]+[,:;]*$/;
|
|
27520
|
-
const asciiPunctuationChainTrailingJoinersRe = /[,:;]+$/;
|
|
27521
|
-
function segmentContainsDecimalDigit(text) {
|
|
27522
|
-
for (const ch of text){
|
|
27523
|
-
if (decimalDigitRe.test(ch)) return true;
|
|
27524
|
-
}
|
|
27525
|
-
return false;
|
|
27526
|
-
}
|
|
27527
|
-
function isNumericRunSegment(text) {
|
|
27528
|
-
if (text.length === 0) return false;
|
|
27529
|
-
for (const ch of text){
|
|
27530
|
-
if (decimalDigitRe.test(ch) || numericJoinerChars.has(ch)) continue;
|
|
27531
|
-
return false;
|
|
27532
|
-
}
|
|
27533
|
-
return true;
|
|
27534
|
-
}
|
|
27535
|
-
function mergeNumericRuns(segmentation) {
|
|
27536
|
-
const texts = [];
|
|
27537
|
-
const isWordLike = [];
|
|
27538
|
-
const kinds = [];
|
|
27539
|
-
const starts = [];
|
|
27540
|
-
for(let i = 0; i < segmentation.len; i++){
|
|
27541
|
-
const text = segmentation.texts[i];
|
|
27542
|
-
const kind = segmentation.kinds[i];
|
|
27543
|
-
if (kind === 'text' && isNumericRunSegment(text) && segmentContainsDecimalDigit(text)) {
|
|
27544
|
-
let mergedText = text;
|
|
27545
|
-
let j = i + 1;
|
|
27546
|
-
while(j < segmentation.len && segmentation.kinds[j] === 'text' && isNumericRunSegment(segmentation.texts[j])){
|
|
27547
|
-
mergedText += segmentation.texts[j];
|
|
27548
|
-
j++;
|
|
27549
|
-
}
|
|
27550
|
-
texts.push(mergedText);
|
|
27551
|
-
isWordLike.push(true);
|
|
27552
|
-
kinds.push('text');
|
|
27553
|
-
starts.push(segmentation.starts[i]);
|
|
27554
|
-
i = j - 1;
|
|
27555
|
-
continue;
|
|
27556
|
-
}
|
|
27557
|
-
texts.push(text);
|
|
27558
|
-
isWordLike.push(segmentation.isWordLike[i]);
|
|
27559
|
-
kinds.push(kind);
|
|
27560
|
-
starts.push(segmentation.starts[i]);
|
|
27561
|
-
}
|
|
27562
|
-
return {
|
|
27563
|
-
len: texts.length,
|
|
27564
|
-
texts,
|
|
27565
|
-
isWordLike,
|
|
27566
|
-
kinds,
|
|
27567
|
-
starts
|
|
27568
|
-
};
|
|
27569
|
-
}
|
|
27570
|
-
function mergeAsciiPunctuationChains(segmentation) {
|
|
27571
|
-
const texts = [];
|
|
27572
|
-
const isWordLike = [];
|
|
27573
|
-
const kinds = [];
|
|
27574
|
-
const starts = [];
|
|
27575
|
-
for(let i = 0; i < segmentation.len; i++){
|
|
27576
|
-
const text = segmentation.texts[i];
|
|
27577
|
-
const kind = segmentation.kinds[i];
|
|
27578
|
-
const wordLike = segmentation.isWordLike[i];
|
|
27579
|
-
if (kind === 'text' && wordLike && asciiPunctuationChainSegmentRe.test(text)) {
|
|
27580
|
-
let mergedText = text;
|
|
27581
|
-
let j = i + 1;
|
|
27582
|
-
while(asciiPunctuationChainTrailingJoinersRe.test(mergedText) && j < segmentation.len && segmentation.kinds[j] === 'text' && segmentation.isWordLike[j] && asciiPunctuationChainSegmentRe.test(segmentation.texts[j])){
|
|
27583
|
-
mergedText += segmentation.texts[j];
|
|
27584
|
-
j++;
|
|
27585
|
-
}
|
|
27586
|
-
texts.push(mergedText);
|
|
27587
|
-
isWordLike.push(true);
|
|
27588
|
-
kinds.push('text');
|
|
27589
|
-
starts.push(segmentation.starts[i]);
|
|
27590
|
-
i = j - 1;
|
|
27591
|
-
continue;
|
|
27592
|
-
}
|
|
27593
|
-
texts.push(text);
|
|
27594
|
-
isWordLike.push(wordLike);
|
|
27595
|
-
kinds.push(kind);
|
|
27596
|
-
starts.push(segmentation.starts[i]);
|
|
27597
|
-
}
|
|
27598
|
-
return {
|
|
27599
|
-
len: texts.length,
|
|
27600
|
-
texts,
|
|
27601
|
-
isWordLike,
|
|
27602
|
-
kinds,
|
|
27603
|
-
starts
|
|
27604
|
-
};
|
|
27605
|
-
}
|
|
27606
|
-
function splitHyphenatedNumericRuns(segmentation) {
|
|
27607
|
-
const texts = [];
|
|
27608
|
-
const isWordLike = [];
|
|
27609
|
-
const kinds = [];
|
|
27610
|
-
const starts = [];
|
|
27611
|
-
for(let i = 0; i < segmentation.len; i++){
|
|
27612
|
-
const text = segmentation.texts[i];
|
|
27613
|
-
if (segmentation.kinds[i] === 'text' && text.includes('-')) {
|
|
27614
|
-
const parts = text.split('-');
|
|
27615
|
-
let shouldSplit = parts.length > 1;
|
|
27616
|
-
for(let j = 0; j < parts.length; j++){
|
|
27617
|
-
const part = parts[j];
|
|
27618
|
-
if (!shouldSplit) break;
|
|
27619
|
-
if (part.length === 0 || !segmentContainsDecimalDigit(part) || !isNumericRunSegment(part)) {
|
|
27620
|
-
shouldSplit = false;
|
|
27621
|
-
}
|
|
27622
|
-
}
|
|
27623
|
-
if (shouldSplit) {
|
|
27624
|
-
let offset = 0;
|
|
27625
|
-
for(let j = 0; j < parts.length; j++){
|
|
27626
|
-
const part = parts[j];
|
|
27627
|
-
const splitText = j < parts.length - 1 ? `${part}-` : part;
|
|
27628
|
-
texts.push(splitText);
|
|
27629
|
-
isWordLike.push(true);
|
|
27630
|
-
kinds.push('text');
|
|
27631
|
-
starts.push(segmentation.starts[i] + offset);
|
|
27632
|
-
offset += splitText.length;
|
|
27633
|
-
}
|
|
27634
|
-
continue;
|
|
27635
|
-
}
|
|
27636
|
-
}
|
|
27637
|
-
texts.push(text);
|
|
27638
|
-
isWordLike.push(segmentation.isWordLike[i]);
|
|
27639
|
-
kinds.push(segmentation.kinds[i]);
|
|
27640
|
-
starts.push(segmentation.starts[i]);
|
|
27641
|
-
}
|
|
27642
|
-
return {
|
|
27643
|
-
len: texts.length,
|
|
27644
|
-
texts,
|
|
27645
|
-
isWordLike,
|
|
27646
|
-
kinds,
|
|
27647
|
-
starts
|
|
27648
|
-
};
|
|
27649
|
-
}
|
|
27650
|
-
function mergeGlueConnectedTextRuns(segmentation) {
|
|
27651
|
-
const texts = [];
|
|
27652
|
-
const isWordLike = [];
|
|
27653
|
-
const kinds = [];
|
|
27654
|
-
const starts = [];
|
|
27655
|
-
let read = 0;
|
|
27656
|
-
while(read < segmentation.len){
|
|
27657
|
-
let text = segmentation.texts[read];
|
|
27658
|
-
let wordLike = segmentation.isWordLike[read];
|
|
27659
|
-
let kind = segmentation.kinds[read];
|
|
27660
|
-
let start = segmentation.starts[read];
|
|
27661
|
-
if (kind === 'glue') {
|
|
27662
|
-
let glueText = text;
|
|
27663
|
-
const glueStart = start;
|
|
27664
|
-
read++;
|
|
27665
|
-
while(read < segmentation.len && segmentation.kinds[read] === 'glue'){
|
|
27666
|
-
glueText += segmentation.texts[read];
|
|
27667
|
-
read++;
|
|
27668
|
-
}
|
|
27669
|
-
if (read < segmentation.len && segmentation.kinds[read] === 'text') {
|
|
27670
|
-
text = glueText + segmentation.texts[read];
|
|
27671
|
-
wordLike = segmentation.isWordLike[read];
|
|
27672
|
-
kind = 'text';
|
|
27673
|
-
start = glueStart;
|
|
27674
|
-
read++;
|
|
27675
|
-
} else {
|
|
27676
|
-
texts.push(glueText);
|
|
27677
|
-
isWordLike.push(false);
|
|
27678
|
-
kinds.push('glue');
|
|
27679
|
-
starts.push(glueStart);
|
|
27680
|
-
continue;
|
|
27681
|
-
}
|
|
27682
|
-
} else {
|
|
27683
|
-
read++;
|
|
27684
|
-
}
|
|
27685
|
-
if (kind === 'text') {
|
|
27686
|
-
while(read < segmentation.len && segmentation.kinds[read] === 'glue'){
|
|
27687
|
-
let glueText = '';
|
|
27688
|
-
while(read < segmentation.len && segmentation.kinds[read] === 'glue'){
|
|
27689
|
-
glueText += segmentation.texts[read];
|
|
27690
|
-
read++;
|
|
27691
|
-
}
|
|
27692
|
-
if (read < segmentation.len && segmentation.kinds[read] === 'text') {
|
|
27693
|
-
text += glueText + segmentation.texts[read];
|
|
27694
|
-
wordLike = wordLike || segmentation.isWordLike[read];
|
|
27695
|
-
read++;
|
|
27696
|
-
continue;
|
|
27697
|
-
}
|
|
27698
|
-
text += glueText;
|
|
27699
|
-
}
|
|
27700
|
-
}
|
|
27701
|
-
texts.push(text);
|
|
27702
|
-
isWordLike.push(wordLike);
|
|
27703
|
-
kinds.push(kind);
|
|
27704
|
-
starts.push(start);
|
|
27705
|
-
}
|
|
27706
|
-
return {
|
|
27707
|
-
len: texts.length,
|
|
27708
|
-
texts,
|
|
27709
|
-
isWordLike,
|
|
27710
|
-
kinds,
|
|
27711
|
-
starts
|
|
27712
|
-
};
|
|
27713
|
-
}
|
|
27714
|
-
function carryTrailingForwardStickyAcrossCJKBoundary(segmentation) {
|
|
27715
|
-
const texts = segmentation.texts.slice();
|
|
27716
|
-
const isWordLike = segmentation.isWordLike.slice();
|
|
27717
|
-
const kinds = segmentation.kinds.slice();
|
|
27718
|
-
const starts = segmentation.starts.slice();
|
|
27719
|
-
for(let i = 0; i < texts.length - 1; i++){
|
|
27720
|
-
if (kinds[i] !== 'text' || kinds[i + 1] !== 'text') continue;
|
|
27721
|
-
if (!isCJK(texts[i]) || !isCJK(texts[i + 1])) continue;
|
|
27722
|
-
const split = splitTrailingForwardStickyCluster(texts[i]);
|
|
27723
|
-
if (split === null) continue;
|
|
27724
|
-
texts[i] = split.head;
|
|
27725
|
-
texts[i + 1] = split.tail + texts[i + 1];
|
|
27726
|
-
starts[i + 1] = starts[i] + split.head.length;
|
|
27727
|
-
}
|
|
27728
|
-
return {
|
|
27729
|
-
len: texts.length,
|
|
27730
|
-
texts,
|
|
27731
|
-
isWordLike,
|
|
27732
|
-
kinds,
|
|
27733
|
-
starts
|
|
27734
|
-
};
|
|
27735
|
-
}
|
|
27736
|
-
function buildMergedSegmentation(normalized, profile, whiteSpaceProfile) {
|
|
27737
|
-
const wordSegmenter = getSharedWordSegmenter();
|
|
27738
|
-
let mergedLen = 0;
|
|
27739
|
-
const mergedTexts = [];
|
|
27740
|
-
const mergedWordLike = [];
|
|
27741
|
-
const mergedKinds = [];
|
|
27742
|
-
const mergedStarts = [];
|
|
27743
|
-
for (const s of wordSegmenter.segment(normalized)){
|
|
27744
|
-
for (const piece of splitSegmentByBreakKind(s.segment, s.isWordLike ?? false, s.index, whiteSpaceProfile)){
|
|
27745
|
-
const isText = piece.kind === 'text';
|
|
27746
|
-
if (profile.carryCJKAfterClosingQuote && isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && isCJK(piece.text) && isCJK(mergedTexts[mergedLen - 1]) && endsWithClosingQuote(mergedTexts[mergedLen - 1])) {
|
|
27747
|
-
mergedTexts[mergedLen - 1] += piece.text;
|
|
27748
|
-
mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
|
|
27749
|
-
} else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && isCJKLineStartProhibitedSegment(piece.text) && isCJK(mergedTexts[mergedLen - 1])) {
|
|
27750
|
-
mergedTexts[mergedLen - 1] += piece.text;
|
|
27751
|
-
mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
|
|
27752
|
-
} else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && endsWithMyanmarMedialGlue(mergedTexts[mergedLen - 1])) {
|
|
27753
|
-
mergedTexts[mergedLen - 1] += piece.text;
|
|
27754
|
-
mergedWordLike[mergedLen - 1] = mergedWordLike[mergedLen - 1] || piece.isWordLike;
|
|
27755
|
-
} else if (isText && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && piece.isWordLike && containsArabicScript(piece.text) && endsWithArabicNoSpacePunctuation(mergedTexts[mergedLen - 1])) {
|
|
27756
|
-
mergedTexts[mergedLen - 1] += piece.text;
|
|
27757
|
-
mergedWordLike[mergedLen - 1] = true;
|
|
27758
|
-
} else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && piece.text.length === 1 && piece.text !== '-' && piece.text !== '—' && isRepeatedSingleCharRun(mergedTexts[mergedLen - 1], piece.text)) {
|
|
27759
|
-
mergedTexts[mergedLen - 1] += piece.text;
|
|
27760
|
-
} else if (isText && !piece.isWordLike && mergedLen > 0 && mergedKinds[mergedLen - 1] === 'text' && (isLeftStickyPunctuationSegment(piece.text) || piece.text === '-' && mergedWordLike[mergedLen - 1])) {
|
|
27761
|
-
mergedTexts[mergedLen - 1] += piece.text;
|
|
27762
|
-
} else {
|
|
27763
|
-
mergedTexts[mergedLen] = piece.text;
|
|
27764
|
-
mergedWordLike[mergedLen] = piece.isWordLike;
|
|
27765
|
-
mergedKinds[mergedLen] = piece.kind;
|
|
27766
|
-
mergedStarts[mergedLen] = piece.start;
|
|
27767
|
-
mergedLen++;
|
|
27768
|
-
}
|
|
27769
|
-
}
|
|
27770
|
-
}
|
|
27771
|
-
for(let i = 1; i < mergedLen; i++){
|
|
27772
|
-
if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isEscapedQuoteClusterSegment(mergedTexts[i]) && mergedKinds[i - 1] === 'text') {
|
|
27773
|
-
mergedTexts[i - 1] += mergedTexts[i];
|
|
27774
|
-
mergedWordLike[i - 1] = mergedWordLike[i - 1] || mergedWordLike[i];
|
|
27775
|
-
mergedTexts[i] = '';
|
|
27776
|
-
}
|
|
27777
|
-
}
|
|
27778
|
-
for(let i = mergedLen - 2; i >= 0; i--){
|
|
27779
|
-
if (mergedKinds[i] === 'text' && !mergedWordLike[i] && isForwardStickyClusterSegment(mergedTexts[i])) {
|
|
27780
|
-
let j = i + 1;
|
|
27781
|
-
while(j < mergedLen && mergedTexts[j] === '')j++;
|
|
27782
|
-
if (j < mergedLen && mergedKinds[j] === 'text') {
|
|
27783
|
-
mergedTexts[j] = mergedTexts[i] + mergedTexts[j];
|
|
27784
|
-
mergedStarts[j] = mergedStarts[i];
|
|
27785
|
-
mergedTexts[i] = '';
|
|
27786
|
-
}
|
|
27787
|
-
}
|
|
27788
|
-
}
|
|
27789
|
-
let compactLen = 0;
|
|
27790
|
-
for(let read = 0; read < mergedLen; read++){
|
|
27791
|
-
const text = mergedTexts[read];
|
|
27792
|
-
if (text.length === 0) continue;
|
|
27793
|
-
if (compactLen !== read) {
|
|
27794
|
-
mergedTexts[compactLen] = text;
|
|
27795
|
-
mergedWordLike[compactLen] = mergedWordLike[read];
|
|
27796
|
-
mergedKinds[compactLen] = mergedKinds[read];
|
|
27797
|
-
mergedStarts[compactLen] = mergedStarts[read];
|
|
27798
|
-
}
|
|
27799
|
-
compactLen++;
|
|
27800
|
-
}
|
|
27801
|
-
mergedTexts.length = compactLen;
|
|
27802
|
-
mergedWordLike.length = compactLen;
|
|
27803
|
-
mergedKinds.length = compactLen;
|
|
27804
|
-
mergedStarts.length = compactLen;
|
|
27805
|
-
const compacted = mergeGlueConnectedTextRuns({
|
|
27806
|
-
len: compactLen,
|
|
27807
|
-
texts: mergedTexts,
|
|
27808
|
-
isWordLike: mergedWordLike,
|
|
27809
|
-
kinds: mergedKinds,
|
|
27810
|
-
starts: mergedStarts
|
|
27811
|
-
});
|
|
27812
|
-
const withMergedUrls = carryTrailingForwardStickyAcrossCJKBoundary(mergeAsciiPunctuationChains(splitHyphenatedNumericRuns(mergeNumericRuns(mergeUrlQueryRuns(mergeUrlLikeRuns(compacted))))));
|
|
27813
|
-
for(let i = 0; i < withMergedUrls.len - 1; i++){
|
|
27814
|
-
const split = splitLeadingSpaceAndMarks(withMergedUrls.texts[i]);
|
|
27815
|
-
if (split === null) continue;
|
|
27816
|
-
if (withMergedUrls.kinds[i] !== 'space' && withMergedUrls.kinds[i] !== 'preserved-space' || withMergedUrls.kinds[i + 1] !== 'text' || !containsArabicScript(withMergedUrls.texts[i + 1])) {
|
|
27817
|
-
continue;
|
|
27818
|
-
}
|
|
27819
|
-
withMergedUrls.texts[i] = split.space;
|
|
27820
|
-
withMergedUrls.isWordLike[i] = false;
|
|
27821
|
-
withMergedUrls.kinds[i] = withMergedUrls.kinds[i] === 'preserved-space' ? 'preserved-space' : 'space';
|
|
27822
|
-
withMergedUrls.texts[i + 1] = split.marks + withMergedUrls.texts[i + 1];
|
|
27823
|
-
withMergedUrls.starts[i + 1] = withMergedUrls.starts[i] + split.space.length;
|
|
27824
|
-
}
|
|
27825
|
-
return withMergedUrls;
|
|
27826
|
-
}
|
|
27827
|
-
function compileAnalysisChunks(segmentation, whiteSpaceProfile) {
|
|
27828
|
-
if (segmentation.len === 0) return [];
|
|
27829
|
-
if (!whiteSpaceProfile.preserveHardBreaks) {
|
|
27830
|
-
return [
|
|
27831
|
-
{
|
|
27832
|
-
startSegmentIndex: 0,
|
|
27833
|
-
endSegmentIndex: segmentation.len,
|
|
27834
|
-
consumedEndSegmentIndex: segmentation.len
|
|
27835
|
-
}
|
|
27836
|
-
];
|
|
27837
|
-
}
|
|
27838
|
-
const chunks = [];
|
|
27839
|
-
let startSegmentIndex = 0;
|
|
27840
|
-
for(let i = 0; i < segmentation.len; i++){
|
|
27841
|
-
if (segmentation.kinds[i] !== 'hard-break') continue;
|
|
27842
|
-
chunks.push({
|
|
27843
|
-
startSegmentIndex,
|
|
27844
|
-
endSegmentIndex: i,
|
|
27845
|
-
consumedEndSegmentIndex: i + 1
|
|
27846
|
-
});
|
|
27847
|
-
startSegmentIndex = i + 1;
|
|
27848
|
-
}
|
|
27849
|
-
if (startSegmentIndex < segmentation.len) {
|
|
27850
|
-
chunks.push({
|
|
27851
|
-
startSegmentIndex,
|
|
27852
|
-
endSegmentIndex: segmentation.len,
|
|
27853
|
-
consumedEndSegmentIndex: segmentation.len
|
|
27854
|
-
});
|
|
27855
|
-
}
|
|
27856
|
-
return chunks;
|
|
27857
|
-
}
|
|
27858
|
-
function analyzeText(text, profile, whiteSpace = 'normal') {
|
|
27859
|
-
const whiteSpaceProfile = getWhiteSpaceProfile(whiteSpace);
|
|
27860
|
-
const normalized = whiteSpaceProfile.mode === 'pre-wrap' ? normalizeWhitespacePreWrap(text) : normalizeWhitespaceNormal(text);
|
|
27861
|
-
if (normalized.length === 0) {
|
|
27862
|
-
return {
|
|
27863
|
-
normalized,
|
|
27864
|
-
chunks: [],
|
|
27865
|
-
len: 0,
|
|
27866
|
-
texts: [],
|
|
27867
|
-
isWordLike: [],
|
|
27868
|
-
kinds: [],
|
|
27869
|
-
starts: []
|
|
27870
|
-
};
|
|
27871
|
-
}
|
|
27872
|
-
const segmentation = buildMergedSegmentation(normalized, profile, whiteSpaceProfile);
|
|
27873
|
-
return {
|
|
27874
|
-
normalized,
|
|
27875
|
-
chunks: compileAnalysisChunks(segmentation, whiteSpaceProfile),
|
|
27876
|
-
...segmentation
|
|
27877
|
-
};
|
|
27878
|
-
}
|
|
27879
|
-
|
|
27880
|
-
let measureContext = null;
|
|
27881
|
-
const segmentMetricCaches = new Map();
|
|
27882
|
-
let cachedEngineProfile = null;
|
|
27883
|
-
const emojiPresentationRe = /\p{Emoji_Presentation}/u;
|
|
27884
|
-
const maybeEmojiRe = /[\p{Emoji_Presentation}\p{Extended_Pictographic}\p{Regional_Indicator}\uFE0F\u20E3]/u;
|
|
27885
|
-
let sharedGraphemeSegmenter$1 = null;
|
|
27886
|
-
const emojiCorrectionCache = new Map();
|
|
27887
|
-
function getMeasureContext() {
|
|
27888
|
-
if (measureContext !== null) return measureContext;
|
|
27889
|
-
if (typeof OffscreenCanvas !== 'undefined') {
|
|
27890
|
-
measureContext = new OffscreenCanvas(1, 1).getContext('2d');
|
|
27891
|
-
return measureContext;
|
|
27892
|
-
}
|
|
27893
|
-
if (typeof document !== 'undefined') {
|
|
27894
|
-
measureContext = document.createElement('canvas').getContext('2d');
|
|
27895
|
-
return measureContext;
|
|
27896
|
-
}
|
|
27897
|
-
throw new Error('Text measurement requires OffscreenCanvas or a DOM canvas context.');
|
|
27898
|
-
}
|
|
27899
|
-
function getSegmentMetricCache(font) {
|
|
27900
|
-
let cache = segmentMetricCaches.get(font);
|
|
27901
|
-
if (!cache) {
|
|
27902
|
-
cache = new Map();
|
|
27903
|
-
segmentMetricCaches.set(font, cache);
|
|
27904
|
-
}
|
|
27905
|
-
return cache;
|
|
27906
|
-
}
|
|
27907
|
-
function getSegmentMetrics(seg, cache) {
|
|
27908
|
-
let metrics = cache.get(seg);
|
|
27909
|
-
if (metrics === undefined) {
|
|
27910
|
-
const ctx = getMeasureContext();
|
|
27911
|
-
metrics = {
|
|
27912
|
-
width: ctx.measureText(seg).width,
|
|
27913
|
-
containsCJK: isCJK(seg)
|
|
27914
|
-
};
|
|
27915
|
-
cache.set(seg, metrics);
|
|
27916
|
-
}
|
|
27917
|
-
return metrics;
|
|
27918
|
-
}
|
|
27919
|
-
function getEngineProfile() {
|
|
27920
|
-
if (cachedEngineProfile !== null) return cachedEngineProfile;
|
|
27921
|
-
if (typeof navigator === 'undefined') {
|
|
27922
|
-
cachedEngineProfile = {
|
|
27923
|
-
lineFitEpsilon: 0.005,
|
|
27924
|
-
carryCJKAfterClosingQuote: false,
|
|
27925
|
-
preferPrefixWidthsForBreakableRuns: false,
|
|
27926
|
-
preferEarlySoftHyphenBreak: false
|
|
27927
|
-
};
|
|
27928
|
-
return cachedEngineProfile;
|
|
27929
|
-
}
|
|
27930
|
-
const ua = navigator.userAgent;
|
|
27931
|
-
const vendor = navigator.vendor;
|
|
27932
|
-
const isSafari = vendor === 'Apple Computer, Inc.' && ua.includes('Safari/') && !ua.includes('Chrome/') && !ua.includes('Chromium/') && !ua.includes('CriOS/') && !ua.includes('FxiOS/') && !ua.includes('EdgiOS/');
|
|
27933
|
-
const isChromium = ua.includes('Chrome/') || ua.includes('Chromium/') || ua.includes('CriOS/') || ua.includes('Edg/');
|
|
27934
|
-
cachedEngineProfile = {
|
|
27935
|
-
lineFitEpsilon: isSafari ? 1 / 64 : 0.005,
|
|
27936
|
-
carryCJKAfterClosingQuote: isChromium,
|
|
27937
|
-
preferPrefixWidthsForBreakableRuns: isSafari,
|
|
27938
|
-
preferEarlySoftHyphenBreak: isSafari
|
|
27939
|
-
};
|
|
27940
|
-
return cachedEngineProfile;
|
|
27941
|
-
}
|
|
27942
|
-
function parseFontSize(font) {
|
|
27943
|
-
const m = font.match(/(\d+(?:\.\d+)?)\s*px/);
|
|
27944
|
-
return m ? parseFloat(m[1]) : 16;
|
|
27945
|
-
}
|
|
27946
|
-
function getSharedGraphemeSegmenter$1() {
|
|
27947
|
-
if (sharedGraphemeSegmenter$1 === null) {
|
|
27948
|
-
sharedGraphemeSegmenter$1 = new Intl.Segmenter(undefined, {
|
|
27949
|
-
granularity: 'grapheme'
|
|
27950
|
-
});
|
|
27951
|
-
}
|
|
27952
|
-
return sharedGraphemeSegmenter$1;
|
|
27953
|
-
}
|
|
27954
|
-
function isEmojiGrapheme(g) {
|
|
27955
|
-
return emojiPresentationRe.test(g) || g.includes('\uFE0F');
|
|
27956
|
-
}
|
|
27957
|
-
function textMayContainEmoji(text) {
|
|
27958
|
-
return maybeEmojiRe.test(text);
|
|
27959
|
-
}
|
|
27960
|
-
function getEmojiCorrection(font, fontSize) {
|
|
27961
|
-
let correction = emojiCorrectionCache.get(font);
|
|
27962
|
-
if (correction !== undefined) return correction;
|
|
27963
|
-
const ctx = getMeasureContext();
|
|
27964
|
-
ctx.font = font;
|
|
27965
|
-
const canvasW = ctx.measureText('\u{1F600}').width;
|
|
27966
|
-
correction = 0;
|
|
27967
|
-
if (canvasW > fontSize + 0.5 && typeof document !== 'undefined' && document.body !== null) {
|
|
27968
|
-
const span = document.createElement('span');
|
|
27969
|
-
span.style.font = font;
|
|
27970
|
-
span.style.display = 'inline-block';
|
|
27971
|
-
span.style.visibility = 'hidden';
|
|
27972
|
-
span.style.position = 'absolute';
|
|
27973
|
-
span.textContent = '\u{1F600}';
|
|
27974
|
-
document.body.appendChild(span);
|
|
27975
|
-
const domW = span.getBoundingClientRect().width;
|
|
27976
|
-
document.body.removeChild(span);
|
|
27977
|
-
if (canvasW - domW > 0.5) {
|
|
27978
|
-
correction = canvasW - domW;
|
|
27979
|
-
}
|
|
27980
|
-
}
|
|
27981
|
-
emojiCorrectionCache.set(font, correction);
|
|
27982
|
-
return correction;
|
|
27983
|
-
}
|
|
27984
|
-
function countEmojiGraphemes(text) {
|
|
27985
|
-
let count = 0;
|
|
27986
|
-
const graphemeSegmenter = getSharedGraphemeSegmenter$1();
|
|
27987
|
-
for (const g of graphemeSegmenter.segment(text)){
|
|
27988
|
-
if (isEmojiGrapheme(g.segment)) count++;
|
|
27989
|
-
}
|
|
27990
|
-
return count;
|
|
27991
|
-
}
|
|
27992
|
-
function getEmojiCount(seg, metrics) {
|
|
27993
|
-
if (metrics.emojiCount === undefined) {
|
|
27994
|
-
metrics.emojiCount = countEmojiGraphemes(seg);
|
|
27995
|
-
}
|
|
27996
|
-
return metrics.emojiCount;
|
|
27997
|
-
}
|
|
27998
|
-
function getCorrectedSegmentWidth(seg, metrics, emojiCorrection) {
|
|
27999
|
-
if (emojiCorrection === 0) return metrics.width;
|
|
28000
|
-
return metrics.width - getEmojiCount(seg, metrics) * emojiCorrection;
|
|
28001
|
-
}
|
|
28002
|
-
function getSegmentGraphemeWidths(seg, metrics, cache, emojiCorrection) {
|
|
28003
|
-
if (metrics.graphemeWidths !== undefined) return metrics.graphemeWidths;
|
|
28004
|
-
const widths = [];
|
|
28005
|
-
const graphemeSegmenter = getSharedGraphemeSegmenter$1();
|
|
28006
|
-
for (const gs of graphemeSegmenter.segment(seg)){
|
|
28007
|
-
const graphemeMetrics = getSegmentMetrics(gs.segment, cache);
|
|
28008
|
-
widths.push(getCorrectedSegmentWidth(gs.segment, graphemeMetrics, emojiCorrection));
|
|
28009
|
-
}
|
|
28010
|
-
metrics.graphemeWidths = widths.length > 1 ? widths : null;
|
|
28011
|
-
return metrics.graphemeWidths;
|
|
28012
|
-
}
|
|
28013
|
-
function getSegmentGraphemePrefixWidths(seg, metrics, cache, emojiCorrection) {
|
|
28014
|
-
if (metrics.graphemePrefixWidths !== undefined) return metrics.graphemePrefixWidths;
|
|
28015
|
-
const prefixWidths = [];
|
|
28016
|
-
const graphemeSegmenter = getSharedGraphemeSegmenter$1();
|
|
28017
|
-
let prefix = '';
|
|
28018
|
-
for (const gs of graphemeSegmenter.segment(seg)){
|
|
28019
|
-
prefix += gs.segment;
|
|
28020
|
-
const prefixMetrics = getSegmentMetrics(prefix, cache);
|
|
28021
|
-
prefixWidths.push(getCorrectedSegmentWidth(prefix, prefixMetrics, emojiCorrection));
|
|
28022
|
-
}
|
|
28023
|
-
metrics.graphemePrefixWidths = prefixWidths.length > 1 ? prefixWidths : null;
|
|
28024
|
-
return metrics.graphemePrefixWidths;
|
|
28025
|
-
}
|
|
28026
|
-
function getFontMeasurementState(font, needsEmojiCorrection) {
|
|
28027
|
-
const ctx = getMeasureContext();
|
|
28028
|
-
ctx.font = font;
|
|
28029
|
-
const cache = getSegmentMetricCache(font);
|
|
28030
|
-
const fontSize = parseFontSize(font);
|
|
28031
|
-
const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection(font, fontSize) : 0;
|
|
28032
|
-
return {
|
|
28033
|
-
cache,
|
|
28034
|
-
fontSize,
|
|
28035
|
-
emojiCorrection
|
|
28036
|
-
};
|
|
28037
|
-
}
|
|
28038
|
-
|
|
28039
|
-
// Text measurement for browser environments using canvas measureText.
|
|
28040
|
-
//
|
|
28041
|
-
// Problem: DOM-based text measurement (getBoundingClientRect, offsetHeight)
|
|
28042
|
-
// forces synchronous layout reflow. When components independently measure text,
|
|
28043
|
-
// each measurement triggers a reflow of the entire document. This creates
|
|
28044
|
-
// read/write interleaving that can cost 30ms+ per frame for 500 text blocks.
|
|
28045
|
-
//
|
|
28046
|
-
// Solution: two-phase measurement centered around canvas measureText.
|
|
28047
|
-
// prepare(text, font) — segments text via Intl.Segmenter, measures each word
|
|
28048
|
-
// via canvas, caches widths, and does one cached DOM calibration read per
|
|
28049
|
-
// font when emoji correction is needed. Call once when text first appears.
|
|
28050
|
-
// layout(prepared, maxWidth, lineHeight) — walks cached word widths with pure
|
|
28051
|
-
// arithmetic to count lines and compute height. Call on every resize.
|
|
28052
|
-
// ~0.0002ms per text.
|
|
28053
|
-
//
|
|
28054
|
-
// i18n: Intl.Segmenter handles CJK (per-character breaking), Thai, Arabic, etc.
|
|
28055
|
-
// Bidi: simplified rich-path metadata for mixed LTR/RTL custom rendering.
|
|
28056
|
-
// Punctuation merging: "better." measured as one unit (matches CSS behavior).
|
|
28057
|
-
// Trailing whitespace: hangs past line edge without triggering breaks (CSS behavior).
|
|
28058
|
-
// overflow-wrap: pre-measured grapheme widths enable character-level word breaking.
|
|
28059
|
-
//
|
|
28060
|
-
// Emoji correction: Chrome/Firefox canvas measures emoji wider than DOM at font
|
|
28061
|
-
// sizes <24px on macOS (Apple Color Emoji). The inflation is constant per emoji
|
|
28062
|
-
// grapheme at a given size, font-independent. Auto-detected by comparing canvas
|
|
28063
|
-
// vs actual DOM emoji width (one cached DOM read per font). Safari canvas and
|
|
28064
|
-
// DOM agree (both wider than fontSize), so correction = 0 there.
|
|
28065
|
-
//
|
|
28066
|
-
// Limitations:
|
|
28067
|
-
// - system-ui font: canvas resolves to different optical variants than DOM on macOS.
|
|
28068
|
-
// Use named fonts (Helvetica, Inter, etc.) for guaranteed accuracy.
|
|
28069
|
-
// See RESEARCH.md "Discovery: system-ui font resolution mismatch".
|
|
28070
|
-
//
|
|
28071
|
-
// Based on Sebastian Markbage's text-layout research (github.com/chenglou/text-layout).
|
|
28072
|
-
let sharedGraphemeSegmenter = null;
|
|
28073
|
-
function getSharedGraphemeSegmenter() {
|
|
28074
|
-
if (sharedGraphemeSegmenter === null) {
|
|
28075
|
-
sharedGraphemeSegmenter = new Intl.Segmenter(undefined, {
|
|
28076
|
-
granularity: 'grapheme'
|
|
28077
|
-
});
|
|
28078
|
-
}
|
|
28079
|
-
return sharedGraphemeSegmenter;
|
|
28080
|
-
}
|
|
28081
|
-
// --- Public API ---
|
|
28082
|
-
function createEmptyPrepared(includeSegments) {
|
|
28083
|
-
{
|
|
28084
|
-
return {
|
|
28085
|
-
widths: [],
|
|
28086
|
-
lineEndFitAdvances: [],
|
|
28087
|
-
lineEndPaintAdvances: [],
|
|
28088
|
-
kinds: [],
|
|
28089
|
-
simpleLineWalkFastPath: true,
|
|
28090
|
-
segLevels: null,
|
|
28091
|
-
breakableWidths: [],
|
|
28092
|
-
breakablePrefixWidths: [],
|
|
28093
|
-
discretionaryHyphenWidth: 0,
|
|
28094
|
-
tabStopAdvance: 0,
|
|
28095
|
-
chunks: [],
|
|
28096
|
-
segments: []
|
|
28097
|
-
};
|
|
28098
|
-
}
|
|
28099
|
-
}
|
|
28100
|
-
function measureAnalysis(analysis, font, includeSegments) {
|
|
28101
|
-
const graphemeSegmenter = getSharedGraphemeSegmenter();
|
|
28102
|
-
const engineProfile = getEngineProfile();
|
|
28103
|
-
const { cache, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
|
|
28104
|
-
const discretionaryHyphenWidth = getCorrectedSegmentWidth('-', getSegmentMetrics('-', cache), emojiCorrection);
|
|
28105
|
-
const spaceWidth = getCorrectedSegmentWidth(' ', getSegmentMetrics(' ', cache), emojiCorrection);
|
|
28106
|
-
const tabStopAdvance = spaceWidth * 8;
|
|
28107
|
-
if (analysis.len === 0) return createEmptyPrepared();
|
|
28108
|
-
const widths = [];
|
|
28109
|
-
const lineEndFitAdvances = [];
|
|
28110
|
-
const lineEndPaintAdvances = [];
|
|
28111
|
-
const kinds = [];
|
|
28112
|
-
let simpleLineWalkFastPath = analysis.chunks.length <= 1;
|
|
28113
|
-
const segStarts = includeSegments ? [] : null;
|
|
28114
|
-
const breakableWidths = [];
|
|
28115
|
-
const breakablePrefixWidths = [];
|
|
28116
|
-
const segments = includeSegments ? [] : null;
|
|
28117
|
-
const preparedStartByAnalysisIndex = Array.from({
|
|
28118
|
-
length: analysis.len
|
|
28119
|
-
});
|
|
28120
|
-
const preparedEndByAnalysisIndex = Array.from({
|
|
28121
|
-
length: analysis.len
|
|
28122
|
-
});
|
|
28123
|
-
function pushMeasuredSegment(text, width, lineEndFitAdvance, lineEndPaintAdvance, kind, start, breakable, breakablePrefix) {
|
|
28124
|
-
if (kind !== 'text' && kind !== 'space' && kind !== 'zero-width-break') {
|
|
28125
|
-
simpleLineWalkFastPath = false;
|
|
28126
|
-
}
|
|
28127
|
-
widths.push(width);
|
|
28128
|
-
lineEndFitAdvances.push(lineEndFitAdvance);
|
|
28129
|
-
lineEndPaintAdvances.push(lineEndPaintAdvance);
|
|
28130
|
-
kinds.push(kind);
|
|
28131
|
-
segStarts?.push(start);
|
|
28132
|
-
breakableWidths.push(breakable);
|
|
28133
|
-
breakablePrefixWidths.push(breakablePrefix);
|
|
28134
|
-
if (segments !== null) segments.push(text);
|
|
28135
|
-
}
|
|
28136
|
-
for(let mi = 0; mi < analysis.len; mi++){
|
|
28137
|
-
preparedStartByAnalysisIndex[mi] = widths.length;
|
|
28138
|
-
const segText = analysis.texts[mi];
|
|
28139
|
-
const segWordLike = analysis.isWordLike[mi];
|
|
28140
|
-
const segKind = analysis.kinds[mi];
|
|
28141
|
-
const segStart = analysis.starts[mi];
|
|
28142
|
-
if (segKind === 'soft-hyphen') {
|
|
28143
|
-
pushMeasuredSegment(segText, 0, discretionaryHyphenWidth, discretionaryHyphenWidth, segKind, segStart, null, null);
|
|
28144
|
-
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
28145
|
-
continue;
|
|
28146
|
-
}
|
|
28147
|
-
if (segKind === 'hard-break') {
|
|
28148
|
-
pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
|
|
28149
|
-
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
28150
|
-
continue;
|
|
28151
|
-
}
|
|
28152
|
-
if (segKind === 'tab') {
|
|
28153
|
-
pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null, null);
|
|
28154
|
-
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
28155
|
-
continue;
|
|
28156
|
-
}
|
|
28157
|
-
const segMetrics = getSegmentMetrics(segText, cache);
|
|
28158
|
-
if (segKind === 'text' && segMetrics.containsCJK) {
|
|
28159
|
-
let unitText = '';
|
|
28160
|
-
let unitStart = 0;
|
|
28161
|
-
for (const gs of graphemeSegmenter.segment(segText)){
|
|
28162
|
-
const grapheme = gs.segment;
|
|
28163
|
-
if (unitText.length === 0) {
|
|
28164
|
-
unitText = grapheme;
|
|
28165
|
-
unitStart = gs.index;
|
|
28166
|
-
continue;
|
|
28167
|
-
}
|
|
28168
|
-
if (kinsokuEnd.has(unitText) || kinsokuStart.has(grapheme) || leftStickyPunctuation.has(grapheme) || engineProfile.carryCJKAfterClosingQuote && isCJK(grapheme) && endsWithClosingQuote(unitText)) {
|
|
28169
|
-
unitText += grapheme;
|
|
28170
|
-
continue;
|
|
28171
|
-
}
|
|
28172
|
-
const unitMetrics = getSegmentMetrics(unitText, cache);
|
|
28173
|
-
const w = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
|
|
28174
|
-
pushMeasuredSegment(unitText, w, w, w, 'text', segStart + unitStart, null, null);
|
|
28175
|
-
unitText = grapheme;
|
|
28176
|
-
unitStart = gs.index;
|
|
28177
|
-
}
|
|
28178
|
-
if (unitText.length > 0) {
|
|
28179
|
-
const unitMetrics = getSegmentMetrics(unitText, cache);
|
|
28180
|
-
const w = getCorrectedSegmentWidth(unitText, unitMetrics, emojiCorrection);
|
|
28181
|
-
pushMeasuredSegment(unitText, w, w, w, 'text', segStart + unitStart, null, null);
|
|
28182
|
-
}
|
|
28183
|
-
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
28184
|
-
continue;
|
|
28185
|
-
}
|
|
28186
|
-
const w = getCorrectedSegmentWidth(segText, segMetrics, emojiCorrection);
|
|
28187
|
-
const lineEndFitAdvance = segKind === 'space' || segKind === 'preserved-space' || segKind === 'zero-width-break' ? 0 : w;
|
|
28188
|
-
const lineEndPaintAdvance = segKind === 'space' || segKind === 'zero-width-break' ? 0 : w;
|
|
28189
|
-
if (segWordLike && segText.length > 1) {
|
|
28190
|
-
const graphemeWidths = getSegmentGraphemeWidths(segText, segMetrics, cache, emojiCorrection);
|
|
28191
|
-
const graphemePrefixWidths = engineProfile.preferPrefixWidthsForBreakableRuns ? getSegmentGraphemePrefixWidths(segText, segMetrics, cache, emojiCorrection) : null;
|
|
28192
|
-
pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, graphemeWidths, graphemePrefixWidths);
|
|
28193
|
-
} else {
|
|
28194
|
-
pushMeasuredSegment(segText, w, lineEndFitAdvance, lineEndPaintAdvance, segKind, segStart, null, null);
|
|
28195
|
-
}
|
|
28196
|
-
preparedEndByAnalysisIndex[mi] = widths.length;
|
|
28197
|
-
}
|
|
28198
|
-
const chunks = mapAnalysisChunksToPreparedChunks(analysis.chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex);
|
|
28199
|
-
const segLevels = segStarts === null ? null : computeSegmentLevels(analysis.normalized, segStarts);
|
|
28200
|
-
if (segments !== null) {
|
|
28201
|
-
return {
|
|
28202
|
-
widths,
|
|
28203
|
-
lineEndFitAdvances,
|
|
28204
|
-
lineEndPaintAdvances,
|
|
28205
|
-
kinds,
|
|
28206
|
-
simpleLineWalkFastPath,
|
|
28207
|
-
segLevels,
|
|
28208
|
-
breakableWidths,
|
|
28209
|
-
breakablePrefixWidths,
|
|
28210
|
-
discretionaryHyphenWidth,
|
|
28211
|
-
tabStopAdvance,
|
|
28212
|
-
chunks,
|
|
28213
|
-
segments
|
|
28214
|
-
};
|
|
28215
|
-
}
|
|
28216
|
-
return {
|
|
28217
|
-
widths,
|
|
28218
|
-
lineEndFitAdvances,
|
|
28219
|
-
lineEndPaintAdvances,
|
|
28220
|
-
kinds,
|
|
28221
|
-
simpleLineWalkFastPath,
|
|
28222
|
-
segLevels,
|
|
28223
|
-
breakableWidths,
|
|
28224
|
-
breakablePrefixWidths,
|
|
28225
|
-
discretionaryHyphenWidth,
|
|
28226
|
-
tabStopAdvance,
|
|
28227
|
-
chunks
|
|
28228
|
-
};
|
|
28229
|
-
}
|
|
28230
|
-
function mapAnalysisChunksToPreparedChunks(chunks, preparedStartByAnalysisIndex, preparedEndByAnalysisIndex) {
|
|
28231
|
-
const preparedChunks = [];
|
|
28232
|
-
for(let i = 0; i < chunks.length; i++){
|
|
28233
|
-
const chunk = chunks[i];
|
|
28234
|
-
const startSegmentIndex = chunk.startSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.startSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
|
|
28235
|
-
const endSegmentIndex = chunk.endSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.endSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
|
|
28236
|
-
const consumedEndSegmentIndex = chunk.consumedEndSegmentIndex < preparedStartByAnalysisIndex.length ? preparedStartByAnalysisIndex[chunk.consumedEndSegmentIndex] : preparedEndByAnalysisIndex[preparedEndByAnalysisIndex.length - 1] ?? 0;
|
|
28237
|
-
preparedChunks.push({
|
|
28238
|
-
startSegmentIndex,
|
|
28239
|
-
endSegmentIndex,
|
|
28240
|
-
consumedEndSegmentIndex
|
|
28241
|
-
});
|
|
28242
|
-
}
|
|
28243
|
-
return preparedChunks;
|
|
28244
|
-
}
|
|
28245
|
-
function prepareInternal(text, font, includeSegments, options) {
|
|
28246
|
-
const analysis = analyzeText(text, getEngineProfile(), options?.whiteSpace);
|
|
28247
|
-
return measureAnalysis(analysis, font, includeSegments);
|
|
28248
|
-
}
|
|
28249
|
-
// Rich variant used by callers that need enough information to render the
|
|
28250
|
-
// laid-out lines themselves.
|
|
28251
|
-
function prepareWithSegments(text, font, options) {
|
|
28252
|
-
return prepareInternal(text, font, true, options);
|
|
28253
|
-
}
|
|
28254
|
-
|
|
28255
26526
|
var hyphenated_cjs = {};
|
|
28256
26527
|
|
|
28257
26528
|
var hyphenatedEnUs_cjs;
|
|
@@ -33556,19 +31827,19 @@
|
|
|
33556
31827
|
var prepared = prepareWithSegments(sentence, "10px sans-serif", {
|
|
33557
31828
|
whiteSpace: "pre-wrap"
|
|
33558
31829
|
});
|
|
33559
|
-
var segments = prepared.segments, kinds = prepared.kinds,
|
|
31830
|
+
var segments = prepared.segments, kinds = prepared.kinds, breakableFitAdvances = prepared.breakableFitAdvances;
|
|
33560
31831
|
var words = [];
|
|
33561
31832
|
var prevKind = null;
|
|
33562
31833
|
var wordHasBreakable = false;
|
|
33563
31834
|
for(var i = 0; i < segments.length; i++){
|
|
33564
31835
|
if ((kinds[i] === "space" || kinds[i] === "preserved-space") && words.length > 0) {
|
|
33565
31836
|
words[words.length - 1] += segments[i];
|
|
33566
|
-
} else if (prevKind === "text" && kinds[i] === "text" && words.length > 0 && !isBreakable(segments[i]) && !isBreakable(segments[i - 1]) && !(
|
|
31837
|
+
} else if (prevKind === "text" && kinds[i] === "text" && words.length > 0 && !isBreakable(segments[i]) && !isBreakable(segments[i - 1]) && !(breakableFitAdvances[i] !== null && wordHasBreakable)) {
|
|
33567
31838
|
words[words.length - 1] += segments[i];
|
|
33568
|
-
if (
|
|
31839
|
+
if (breakableFitAdvances[i] !== null) wordHasBreakable = true;
|
|
33569
31840
|
} else {
|
|
33570
31841
|
words.push(segments[i]);
|
|
33571
|
-
wordHasBreakable =
|
|
31842
|
+
wordHasBreakable = breakableFitAdvances[i] !== null;
|
|
33572
31843
|
}
|
|
33573
31844
|
prevKind = kinds[i];
|
|
33574
31845
|
}
|