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 CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * three-text v0.4.1
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 using cumulative arrays
792
- const lineW = curWidth - cumulative.width[a.position] + breakWidth;
793
- const lineStretch = curStretch - cumulative.stretch[a.position];
794
- const lineShrink = curShrink - cumulative.shrink[a.position];
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.1
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 using cumulative arrays
789
- const lineW = curWidth - cumulative.width[a.position] + breakWidth;
790
- const lineStretch = curStretch - cumulative.stretch[a.position];
791
- const lineShrink = curShrink - cumulative.shrink[a.position];
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;