three-text 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +49 -35
- package/dist/index.js +49 -35
- package/dist/index.min.cjs +583 -583
- package/dist/index.min.js +595 -595
- package/dist/index.umd.js +49 -35
- package/dist/index.umd.min.js +448 -448
- package/dist/types/core/layout/LineBreak.d.ts +0 -1
- package/dist/types/core/layout/LineBreakDelta.d.ts +97 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.4.
|
|
2
|
+
* three-text v0.4.2
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -218,6 +218,9 @@ class ActiveNodeList {
|
|
|
218
218
|
existing.previous = node.previous;
|
|
219
219
|
existing.hyphenated = node.hyphenated;
|
|
220
220
|
existing.line = node.line;
|
|
221
|
+
existing.cumWidth = node.cumWidth;
|
|
222
|
+
existing.cumStretch = node.cumStretch;
|
|
223
|
+
existing.cumShrink = node.cumShrink;
|
|
221
224
|
return true;
|
|
222
225
|
}
|
|
223
226
|
return false;
|
|
@@ -294,27 +297,6 @@ class LineBreak {
|
|
|
294
297
|
return FitnessClass.LOOSE; // stretching 0.5-1.0
|
|
295
298
|
return FitnessClass.VERY_LOOSE; // stretching > 1.0
|
|
296
299
|
}
|
|
297
|
-
// Build prefix sums so we can quickly compute the width/stretch/shrink
|
|
298
|
-
// of any range [a, b] as cumulative[b] - cumulative[a]
|
|
299
|
-
static computeCumulativeWidths(items) {
|
|
300
|
-
const n = items.length + 1;
|
|
301
|
-
const width = new Float64Array(n);
|
|
302
|
-
const stretch = new Float64Array(n);
|
|
303
|
-
const shrink = new Float64Array(n);
|
|
304
|
-
for (let i = 0; i < items.length; i++) {
|
|
305
|
-
const item = items[i];
|
|
306
|
-
width[i + 1] = width[i] + item.width;
|
|
307
|
-
if (item.type === ItemType.GLUE) {
|
|
308
|
-
stretch[i + 1] = stretch[i] + item.stretch;
|
|
309
|
-
shrink[i + 1] = shrink[i] + item.shrink;
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
stretch[i + 1] = stretch[i];
|
|
313
|
-
shrink[i + 1] = shrink[i];
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return { width, stretch, shrink };
|
|
317
|
-
}
|
|
318
300
|
static findHyphenationPoints(word, language = 'en-us', availablePatterns, lefthyphenmin = DEFAULT_LEFT_HYPHEN_MIN, righthyphenmin = DEFAULT_RIGHT_HYPHEN_MIN) {
|
|
319
301
|
let patternTrie;
|
|
320
302
|
if (availablePatterns && availablePatterns[language]) {
|
|
@@ -734,9 +716,8 @@ class LineBreak {
|
|
|
734
716
|
// TeX: line_break inner loop (tex.web lines 16169-17256)
|
|
735
717
|
// Finds optimal breakpoints using Knuth-Plass algorithm
|
|
736
718
|
static lineBreak(items, lineWidth, threshold, emergencyStretch, context) {
|
|
737
|
-
const cumulative = this.computeCumulativeWidths(items);
|
|
738
719
|
const activeNodes = new ActiveNodeList();
|
|
739
|
-
// Start node
|
|
720
|
+
// Start node with zero cumulative width
|
|
740
721
|
activeNodes.insert({
|
|
741
722
|
position: 0,
|
|
742
723
|
line: 0,
|
|
@@ -745,8 +726,15 @@ class LineBreak {
|
|
|
745
726
|
previous: null,
|
|
746
727
|
hyphenated: false,
|
|
747
728
|
active: true,
|
|
748
|
-
activeIndex: 0
|
|
729
|
+
activeIndex: 0,
|
|
730
|
+
cumWidth: 0,
|
|
731
|
+
cumStretch: 0,
|
|
732
|
+
cumShrink: 0
|
|
749
733
|
});
|
|
734
|
+
// Cumulative width from paragraph start, representing items[0..i-1]
|
|
735
|
+
let cumWidth = 0;
|
|
736
|
+
let cumStretch = 0;
|
|
737
|
+
let cumShrink = 0;
|
|
750
738
|
// Process each item
|
|
751
739
|
for (let i = 0; i < items.length; i++) {
|
|
752
740
|
const item = items[i];
|
|
@@ -757,8 +745,22 @@ class LineBreak {
|
|
|
757
745
|
(item.type === ItemType.GLUE &&
|
|
758
746
|
i > 0 &&
|
|
759
747
|
items[i - 1].type === ItemType.BOX);
|
|
760
|
-
if (!isBreakpoint)
|
|
748
|
+
if (!isBreakpoint) {
|
|
749
|
+
// Accumulate width for non-breakpoint items
|
|
750
|
+
if (item.type === ItemType.BOX) {
|
|
751
|
+
cumWidth += item.width;
|
|
752
|
+
}
|
|
753
|
+
else if (item.type === ItemType.GLUE) {
|
|
754
|
+
const glue = item;
|
|
755
|
+
cumWidth += glue.width;
|
|
756
|
+
cumStretch += glue.stretch;
|
|
757
|
+
cumShrink += glue.shrink;
|
|
758
|
+
}
|
|
759
|
+
else if (item.type === ItemType.DISCRETIONARY) {
|
|
760
|
+
cumWidth += item.width;
|
|
761
|
+
}
|
|
761
762
|
continue;
|
|
763
|
+
}
|
|
762
764
|
// Get penalty and flagged status
|
|
763
765
|
let pi = 0;
|
|
764
766
|
let flagged = false;
|
|
@@ -780,18 +782,14 @@ class LineBreak {
|
|
|
780
782
|
const bestDemerits = [Infinity, Infinity, Infinity, Infinity];
|
|
781
783
|
// Nodes to deactivate
|
|
782
784
|
const toDeactivate = [];
|
|
783
|
-
// Current cumulative values at position i
|
|
784
|
-
const curWidth = cumulative.width[i];
|
|
785
|
-
const curStretch = cumulative.stretch[i];
|
|
786
|
-
const curShrink = cumulative.shrink[i];
|
|
787
785
|
// Try each active node as predecessor
|
|
788
786
|
const active = activeNodes.getActive();
|
|
789
787
|
for (let j = 0; j < active.length; j++) {
|
|
790
788
|
const a = active[j];
|
|
791
|
-
// Line width from a to i
|
|
792
|
-
const lineW =
|
|
793
|
-
const lineStretch =
|
|
794
|
-
const lineShrink =
|
|
789
|
+
// Line width from a to i
|
|
790
|
+
const lineW = cumWidth - a.cumWidth + breakWidth;
|
|
791
|
+
const lineStretch = cumStretch - a.cumStretch;
|
|
792
|
+
const lineShrink = cumShrink - a.cumShrink;
|
|
795
793
|
const shortfall = lineWidth - lineW;
|
|
796
794
|
let ratio;
|
|
797
795
|
if (shortfall > 0) {
|
|
@@ -847,7 +845,10 @@ class LineBreak {
|
|
|
847
845
|
previous: a,
|
|
848
846
|
hyphenated: flagged,
|
|
849
847
|
active: true,
|
|
850
|
-
activeIndex: -1
|
|
848
|
+
activeIndex: -1,
|
|
849
|
+
cumWidth: cumWidth,
|
|
850
|
+
cumStretch: cumStretch,
|
|
851
|
+
cumShrink: cumShrink
|
|
851
852
|
};
|
|
852
853
|
}
|
|
853
854
|
}
|
|
@@ -864,6 +865,19 @@ class LineBreak {
|
|
|
864
865
|
if (activeNodes.size() === 0 && pi !== EJECT_PENALTY) {
|
|
865
866
|
return null;
|
|
866
867
|
}
|
|
868
|
+
// Accumulate width after evaluating this breakpoint
|
|
869
|
+
if (item.type === ItemType.BOX) {
|
|
870
|
+
cumWidth += item.width;
|
|
871
|
+
}
|
|
872
|
+
else if (item.type === ItemType.GLUE) {
|
|
873
|
+
const glue = item;
|
|
874
|
+
cumWidth += glue.width;
|
|
875
|
+
cumStretch += glue.stretch;
|
|
876
|
+
cumShrink += glue.shrink;
|
|
877
|
+
}
|
|
878
|
+
else if (item.type === ItemType.DISCRETIONARY) {
|
|
879
|
+
cumWidth += item.width;
|
|
880
|
+
}
|
|
867
881
|
}
|
|
868
882
|
// Find best solution
|
|
869
883
|
let best = null;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* three-text v0.4.
|
|
2
|
+
* three-text v0.4.2
|
|
3
3
|
* Copyright (C) 2025 Countertype LLC
|
|
4
4
|
*
|
|
5
5
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -215,6 +215,9 @@ class ActiveNodeList {
|
|
|
215
215
|
existing.previous = node.previous;
|
|
216
216
|
existing.hyphenated = node.hyphenated;
|
|
217
217
|
existing.line = node.line;
|
|
218
|
+
existing.cumWidth = node.cumWidth;
|
|
219
|
+
existing.cumStretch = node.cumStretch;
|
|
220
|
+
existing.cumShrink = node.cumShrink;
|
|
218
221
|
return true;
|
|
219
222
|
}
|
|
220
223
|
return false;
|
|
@@ -291,27 +294,6 @@ class LineBreak {
|
|
|
291
294
|
return FitnessClass.LOOSE; // stretching 0.5-1.0
|
|
292
295
|
return FitnessClass.VERY_LOOSE; // stretching > 1.0
|
|
293
296
|
}
|
|
294
|
-
// Build prefix sums so we can quickly compute the width/stretch/shrink
|
|
295
|
-
// of any range [a, b] as cumulative[b] - cumulative[a]
|
|
296
|
-
static computeCumulativeWidths(items) {
|
|
297
|
-
const n = items.length + 1;
|
|
298
|
-
const width = new Float64Array(n);
|
|
299
|
-
const stretch = new Float64Array(n);
|
|
300
|
-
const shrink = new Float64Array(n);
|
|
301
|
-
for (let i = 0; i < items.length; i++) {
|
|
302
|
-
const item = items[i];
|
|
303
|
-
width[i + 1] = width[i] + item.width;
|
|
304
|
-
if (item.type === ItemType.GLUE) {
|
|
305
|
-
stretch[i + 1] = stretch[i] + item.stretch;
|
|
306
|
-
shrink[i + 1] = shrink[i] + item.shrink;
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
stretch[i + 1] = stretch[i];
|
|
310
|
-
shrink[i + 1] = shrink[i];
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return { width, stretch, shrink };
|
|
314
|
-
}
|
|
315
297
|
static findHyphenationPoints(word, language = 'en-us', availablePatterns, lefthyphenmin = DEFAULT_LEFT_HYPHEN_MIN, righthyphenmin = DEFAULT_RIGHT_HYPHEN_MIN) {
|
|
316
298
|
let patternTrie;
|
|
317
299
|
if (availablePatterns && availablePatterns[language]) {
|
|
@@ -731,9 +713,8 @@ class LineBreak {
|
|
|
731
713
|
// TeX: line_break inner loop (tex.web lines 16169-17256)
|
|
732
714
|
// Finds optimal breakpoints using Knuth-Plass algorithm
|
|
733
715
|
static lineBreak(items, lineWidth, threshold, emergencyStretch, context) {
|
|
734
|
-
const cumulative = this.computeCumulativeWidths(items);
|
|
735
716
|
const activeNodes = new ActiveNodeList();
|
|
736
|
-
// Start node
|
|
717
|
+
// Start node with zero cumulative width
|
|
737
718
|
activeNodes.insert({
|
|
738
719
|
position: 0,
|
|
739
720
|
line: 0,
|
|
@@ -742,8 +723,15 @@ class LineBreak {
|
|
|
742
723
|
previous: null,
|
|
743
724
|
hyphenated: false,
|
|
744
725
|
active: true,
|
|
745
|
-
activeIndex: 0
|
|
726
|
+
activeIndex: 0,
|
|
727
|
+
cumWidth: 0,
|
|
728
|
+
cumStretch: 0,
|
|
729
|
+
cumShrink: 0
|
|
746
730
|
});
|
|
731
|
+
// Cumulative width from paragraph start, representing items[0..i-1]
|
|
732
|
+
let cumWidth = 0;
|
|
733
|
+
let cumStretch = 0;
|
|
734
|
+
let cumShrink = 0;
|
|
747
735
|
// Process each item
|
|
748
736
|
for (let i = 0; i < items.length; i++) {
|
|
749
737
|
const item = items[i];
|
|
@@ -754,8 +742,22 @@ class LineBreak {
|
|
|
754
742
|
(item.type === ItemType.GLUE &&
|
|
755
743
|
i > 0 &&
|
|
756
744
|
items[i - 1].type === ItemType.BOX);
|
|
757
|
-
if (!isBreakpoint)
|
|
745
|
+
if (!isBreakpoint) {
|
|
746
|
+
// Accumulate width for non-breakpoint items
|
|
747
|
+
if (item.type === ItemType.BOX) {
|
|
748
|
+
cumWidth += item.width;
|
|
749
|
+
}
|
|
750
|
+
else if (item.type === ItemType.GLUE) {
|
|
751
|
+
const glue = item;
|
|
752
|
+
cumWidth += glue.width;
|
|
753
|
+
cumStretch += glue.stretch;
|
|
754
|
+
cumShrink += glue.shrink;
|
|
755
|
+
}
|
|
756
|
+
else if (item.type === ItemType.DISCRETIONARY) {
|
|
757
|
+
cumWidth += item.width;
|
|
758
|
+
}
|
|
758
759
|
continue;
|
|
760
|
+
}
|
|
759
761
|
// Get penalty and flagged status
|
|
760
762
|
let pi = 0;
|
|
761
763
|
let flagged = false;
|
|
@@ -777,18 +779,14 @@ class LineBreak {
|
|
|
777
779
|
const bestDemerits = [Infinity, Infinity, Infinity, Infinity];
|
|
778
780
|
// Nodes to deactivate
|
|
779
781
|
const toDeactivate = [];
|
|
780
|
-
// Current cumulative values at position i
|
|
781
|
-
const curWidth = cumulative.width[i];
|
|
782
|
-
const curStretch = cumulative.stretch[i];
|
|
783
|
-
const curShrink = cumulative.shrink[i];
|
|
784
782
|
// Try each active node as predecessor
|
|
785
783
|
const active = activeNodes.getActive();
|
|
786
784
|
for (let j = 0; j < active.length; j++) {
|
|
787
785
|
const a = active[j];
|
|
788
|
-
// Line width from a to i
|
|
789
|
-
const lineW =
|
|
790
|
-
const lineStretch =
|
|
791
|
-
const lineShrink =
|
|
786
|
+
// Line width from a to i
|
|
787
|
+
const lineW = cumWidth - a.cumWidth + breakWidth;
|
|
788
|
+
const lineStretch = cumStretch - a.cumStretch;
|
|
789
|
+
const lineShrink = cumShrink - a.cumShrink;
|
|
792
790
|
const shortfall = lineWidth - lineW;
|
|
793
791
|
let ratio;
|
|
794
792
|
if (shortfall > 0) {
|
|
@@ -844,7 +842,10 @@ class LineBreak {
|
|
|
844
842
|
previous: a,
|
|
845
843
|
hyphenated: flagged,
|
|
846
844
|
active: true,
|
|
847
|
-
activeIndex: -1
|
|
845
|
+
activeIndex: -1,
|
|
846
|
+
cumWidth: cumWidth,
|
|
847
|
+
cumStretch: cumStretch,
|
|
848
|
+
cumShrink: cumShrink
|
|
848
849
|
};
|
|
849
850
|
}
|
|
850
851
|
}
|
|
@@ -861,6 +862,19 @@ class LineBreak {
|
|
|
861
862
|
if (activeNodes.size() === 0 && pi !== EJECT_PENALTY) {
|
|
862
863
|
return null;
|
|
863
864
|
}
|
|
865
|
+
// Accumulate width after evaluating this breakpoint
|
|
866
|
+
if (item.type === ItemType.BOX) {
|
|
867
|
+
cumWidth += item.width;
|
|
868
|
+
}
|
|
869
|
+
else if (item.type === ItemType.GLUE) {
|
|
870
|
+
const glue = item;
|
|
871
|
+
cumWidth += glue.width;
|
|
872
|
+
cumStretch += glue.stretch;
|
|
873
|
+
cumShrink += glue.shrink;
|
|
874
|
+
}
|
|
875
|
+
else if (item.type === ItemType.DISCRETIONARY) {
|
|
876
|
+
cumWidth += item.width;
|
|
877
|
+
}
|
|
864
878
|
}
|
|
865
879
|
// Find best solution
|
|
866
880
|
let best = null;
|