knuth-plass-linebreak 0.9.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.9.0] - 2026-03-28
9
+
10
+ ### Changed
11
+
12
+ - Rename package from `tex-linebreak` to `knuth-plass-linebreak`
13
+ - Migrate build and test tooling from webpack/karma to vite/vitest
14
+
15
+ ### Fixed
16
+
17
+ - Exclude penalty width from `widthToNextBox` scan
18
+ - Return adjustment ratio 0 for exact-fit lines with rigid glue
19
+ - Require terminal forced break in `breakLines`
20
+ - Preserve fitness class nodes during line breaking
21
+
22
+ ## [0.8.1] - 2025-08-04
23
+
24
+ - Limit an optimization to only apply when no items have negative widths,
25
+ shrink or stretch -
26
+ <https://github.com/robertknight/tex-linebreak/pull/105>
27
+
28
+ ## [0.8.0] - 2025-08-02
29
+
30
+ - Support negative widths and stretchability/shrinkability (thanks @basil) -
31
+ <https://github.com/robertknight/tex-linebreak/pull/103>
32
+
33
+ ## [0.7.0] - 2024-01-15
34
+
35
+ - Add built-in type declarations (thanks @w8r) -
36
+ <https://github.com/robertknight/tex-linebreak/pull/74>
37
+
38
+ ## [0.6.0] - 2021-12-28
39
+
40
+ The package's code has been converted to ES 2017+. As such it no longer supports
41
+ IE 11 or other pre-2017 browsers.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # knuth-plass-linebreak
2
+
3
+ ![npm version](https://img.shields.io/npm/v/knuth-plass-linebreak.svg)
4
+
5
+ _knuth-plass-linebreak_ is a JavaScript library for laying out justified
6
+ text as you would find in a newspaper, book or technical paper. It
7
+ implements the Knuth-Plass line-breaking algorithm, as used by TeX.
8
+
9
+ ## Introduction
10
+
11
+ Most text on the web is presented with "ragged-right" margins, as opposed to
12
+ the justified text you would find in eg. a scientific paper or newspaper.
13
+ Text can be justified in web pages using `text-align: justify`.
14
+ However this option alone tends to result in large&nbsp;&nbsp;&nbsp;spaces
15
+ &nbsp;&nbsp;&nbsp;between words which is distracting to read. This is due to the
16
+ use of "first fit" line-breaking algorithms where the browser considers only the
17
+ current line when finding the next breakpoint. Some browsers support hyphenation
18
+ via `hyphens: auto` which reduces this effect. However the first-fit approach
19
+ can still produce wide lines and it can also produce more hyphenated lines than
20
+ necessary.
21
+
22
+ The Knuth-Plass algorithm on the other hand optimizes the spacing between words
23
+ over the whole paragraph, seeking to minimize the overall "badness" of the
24
+ layout. This factor depends on the amount by which spaces have been shrunk or
25
+ stretched and the number of hyphenated lines. The benefits of this approach are
26
+ greater when rendering narrower columns of text (eg. on small screens).
27
+
28
+ This table compares the same text rendered in the same environment (font, font
29
+ size, device width, margins) using CSS justification, CSS justification +
30
+ hyphenation and this library:
31
+
32
+ <table>
33
+ <tr>
34
+ <td>Safari: text-align: justify</td>
35
+ <td>Chrome: text-align: justify; hyphens: auto</td>
36
+ <td>_knuth-plass-linebreak_</td>
37
+ </tr>
38
+ <tr>
39
+ <td><img width="200"
40
+ src="images/bigint-safari-justify.png"
41
+ alt="Safari: text-align: justify"></td>
42
+ <td><img width="200"
43
+ src="images/bigint-chrome-justify-hyphens.png"
44
+ alt="Chrome: text-align: justify; hyphens: auto"></td>
45
+ <td><img width="200"
46
+ src="images/bigint-tex-linebreak.png"
47
+ alt="knuth-plass-linebreak"></td>
48
+ </tr>
49
+ <tr>
50
+ <td>CSS justification produces large spaces on the second and penultimate
51
+ lines.</td>
52
+ <td>Enabling hyphenation using `hyphens: auto` in browsers that support it
53
+ (as of 2018-04-07 this appears to be only Chrome) produces better
54
+ output but still produces wide lines.</td>
55
+ <td>The TeX algorithm in contrast hyphenates fewer lines and avoids
56
+ excessive spacing between words.</td>
57
+ </tr>
58
+ </table>
59
+
60
+ _knuth-plass-linebreak_ has no dependencies on a particular JS environment (browser,
61
+ Node) or render target (`<canvas>`, HTML elements, PDF).
62
+
63
+ ## Try it out
64
+
65
+ The easiest way to see what the library can do is to [install the bookmarklet](
66
+ src/demos/bookmarklet.js) and activate it on an existing web page, such as this
67
+ [Medium article](https://medium.com/@parismarx/ubers-unrealistic-plan-for-flying-cars-6c9569d6fa8b).
68
+
69
+ It will justify and apply hyphenation to the content of any paragraph (`<p>`)
70
+ elements on the page. The difference is more beneficial on smaller screens,
71
+ so try in your browser's responsive design mode.
72
+
73
+ Note that the bookmarklet does not work on sites that use
74
+ [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
75
+ to restrict where scripts can be loaded from.
76
+
77
+ ## Usage
78
+
79
+ First, add the _knuth-plass-linebreak_ package to your dependencies:
80
+
81
+ ```sh
82
+ npm install knuth-plass-linebreak
83
+ ```
84
+
85
+ The library has low-level APIs which implement the core line-breaking and
86
+ positioning algorithm, as well as higher-level APIs that provide a convenient
87
+ way to justify existing HTML content.
88
+
89
+ ### Low-level APIs
90
+
91
+ The low-level APIs `breakLines` and `positionItems` work with generic "box"
92
+ (typeset material), "glue" (spaces with flexible sizing) and "penalty" items.
93
+ Typically "boxes" are words, "glue" items are spaces and "penalty" items
94
+ represent hyphenation points or the end of a paragraph. However you can use them
95
+ to lay out arbitrary content.
96
+
97
+ ```js
98
+ import { layoutItemsFromString, breakLines, positionItems } from 'knuth-plass-linebreak';
99
+
100
+ // Convert your text to a set of "box", "glue" and "penalty" items used by the
101
+ // line-breaking process.
102
+ //
103
+ // "Box" items are things (typically words) to typeset.
104
+ // "Glue" items are spaces that can stretch or shrink or be a breakpoint.
105
+ // "Penalty" items are possible breakpoints (hyphens, end of a paragraph etc.).
106
+ //
107
+ // `layoutItemsFromString` is a helper that takes a string and a function to
108
+ // measure the width of a piece of that string and returns a suitable set of
109
+ // items.
110
+ const measureText = text => text.length * 5;
111
+ const items = layoutItemsFromString(yourText, measureText);
112
+
113
+ // Find where to insert line-breaks in order to optimally lay out the text.
114
+ const lineWidth = 200;
115
+ const breakpoints = breakLines(items, lineWidth)
116
+
117
+ // Compute the (xOffset, line number) at which to draw each box item.
118
+ const positionedItems = positionItems(items, lineWidth, breakpoints);
119
+
120
+ positionedItems.forEach(pi => {
121
+ const item = items[pi.item];
122
+
123
+ // Add code to draw `item.text` at `(box.xOffset, box.line)` to whatever output
124
+ // you want, eg. `<canvas>`, HTML elements with spacing created using CSS,
125
+ // WebGL, ...
126
+ });
127
+ ```
128
+
129
+ ### High-level APIs
130
+
131
+ The high-level APIs provide convenience methods for justifying content in
132
+ existing HTML elements and laying out justified lines for rendering to HTML,
133
+ canvas or other outputs. This includes support for hyphenation using the
134
+ [hypher](https://github.com/bramstein/hypher) library.
135
+
136
+ #### Justifying existing HTML content
137
+
138
+ The contents of an existing HTML element can be justified using the
139
+ `justifyContent` function.
140
+
141
+ ```js
142
+ import enUsPatterns from 'hyphenation.en-us';
143
+ import { createHyphenator, justifyContent } from 'knuth-plass-linebreak';
144
+
145
+ const hyphenate = createHyphenator(enUsPatterns);
146
+ const paragraphs = Array.from(document.querySelectorAll('p'));
147
+ justifyContent(paragraphs, hyphenate);
148
+ ```
149
+
150
+ After an element is justified, its layout will remain fixed until `justifyContent`
151
+ is called again. In order to re-justify content in response to window size
152
+ changes or other events, your code will need to listen for the appropriate
153
+ events and re-invoke `justifyContent`.
154
+
155
+ #### Rendering text
156
+
157
+ For rendering justified text into a variety of targets (HTML, canvas, SVG,
158
+ WebGL etc.), the `layoutText` helper can be used to lay out justifed text and
159
+ obtain the positions which each word should be drawn at.
160
+
161
+ ```js
162
+ import { createHyphenator, layoutText } from 'knuth-plass-linebreak';
163
+
164
+ import enUsPatterns from 'hyphenation.en-us';
165
+
166
+ const hyphenate = createHyphenator(enUsPatterns);
167
+ const measure = word => word.length * 5;
168
+
169
+ const { items, positions } = layoutText(text, lineWidth, measure, hyphenate);
170
+
171
+ positions.forEach(pos => {
172
+ // Draw text as in the above example for the low-level APIs
173
+ });
174
+ ```
175
+
176
+ ## API reference
177
+
178
+ The source files in [src/](src/) have documentation in the form of TypeScript
179
+ annotations.
180
+
181
+ ## Examples
182
+
183
+ For working code showing different ways to use this library, see [the
184
+ demos](src/demos/). You can build and run the demos using:
185
+
186
+ ```sh
187
+ git clone https://github.com/basil/knuth-plass-linebreak.git
188
+ cd knuth-plass-linebreak
189
+ yarn
190
+ yarn dev
191
+ ```
192
+
193
+ Run the test suite with `yarn test`. The test script installs Playwright's
194
+ Chromium browser automatically if it is not already available locally.
195
+
196
+ ## Caveats
197
+
198
+ The library currently has a number of caveats:
199
+
200
+ - It is [not aware of floated content](https://github.com/robertknight/tex-linebreak/issues/1)
201
+ which can affect the available space in a paragraph to lay out text into.
202
+ In the presence of floats lines can exceed the width of the paragraph.
203
+ - Justification of existing HTML content relies on modifying the DOM to insert
204
+ linebreaks and wrap text nodes in order to adjust inter-word spacing on each
205
+ line. This can be in slow in large documents. Test it on your content to
206
+ decide whether the overhead is acceptable for your use case. Also limit the
207
+ number of elements which you apply justification to.
208
+
209
+ ## References
210
+
211
+ [1] D. E. Knuth and M. F. Plass,
212
+ “[Breaking paragraphs into lines](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf),”
213
+ Softw. Pract. Exp., vol. 11, no. 11, pp. 1119–1184, Nov. 1981.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { Box, Glue, Penalty, PositionedItem } from './layout';
2
+ export interface TextBox extends Box {
3
+ text: string;
4
+ }
5
+ export interface TextGlue extends Glue {
6
+ text: string;
7
+ }
8
+ export type TextInputItem = TextBox | TextGlue | Penalty;
9
+ /**
10
+ * A convenience function that generates a set of input items for `breakLines`
11
+ * from a string.
12
+ *
13
+ * @param s - Text to process
14
+ * @param measureFn - Callback that calculates the width of a given string
15
+ * @param hyphenateFn - Callback that calculates legal hyphenation points in
16
+ * words and returns an array of pieces that can be joined
17
+ * with hyphens.
18
+ */
19
+ export declare function layoutItemsFromString(s: string, measureFn: (word: string) => number, hyphenateFn?: (word: string) => string[]): TextInputItem[];
20
+ /**
21
+ * Helper for laying out a paragraph of text.
22
+ *
23
+ * @param text - The text to lay out
24
+ * @param lineWidth - Width for each line
25
+ * @param measure - Function which is called to measure each word or space in the input
26
+ * @param hyphenate - Function which is called to split words at possible
27
+ * hyphenation points
28
+ */
29
+ export declare function layoutText(text: string, lineWidth: number | number[], measure: (word: string) => number, hyphenate: (word: string) => string[]): {
30
+ items: TextInputItem[];
31
+ breakpoints: number[];
32
+ positions: PositionedItem[];
33
+ };
package/dist/html.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Reverse the changes made to an element by `justifyContent`.
3
+ */
4
+ export declare function unjustifyContent(el: HTMLElement): void;
5
+ /**
6
+ * Justify an existing paragraph.
7
+ *
8
+ * Justify the contents of `elements`, using `hyphenateFn` to apply hyphenation if
9
+ * necessary.
10
+ *
11
+ * To justify multiple paragraphs, it is more efficient to call `justifyContent`
12
+ * once with all the elements to be processed, than to call `justifyContent`
13
+ * separately for each element. Passing a list allows `justifyContent` to
14
+ * optimize DOM manipulations.
15
+ */
16
+ export declare function justifyContent(elements: HTMLElement | HTMLElement[], hyphenateFn?: (word: string) => string[]): void;
@@ -0,0 +1,14 @@
1
+ export interface Patterns {
2
+ id: string;
3
+ leftmin: number;
4
+ rightmin: number;
5
+ patterns: {
6
+ [key: string]: string;
7
+ };
8
+ }
9
+ /**
10
+ * Create a hyphenator that uses the given patterns.
11
+ *
12
+ * A wrapper around the `hypher` hyphenation library.
13
+ */
14
+ export declare function createHyphenator(patterns: Patterns): (word: string) => string[];
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),l=-1e3,u=1e3,d=-1;function f(e){return e.type===`penalty`&&e.cost<=-1e3}var p={maxAdjustmentRatio:null,initialMaxAdjustmentRatio:1,doubleHyphenPenalty:0,adjacentLooseTightPenalty:0},m=class extends Error{};function h(e,t,n={}){if(e.length===0)return[];if(!f(e[e.length-1]))throw Error(`Paragraph items must end with a forced break`);let r=e.some(e=>e.type===`box`||e.type===`penalty`?e.width<0:e.width<0||e.stretch<0||e.shrink<0),i={...p,...n},a=e=>Array.isArray(t)?t[e]:t,o=Math.min(i.initialMaxAdjustmentRatio,i.maxAdjustmentRatio===null?1/0:i.maxAdjustmentRatio),s=new Set;s.add({index:0,line:0,fitness:0,totalWidth:0,totalStretch:0,totalShrink:0,totalDemerits:0,prev:null});let c=0,l=0,g=0,_=1/0;for(let p=0;p<e.length;p++){let v=e[p],y=!1;if(v.type===`box`?c+=v.width:v.type===`glue`?(y=p>0&&e[p-1].type===`box`,y||(c+=v.width,g+=v.shrink,l+=v.stretch)):v.type===`penalty`&&(y=v.cost<u),!y)continue;let b=null,x=[];if(s.forEach(t=>{let n=0,u=g-t.totalShrink,m=l-t.totalStretch,h=a(t.line),y=c-t.totalWidth;if(v.type===`penalty`&&(y+=v.width),n=y<h?(h-y)/m:y>h?(h-y)/u:0,n>o&&(_=Math.min(n,_)),(!r&&n<d||f(v))&&(s.delete(t),b=t),n>=d&&n<=o){let r,a=100*Math.abs(n)**3,o=v.type===`penalty`?v.cost:0;r=o>=0?(1+a+o)**2:o>-1e3?(1+a)**2-o**2:(1+a)**2;let s=0,u=e[t.index];v.type===`penalty`&&u.type===`penalty`&&v.flagged&&u.flagged&&(s=i.doubleHyphenPenalty),r+=s;let d;d=n<-.5?0:n<.5?1:n<1?2:3,t.index>0&&Math.abs(d-t.fitness)>1&&(r+=i.adjacentLooseTightPenalty);let f=0,m=0,h=0;for(let t=p+(v.type===`penalty`?1:0);t<e.length;t++){let n=e[t];if(n.type===`box`||n.type===`penalty`&&n.cost>=1e3)break;f+=n.width,n.type===`glue`&&(m+=n.shrink,h+=n.stretch)}let _={index:p,line:t.line+1,fitness:d,totalWidth:c+f,totalShrink:g+m,totalStretch:l+h,totalDemerits:t.totalDemerits+r,prev:t};x.push(_)}}),x.length>0){let e=new Map;for(let t of x){let n=`${t.line}:${t.fitness}`,r=e.get(n);(!r||t.totalDemerits<r.totalDemerits)&&e.set(n,t)}e.forEach(e=>s.add(e))}if(s.size===0)if(isFinite(_)){if(i.maxAdjustmentRatio===o)throw new m;return h(e,t,{...n,initialMaxAdjustmentRatio:_*2})}else s.add({index:p,line:b.line+1,fitness:1,totalWidth:c,totalShrink:g,totalStretch:l,totalDemerits:b.totalDemerits+1e3,prev:b});v.type===`glue`&&(c+=v.width,l+=v.stretch,g+=v.shrink)}let v=null;s.forEach(e=>{(!v||e.totalDemerits<v.totalDemerits)&&(v=e)});let y=[],b=v;for(;b;)y.push(b.index),b=b.prev;return y.reverse(),y}function g(e,t,n){let r=e=>Array.isArray(t)?t[e]:t,i=[];for(let t=0;t<n.length-1;t++){let a=r(t),o=0,s=0,c=0,l=t===0?n[t]:n[t]+1;for(let r=l;r<=n[t+1];r++){let i=e[r];i.type===`box`?o+=i.width:i.type===`glue`&&r!==l&&r!==n[t+1]?(o+=i.width,s+=i.shrink,c+=i.stretch):i.type===`penalty`&&r===n[t+1]&&(o+=i.width)}let u;u=o<a?(a-o)/c:o>a?(a-o)/s:0,i.push(u)}return i}function _(e,t,n,r={}){let i=g(e,t,n),a=[];for(let t=0;t<n.length-1;t++){let o=Math.max(i[t],d),s=0,c=t===0?n[t]:n[t]+1;for(let i=c;i<=n[t+1];i++){let l=e[i];if(l.type===`box`)a.push({item:i,line:t,xOffset:s,width:l.width}),s+=l.width;else if(l.type===`glue`&&i!==c&&i!==n[t+1]){let e;e=o<0?l.width+o*l.shrink:l.width+o*l.stretch,r.includeGlue&&a.push({item:i,line:t,xOffset:s,width:e}),s+=e}else l.type===`penalty`&&i===n[t+1]&&l.width>0&&a.push({item:i,line:t,xOffset:s,width:l.width})}}return a}function v(){return{type:`penalty`,cost:l,width:0,flagged:!1}}function y(e,t,n){let r=[],i=e.split(/(\s+)/).filter(e=>e.length>0),a=t(` `),o=t(`-`),s=e=>/\s/.test(e.charAt(0)),c=Math.max(0,a-2);return i.forEach(e=>{if(s(e)){let t={type:`glue`,width:a,shrink:c,stretch:a*1.5,text:e};r.push(t);return}if(n){let i=n(e);i.forEach((e,n)=>{let a={type:`box`,width:t(e),text:e};if(r.push(a),n<i.length-1){let e={type:`penalty`,width:o,cost:10,flagged:!0};r.push(e)}})}else{let n={type:`box`,width:t(e),text:e};r.push(n)}}),r.push({type:`glue`,width:0,stretch:u,shrink:0,text:``}),r.push(v()),r}function b(e,t,n,r){let i,a,o;try{i=y(e,n),a=h(i,t,{maxAdjustmentRatio:1}),o=_(i,t,a)}catch(s){if(s instanceof m)i=y(e,n,r),a=h(i,t),o=_(i,t,a);else throw s}return{items:i,breakpoints:a,positions:o}}function x(e,t){let n=e.commonAncestorContainer,r=n.ownerDocument.createTreeWalker(n,NodeFilter.SHOW_ALL,{acceptNode(e){return t(e)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}}),i=r.currentNode,a=[];for(;i;)e.intersectsNode(i)&&i instanceof Text&&a.push(i),i=r.nextNode();return a}var S=class{_fonts;_textWidths;constructor(){this._fonts=new Map,this._textWidths=new Map}putFont(e,t){this._fonts.set(e,t)}cssFontForElement(e){return this._fonts.get(e)}putWidth(e,t,n){let r=this._textWidths.get(e);r||(r=new Map,this._textWidths.set(e,r)),r.set(t,n)}getWidth(e,t){let n=this._textWidths.get(e);return n?n.get(t):null}};function C(e){let t=getComputedStyle(e),n=t.font;if(n.length>0)return n;let{fontStyle:r,fontVariant:i,fontWeight:a,fontSize:o,fontFamily:s}=t;return n=`${r} ${i} ${a} ${o} ${s}`,n}var w;function T(e,t){return w||=document.createElement(`canvas`).getContext(`2d`),w.font=e,w.measureText(t).width}var E=class{_cache;constructor(){this._cache=new S}measure(e,t){let n=this._cache.cssFontForElement(e);n||(n=C(e),this._cache.putFont(e,n));let r=this._cache.getWidth(n,t);return r||(r=T(n,t),this._cache.putWidth(n,t,r)),r}},D=`insertedByTexLinebreak`;function O(e,t,n,r){let i=t.nodeValue,a=t.parentNode,o=n(a,` `),s=Math.max(0,o-3),c=n(a,`-`),l=e=>/\s/.test(e.charAt(0)),u=i.split(/(\s+)/).filter(e=>e.length>0),d=0;u.forEach(i=>{if(l(i)){let n={type:`glue`,width:o,shrink:s,stretch:o,node:t,start:d,end:d+i.length};e.push(n),d+=i.length;return}if(r){let o=r(i);o.forEach((r,i)=>{let s={type:`box`,width:n(a,r),node:t,start:d,end:d+r.length};if(d+=r.length,e.push(s),i<o.length-1){let n={type:`penalty`,width:c,cost:10,flagged:!0,node:t,start:d,end:d};e.push(n)}})}else{let r={type:`box`,width:n(a,i),node:t,start:d,end:d+i.length};d+=i.length,e.push(r)}})}function k(e,t,n,r){let{display:i,width:a,paddingLeft:o,paddingRight:s,marginLeft:c,marginRight:l,borderLeftWidth:u,borderRightWidth:d}=getComputedStyle(t);if(i===`inline`){let i=parseFloat(c)+parseFloat(u)+parseFloat(o);i>0&&e.push({type:`box`,width:i,node:t,start:0,end:0}),A(e,t,n,r,!1);let a=parseFloat(l)+parseFloat(d)+parseFloat(s);if(a>0){let n=t.childNodes.length;e.push({type:`box`,width:a,node:t,start:n,end:n})}}else e.push({type:`box`,width:parseFloat(a),node:t,start:0,end:1})}function A(e,t,n,r,i=!0){if(Array.from(t.childNodes).forEach(t=>{t instanceof Text?O(e,t,n,r):t instanceof Element&&k(e,t,n,r)}),i){let n=t.childNodes.length;e.push({type:`glue`,width:0,shrink:0,stretch:1e3,node:t,start:n,end:n}),e.push({...v(),node:t,start:n,end:n})}}function j(e){let{width:t,boxSizing:n,paddingLeft:r,paddingRight:i}=getComputedStyle(e),a=parseFloat(t);return n===`border-box`&&(a-=parseFloat(r),a-=parseFloat(i)),a}function M(e,t){let n=[],r=[];for(let i=0;i<t.length-1;i++){let a=0,o=0,s=i===0?t[i]:t[i]+1;for(let n=s;n<=t[i+1];n++){let r=e[n];r.type===`box`?a+=r.width:r.type===`glue`&&n!==s&&n!==t[i+1]?(a+=r.width,++o):r.type===`penalty`&&n===t[i+1]&&(a+=r.width)}n.push(a),r.push(o)}return[n,r]}function N(e){e[D]=!0}function P(e){return e.hasOwnProperty(D)}function F(e){let t=[];for(let n=0;n<e.childNodes.length;n++){let r=e.childNodes[n];P(r)&&t.push(r),r.childNodes.length>0&&t.push(...F(r))}return t}function I(e){return e instanceof Text?!0:e instanceof Element?getComputedStyle(e).display===`inline`:!1}function L(e,t){let n=x(e,I);for(let e of n){let n=document.createElement(`span`);N(n),n.style.wordSpacing=`${t}px`,e.parentNode.replaceChild(n,e),n.appendChild(e)}return n}function R(e){let t=F(e);for(let e of t){let t=e.parentNode;Array.from(e.childNodes).forEach(n=>{t.insertBefore(n,e)}),t.removeChild(e)}e.normalize()}function z(e,t){Array.isArray(e)||(e=[e]),e.forEach(e=>{R(e)});let n=new E,r=n.measure.bind(n),i=[];e.forEach(e=>{let n=j(e),a=[];A(a,e,r);let o;try{o=h(a,n,{maxAdjustmentRatio:2})}catch(i){if(i instanceof m)a=[],A(a,e,r,t),o=h(a,n);else throw i}i.push({el:e,items:a,breakpoints:o,lineWidth:n})}),i.forEach(({el:e,items:t,breakpoints:n,lineWidth:r})=>{let[i,a]=M(t,n),o=[],s=[];for(let r=1;r<n.length;r++){let i=t[n[r-1]],a=t[n[r]],c=document.createRange();r>1?c.setStart(i.node,i.end):c.setStart(e,0),c.setEnd(a.node,a.start),s.push(c),o.push(a.type===`penalty`&&a.flagged)}e.style.whiteSpace=`nowrap`,s.forEach((e,t)=>{if(t===0)return;let n=document.createElement(`br`);N(n),e.insertNode(n),e.setStart(n.nextSibling,0)}),s.forEach((e,t)=>{let n=(r-i[t])/a[t];if(t===s.length-1&&n>=0)return;let c=L(e,n);if(o[t]&&c.length>0){let e=c[c.length-1],t=document.createTextNode(`-`);N(t),e.parentNode.appendChild(t)}})})}var B=c(o(((e,t)=>{function n(e){var t=[],n=0;if(this.trie=this.createTrie(e.patterns),this.leftMin=e.leftmin,this.rightMin=e.rightmin,this.exceptions={},e.exceptions)for(t=e.exceptions.split(/,\s?/g);n<t.length;n+=1)this.exceptions[t[n].replace(/\u2027/g,``).toLowerCase()]=RegExp(`(`+t[n].split(`‧`).join(`)(`)+`)`,`i`)}n.TrieNode,n.prototype.createTrie=function(e){var t=0,n=0,r=0,i=0,a=null,o=null,s=null,c=null,l={_points:[]},u;for(t in e)if(e.hasOwnProperty(t))for(u=e[t].match(RegExp(`.{1,`+ +t+`}`,`g`)),n=0;n<u.length;n+=1){for(a=u[n].replace(/[0-9]/g,``).split(``),o=u[n].split(/\D/),c=l,r=0;r<a.length;r+=1)s=a[r].charCodeAt(0),c[s]||(c[s]={}),c=c[s];for(c._points=[],i=0;i<o.length;i+=1)c._points[i]=o[i]||0}return l},n.prototype.hyphenateText=function(e,t){t||=4;for(var n=e.split(/([a-zA-Z0-9_\u0027\u00AD\u00DF-\u00EA\u00EB\u00EC-\u00EF\u00F1-\u00F6\u00F8-\u00FD\u0101\u0103\u0105\u0107\u0109\u010D\u010F\u0111\u0113\u0117\u0119\u011B\u011D\u011F\u0123\u0125\u012B\u012F\u0131\u0135\u0137\u013C\u013E\u0142\u0144\u0146\u0148\u0151\u0153\u0155\u0159\u015B\u015D\u015F\u0161\u0165\u016B\u016D\u016F\u0171\u0173\u017A\u017C\u017E\u017F\u0219\u021B\u02BC\u0390\u03AC-\u03CE\u03F2\u0401\u0410-\u044F\u0451\u0454\u0456\u0457\u045E\u0491\u0531-\u0556\u0561-\u0587\u0902\u0903\u0905-\u090B\u090E-\u0910\u0912\u0914-\u0928\u092A-\u0939\u093E-\u0943\u0946-\u0948\u094A-\u094D\u0982\u0983\u0985-\u098B\u098F\u0990\u0994-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BE-\u09C3\u09C7\u09C8\u09CB-\u09CD\u09D7\u0A02\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A14-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A82\u0A83\u0A85-\u0A8B\u0A8F\u0A90\u0A94-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABE-\u0AC3\u0AC7\u0AC8\u0ACB-\u0ACD\u0B02\u0B03\u0B05-\u0B0B\u0B0F\u0B10\u0B14-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3E-\u0B43\u0B47\u0B48\u0B4B-\u0B4D\u0B57\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB5\u0BB7-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C02\u0C03\u0C05-\u0C0B\u0C0E-\u0C10\u0C12\u0C14-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3E-\u0C43\u0C46-\u0C48\u0C4A-\u0C4D\u0C82\u0C83\u0C85-\u0C8B\u0C8E-\u0C90\u0C92\u0C94-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBE-\u0CC3\u0CC6-\u0CC8\u0CCA-\u0CCD\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3E-\u0D43\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D60\u0D61\u0D7A-\u0D7F\u1F00-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB2-\u1FB4\u1FB6\u1FB7\u1FBD\u1FBF\u1FC2-\u1FC4\u1FC6\u1FC7\u1FD2\u1FD3\u1FD6\u1FD7\u1FE2-\u1FE7\u1FF2-\u1FF4\u1FF6\u1FF7\u200D\u2019]+)/gi),r=0;r<n.length;r+=1)n[r].indexOf(`/`)===-1?n[r].length>t&&(n[r]=this.hyphenate(n[r]).join(`­`)):r!==0&&r!==n.length-1&&!/\s+\/|\/\s+/.test(n[r])&&(n[r]+=`​`);return n.join(``)},n.prototype.hyphenate=function(e){var t,n=[],r,i,a,o,s,c=[],l,u=e.toLowerCase(),d,f,p=Math.max,m=this.trie,h=[``];if(this.exceptions.hasOwnProperty(u))return e.match(this.exceptions[u]).slice(1);if(e.indexOf(`­`)!==-1)return[e];for(e=`_`+e+`_`,t=e.toLowerCase().split(``),r=e.split(``),l=t.length,i=0;i<l;i+=1)c[i]=0,n[i]=t[i].charCodeAt(0);for(i=0;i<l;i+=1)for(s=m,a=i;a<l&&(s=s[n[a]],s);a+=1)if(d=s._points,d)for(o=0,f=d.length;o<f;o+=1)c[i+o]=p(c[i+o],d[o]);for(i=1;i<l-1;i+=1)i>this.leftMin&&i<l-this.rightMin&&c[i]%2?h.push(r[i]):h[h.length-1]+=r[i];return h},t.exports=n}))(),1);function V(e){let t=new B.default(e);return e=>t.hyphenate(e)}exports.MAX_COST=u,exports.MIN_COST=l,exports.MaxAdjustmentExceededError=m,exports.adjustmentRatios=g,exports.breakLines=h,exports.createHyphenator=V,exports.forcedBreak=v,exports.justifyContent=z,exports.layoutItemsFromString=y,exports.layoutText=b,exports.positionItems=_,exports.unjustifyContent=R;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/layout.ts","../src/helpers.ts","../src/util/range.ts","../src/util/dom-text-measurer.ts","../src/html.ts","../node_modules/hypher/lib/hypher.js","../src/hyphenate.ts"],"sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2018-2021 Robert Knight\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * An object (eg. a word) to be typeset.\n */\nexport interface Box {\n type: 'box';\n\n /** Amount of space required by this content. Can be negative. */\n width: number;\n}\n\n/**\n * A space between `Box` items with a preferred width and some\n * capacity to stretch or shrink.\n *\n * `Glue` items are also candidates for breakpoints if they immediately follow a\n * `Box`.\n */\nexport interface Glue {\n type: 'glue';\n /**\n * Preferred width of this space. Can be negative.\n */\n width: number;\n /** Maximum amount by which this space can grow. Can be negative. */\n stretch: number;\n /** Maximum amount by which this space can shrink. Can be negative. */\n shrink: number;\n}\n\n/**\n * An explicit candidate position for breaking a line.\n */\nexport interface Penalty {\n type: 'penalty';\n\n /**\n * Amount of space required for typeset content to be added (eg. a hyphen) if\n * a line is broken here. Can be negative.\n */\n width: number;\n /**\n * The undesirability of breaking the line at this point.\n *\n * Values <= `MIN_COST` and >= `MAX_COST` mandate or prevent breakpoints\n * respectively.\n */\n cost: number;\n /**\n * A hint used to prevent successive lines being broken with hyphens. The\n * layout algorithm will try to avoid successive lines being broken at flagged\n * `Penalty` items.\n */\n flagged: boolean;\n}\n\nexport type InputItem = Box | Penalty | Glue;\n\n/**\n * Parameters for the layout process.\n */\nexport interface Options {\n /**\n * A factor indicating the maximum amount by which items in a line can be\n * spaced out by expanding `Glue` items.\n *\n * The maximum size which a `Glue` on a line can expand to is `glue.width +\n * (maxAdjustmentRatio * glue.stretch)`.\n *\n * If the paragraph cannot be laid out without exceeding this threshold then a\n * `MaxAdjustmentExceededError` error is thrown. The caller can use this to\n * apply hyphenation and try again. If `null`, lines are stretched as far as\n * necessary.\n */\n maxAdjustmentRatio: number | null;\n\n /**\n * The maximum adjustment ratio used for the initial line breaking attempt.\n */\n initialMaxAdjustmentRatio: number;\n\n /**\n * Penalty for consecutive hyphenated lines.\n */\n doubleHyphenPenalty: number;\n\n /**\n * Penalty for significant differences in the tightness of adjacent lines.\n */\n adjacentLooseTightPenalty: number;\n}\n\n/**\n * Minimum cost for a breakpoint.\n *\n * Values <= `MIN_COST` force a break.\n */\nexport const MIN_COST = -1000;\n\n/**\n * Maximum cost for a breakpoint.\n *\n * Values >= `MAX_COST` prevent a break.\n */\nexport const MAX_COST = 1000;\n\nconst MIN_ADJUSTMENT_RATIO = -1;\n\nfunction isForcedBreak(item: InputItem) {\n return item.type === 'penalty' && item.cost <= MIN_COST;\n}\n\nconst defaultOptions: Options = {\n maxAdjustmentRatio: null,\n initialMaxAdjustmentRatio: 1,\n doubleHyphenPenalty: 0,\n adjacentLooseTightPenalty: 0,\n};\n\n/**\n * Error thrown by `breakLines` when `maxAdjustmentRatio` is exceeded.\n */\nexport class MaxAdjustmentExceededError extends Error {}\n\n/**\n * Break a paragraph of text into justified lines.\n *\n * Returns the indexes from `items` which have been chosen as breakpoints.\n * `positionBoxes` can be used to generate the X offsets and line numbers of\n * each box using the resulting breakpoints.\n *\n * May throw an `Error` if valid breakpoints cannot be found given the specified\n * adjustment ratio thresholds.\n *\n * The implementation uses the \"TeX algorithm\" from [1].\n *\n * [1] D. E. Knuth and M. F. Plass, “Breaking paragraphs into lines,” Softw.\n * Pract. Exp., vol. 11, no. 11, pp. 1119–1184, Nov. 1981.\n *\n * @param items - Sequence of box, glue and penalty items to layout.\n * @param lineLengths - Length or lengths of each line.\n */\nexport function breakLines(\n items: InputItem[],\n lineLengths: number | number[],\n opts: Partial<Options> = {},\n): number[] {\n if (items.length === 0) {\n return [];\n }\n\n if (!isForcedBreak(items[items.length - 1])) {\n // Knuth-Plass assumes that at the very end of a paragraph a forced break\n // is appended after the finishing glue.\n throw new Error('Paragraph items must end with a forced break');\n }\n\n const hasNegativeValues = items.some((it) => {\n if (it.type === 'box' || it.type === 'penalty') {\n return it.width < 0;\n } else {\n return it.width < 0 || it.stretch < 0 || it.shrink < 0;\n }\n });\n\n const opts_ = { ...defaultOptions, ...opts };\n const lineLen = (i: number) => (Array.isArray(lineLengths) ? lineLengths[i] : lineLengths);\n const currentMaxAdjustmentRatio = Math.min(\n opts_.initialMaxAdjustmentRatio,\n opts_.maxAdjustmentRatio !== null ? opts_.maxAdjustmentRatio : Infinity,\n );\n\n type Node = {\n index: number; // Index in `items`.\n line: number; // Line number.\n fitness: number;\n // Sum of `width` up to first box or forced break after this break.\n totalWidth: number;\n // Sum of `stretch` up to first box or forced break after this break.\n totalStretch: number;\n // Sum of `shrink` up to first box or forced break after this break.\n totalShrink: number;\n // Minimum sum of demerits up this break.\n totalDemerits: number;\n prev: null | Node;\n };\n\n const active = new Set<Node>();\n\n // Add initial active node for beginning of paragraph.\n active.add({\n index: 0,\n line: 0,\n // Fitness is ignored for this node.\n fitness: 0,\n totalWidth: 0,\n totalStretch: 0,\n totalShrink: 0,\n totalDemerits: 0,\n prev: null,\n });\n\n // Sum of `width` of items up to current item.\n let sumWidth = 0;\n // Sum of `stretch` of glue items up to current item.\n let sumStretch = 0;\n // Sum of `shrink` of glue items up to current item.\n let sumShrink = 0;\n\n let minAdjustmentRatioAboveThreshold = Infinity;\n\n for (let b = 0; b < items.length; b++) {\n const item = items[b];\n\n // Determine if this is a feasible breakpoint and update `sumWidth`,\n // `sumStretch` and `sumShrink`.\n let canBreak = false;\n if (item.type === 'box') {\n sumWidth += item.width;\n } else if (item.type === 'glue') {\n canBreak = b > 0 && items[b - 1].type === 'box';\n if (!canBreak) {\n sumWidth += item.width;\n sumShrink += item.shrink;\n sumStretch += item.stretch;\n }\n } else if (item.type === 'penalty') {\n canBreak = item.cost < MAX_COST;\n }\n if (!canBreak) {\n continue;\n }\n\n // Update the set of active nodes.\n let lastActive: Node | null = null;\n\n const feasible: Node[] = [];\n active.forEach((a) => {\n // Compute adjustment ratio from `a` to `b`.\n let adjustmentRatio = 0;\n const lineShrink = sumShrink - a.totalShrink;\n const lineStretch = sumStretch - a.totalStretch;\n const idealLen = lineLen(a.line);\n let actualLen = sumWidth - a.totalWidth;\n\n // Include width of penalty in line length if chosen as a breakpoint.\n if (item.type === 'penalty') {\n actualLen += item.width;\n }\n\n if (actualLen < idealLen) {\n adjustmentRatio = (idealLen - actualLen) / lineStretch;\n } else if (actualLen > idealLen) {\n adjustmentRatio = (idealLen - actualLen) / lineShrink;\n } else {\n adjustmentRatio = 0;\n }\n if (adjustmentRatio > currentMaxAdjustmentRatio) {\n // In case we need to try again later with a higher\n // `maxAdjustmentRatio`, track the minimum value needed to produce\n // different output.\n minAdjustmentRatioAboveThreshold = Math.min(\n adjustmentRatio,\n minAdjustmentRatioAboveThreshold,\n );\n }\n\n // The optimization that removes an active node when $r < –1$ is safe only if\n // all widths, stretches, and shrinks are non–negative, as explained on p.\n // 1161:\n //\n // \"Note that Restriction 1 makes it legitimate to deactivate a node when\n // we discover that $r < -1$, since $r < -1$ is equivalent to $l_l < L_{ab}\n // - Z_{ab}$, therefore subsequent breakpoints $b' > b$ will have $L_{ab'}\n // - Z_{ab'} >= L_{ab} - Z_{ab}$. Thus it is not difficult to verify that\n // the algorithm does indeed find an optimal solution: Given any sequence\n // of feasible breakpoints $b_1 < ... < b_k$, we can prove by induction on\n // $j$ that the algorithm constructs a node for a feasible break at $j$,\n // with appropriate line numbers and fitness classifications, having no\n // more demerits than the given sequence does.\"\n //\n // Therefore, we deactivate an active node in the usual TeX way only when the\n // paragraph satisfies Restriction 1 (i.e., no negative values). Forced breaks\n // are always allowed to prune the list.\n if ((!hasNegativeValues && adjustmentRatio < MIN_ADJUSTMENT_RATIO) || isForcedBreak(item)) {\n // Items from `a` to `b` cannot fit on one line.\n active.delete(a);\n lastActive = a;\n }\n if (adjustmentRatio >= MIN_ADJUSTMENT_RATIO && adjustmentRatio <= currentMaxAdjustmentRatio) {\n // We found a feasible breakpoint. Compute a `demerits` score for it as\n // per formula on p. 1128.\n let demerits;\n const badness = 100 * Math.abs(adjustmentRatio) ** 3;\n const penalty = item.type === 'penalty' ? item.cost : 0;\n\n if (penalty >= 0) {\n demerits = (1 + badness + penalty) ** 2;\n } else if (penalty > MIN_COST) {\n demerits = (1 + badness) ** 2 - penalty ** 2;\n } else {\n demerits = (1 + badness) ** 2;\n }\n\n let doubleHyphenPenalty = 0;\n const prevItem = items[a.index];\n if (item.type === 'penalty' && prevItem.type === 'penalty') {\n if (item.flagged && prevItem.flagged) {\n doubleHyphenPenalty = opts_.doubleHyphenPenalty;\n }\n }\n demerits += doubleHyphenPenalty;\n\n // Fitness classes are defined on p. 1155\n let fitness;\n if (adjustmentRatio < -0.5) {\n fitness = 0;\n } else if (adjustmentRatio < 0.5) {\n fitness = 1;\n } else if (adjustmentRatio < 1) {\n fitness = 2;\n } else {\n fitness = 3;\n }\n if (a.index > 0 && Math.abs(fitness - a.fitness) > 1) {\n demerits += opts_.adjacentLooseTightPenalty;\n }\n\n // If this breakpoint is followed by glue or non-breakable penalty items\n // then we don't want to include the width of those when calculating the\n // width of lines starting after this breakpoint. This is because when\n // rendering we ignore glue/penalty items at the start of lines.\n //\n // Knuth’s original algorithm (TeX) keeps running sums of width / stretch /\n // shrink from the *start* of the paragraph up to every breakpoint. The\n // values for an individual line are then obtained simply by subtracting the\n // two totals:\n //\n // lineWidth = Σwidth[b] − Σwidth[a]\n // lineStretch/shrink likewise\n //\n // This is only correct if there is at least one **box** item between the\n // two breakpoints; otherwise the subtraction would leave out the glue and\n // penalties that precede the first box of the new line. For efficiency TeX\n // therefore imposes Restriction 2 (p. 1156): two legal breakpoints may not\n // be separated solely by glue and/or ordinary penalties.\n //\n // Instead of enforcing Restriction 2 we take a different approach: whenever\n // we create a node for a candidate breakpoint `b` we *look ahead* until we\n // reach the next box (or an unbreakable penalty) and add the intervening\n // width/stretch/shrink to the node’s accumulated totals. Hence\n //\n // node.totalWidth = Σwidth up to the first box of the next line\n // node.totalStretch = …\n // node.totalShrink = …\n //\n // With these augmented totals the simple subtraction still yields the correct\n // figures even if two successive breakpoints are separated only by glue.\n // The price is a tiny extra scan per node, which is acceptable for the sizes\n // we deal with here and keeps the rest of the algorithm (and the input it can\n // accept) simple.\n let widthToNextBox = 0;\n let shrinkToNextBox = 0;\n let stretchToNextBox = 0;\n // A penalty's width (e.g. a hyphen) is charged to the line that ends\n // here, not the next line. Skip it so it doesn't inflate the totals.\n for (let bp = b + (item.type === 'penalty' ? 1 : 0); bp < items.length; bp++) {\n const item = items[bp];\n if (item.type === 'box') {\n break;\n }\n if (item.type === 'penalty' && item.cost >= MAX_COST) {\n break;\n }\n widthToNextBox += item.width;\n if (item.type === 'glue') {\n shrinkToNextBox += item.shrink;\n stretchToNextBox += item.stretch;\n }\n }\n\n const node = {\n index: b,\n line: a.line + 1,\n fitness,\n totalWidth: sumWidth + widthToNextBox,\n totalShrink: sumShrink + shrinkToNextBox,\n totalStretch: sumStretch + stretchToNextBox,\n totalDemerits: a.totalDemerits + demerits,\n prev: a,\n };\n feasible.push(node);\n }\n });\n\n // Knuth-Plass p. 1155: \"a feasible breakpoint may now have to be\n // entered into the active list up to four times.\" Keep the best node for\n // each future-relevant state instead of collapsing everything at `b` to a\n // single winner.\n if (feasible.length > 0) {\n const bestByState = new Map<string, Node>();\n for (const f of feasible) {\n const key = `${f.line}:${f.fitness}`;\n const bestNode = bestByState.get(key);\n if (!bestNode || f.totalDemerits < bestNode.totalDemerits) {\n bestByState.set(key, f);\n }\n }\n bestByState.forEach((node) => active.add(node));\n }\n\n // Handle situation where there is no way to break the paragraph without\n // shrinking or stretching a line beyond [-1, currentMaxAdjustmentRatio].\n if (active.size === 0) {\n if (isFinite(minAdjustmentRatioAboveThreshold)) {\n if (opts_.maxAdjustmentRatio === currentMaxAdjustmentRatio) {\n throw new MaxAdjustmentExceededError();\n }\n // Too much stretching was required for an earlier ignored breakpoint.\n // Try again with a higher threshold.\n return breakLines(items, lineLengths, {\n ...opts,\n initialMaxAdjustmentRatio: minAdjustmentRatioAboveThreshold * 2,\n });\n } else {\n // We cannot create a breakpoint sequence by increasing the max\n // adjustment ratio. This could happen if a box is too wide or there are\n // glue items with zero stretch/shrink.\n //\n // Give up and create a breakpoint at the current position.\n active.add({\n index: b,\n line: lastActive!.line + 1,\n fitness: 1,\n totalWidth: sumWidth,\n totalShrink: sumShrink,\n totalStretch: sumStretch,\n totalDemerits: lastActive!.totalDemerits + 1000,\n prev: lastActive!,\n });\n }\n }\n\n if (item.type === 'glue') {\n sumWidth += item.width;\n sumStretch += item.stretch;\n sumShrink += item.shrink;\n }\n }\n\n // Choose active node with fewest total demerits as the last breakpoint.\n //\n // There should always be an active node at this point since:\n //\n // 1. We add a node to the active set before entering the loop.\n // 2. Each iteration of the loop either returns from the function, leaves the\n // active set unchanged and breaks early or finishes with a non-empty active\n // set.\n let bestNode: Node | null = null;\n active.forEach((a) => {\n if (!bestNode || a.totalDemerits < bestNode.totalDemerits) {\n bestNode = a;\n }\n });\n\n // Follow the chain backwards from the chosen node to get the sequence of\n // chosen breakpoints.\n const output = [];\n let next: Node | null = bestNode!;\n while (next) {\n output.push(next.index);\n next = next.prev;\n }\n output.reverse();\n\n return output;\n}\n\nexport interface PositionedItem {\n /** Index of the item. */\n item: number;\n /** Index of the line on which the resulting item should appear. */\n line: number;\n /** X offset of the item. */\n xOffset: number;\n /**\n * Width which this item should be rendered with.\n *\n * For box and penalty items this will just be the item's width.\n * For glue items this will be the adjusted width.\n */\n width: number;\n}\n\n/**\n * Compute adjustment ratios for lines given a set of breakpoints.\n *\n * The adjustment ratio of a line is the proportion of each glue item's stretch\n * (if positive) or shrink (if negative) which needs to be used in order to make\n * the line the specified width. A value of zero indicates that every glue item\n * is exactly its preferred width.\n *\n * @param items - The box, glue and penalty items being laid out\n * @param lineLengths - Length or lengths of each line\n * @param breakpoints - Indexes in `items` where lines are being broken\n */\nexport function adjustmentRatios(\n items: InputItem[],\n lineLengths: number | number[],\n breakpoints: number[],\n) {\n const lineLen = (i: number) => (Array.isArray(lineLengths) ? lineLengths[i] : lineLengths);\n const ratios = [];\n\n for (let b = 0; b < breakpoints.length - 1; b++) {\n let idealWidth = lineLen(b);\n let actualWidth = 0;\n let lineShrink = 0;\n let lineStretch = 0;\n\n const start = b === 0 ? breakpoints[b] : breakpoints[b] + 1;\n for (let p = start; p <= breakpoints[b + 1]; p++) {\n const item = items[p];\n if (item.type === 'box') {\n actualWidth += item.width;\n } else if (item.type === 'glue' && p !== start && p !== breakpoints[b + 1]) {\n actualWidth += item.width;\n lineShrink += item.shrink;\n lineStretch += item.stretch;\n } else if (item.type === 'penalty' && p === breakpoints[b + 1]) {\n actualWidth += item.width;\n }\n }\n\n let adjustmentRatio;\n if (actualWidth < idealWidth) {\n adjustmentRatio = (idealWidth - actualWidth) / lineStretch;\n } else if (actualWidth > idealWidth) {\n adjustmentRatio = (idealWidth - actualWidth) / lineShrink;\n } else {\n adjustmentRatio = 0;\n }\n\n ratios.push(adjustmentRatio);\n }\n\n return ratios;\n}\n\nexport interface PositionOptions {\n includeGlue?: boolean;\n}\n\n/**\n * Compute the positions at which to draw boxes forming a paragraph given a set\n * of breakpoints.\n *\n * @param items - The sequence of items that form the paragraph.\n * @param lineLengths - Length or lengths of each line.\n * @param breakpoints - Indexes within `items` of the start of each line.\n */\nexport function positionItems(\n items: InputItem[],\n lineLengths: number | number[],\n breakpoints: number[],\n options: PositionOptions = {},\n): PositionedItem[] {\n const adjRatios = adjustmentRatios(items, lineLengths, breakpoints);\n const result: PositionedItem[] = [];\n\n for (let b = 0; b < breakpoints.length - 1; b++) {\n // Limit the amount of shrinking of lines to 1x `glue.shrink` for each glue\n // item in a line.\n const adjustmentRatio = Math.max(adjRatios[b], MIN_ADJUSTMENT_RATIO);\n let xOffset = 0;\n const start = b === 0 ? breakpoints[b] : breakpoints[b] + 1;\n\n for (let p = start; p <= breakpoints[b + 1]; p++) {\n const item = items[p];\n if (item.type === 'box') {\n result.push({\n item: p,\n line: b,\n xOffset,\n width: item.width,\n });\n xOffset += item.width;\n } else if (item.type === 'glue' && p !== start && p !== breakpoints[b + 1]) {\n let gap;\n if (adjustmentRatio < 0) {\n gap = item.width + adjustmentRatio * item.shrink;\n } else {\n gap = item.width + adjustmentRatio * item.stretch;\n }\n if (options.includeGlue) {\n result.push({\n item: p,\n line: b,\n xOffset,\n width: gap,\n });\n }\n xOffset += gap;\n } else if (item.type === 'penalty' && p === breakpoints[b + 1] && item.width > 0) {\n result.push({\n item: p,\n line: b,\n xOffset,\n width: item.width,\n });\n }\n }\n }\n\n return result;\n}\n\n/**\n * Return a `Penalty` item which forces a line-break.\n */\nexport function forcedBreak(): Penalty {\n return { type: 'penalty', cost: MIN_COST, width: 0, flagged: false };\n}\n","/*\n * MIT License\n *\n * Copyright (c) 2018-2021 Robert Knight\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport {\n breakLines,\n positionItems,\n MaxAdjustmentExceededError,\n Box,\n Glue,\n Penalty,\n PositionedItem,\n MAX_COST,\n forcedBreak,\n} from './layout';\n\nexport interface TextBox extends Box {\n text: string;\n}\n\nexport interface TextGlue extends Glue {\n text: string;\n}\n\nexport type TextInputItem = TextBox | TextGlue | Penalty;\n\n/**\n * A convenience function that generates a set of input items for `breakLines`\n * from a string.\n *\n * @param s - Text to process\n * @param measureFn - Callback that calculates the width of a given string\n * @param hyphenateFn - Callback that calculates legal hyphenation points in\n * words and returns an array of pieces that can be joined\n * with hyphens.\n */\nexport function layoutItemsFromString(\n s: string,\n measureFn: (word: string) => number,\n hyphenateFn?: (word: string) => string[],\n): TextInputItem[] {\n const items: TextInputItem[] = [];\n const chunks = s.split(/(\\s+)/).filter((w) => w.length > 0);\n\n // Here we assume that every space has the same default size. Callers who want\n // more flexibility can use the lower-level functions.\n const spaceWidth = measureFn(' ');\n const hyphenWidth = measureFn('-');\n const isSpace = (word: string) => /\\s/.test(word.charAt(0));\n\n const shrink = Math.max(0, spaceWidth - 2);\n chunks.forEach((w) => {\n if (isSpace(w)) {\n const g: TextGlue = {\n type: 'glue',\n width: spaceWidth,\n shrink,\n stretch: spaceWidth * 1.5,\n text: w,\n };\n items.push(g);\n return;\n }\n\n if (hyphenateFn) {\n const chunks = hyphenateFn(w);\n chunks.forEach((c, i) => {\n const b: TextBox = { type: 'box', width: measureFn(c), text: c };\n items.push(b);\n if (i < chunks.length - 1) {\n const hyphen: Penalty = { type: 'penalty', width: hyphenWidth, cost: 10, flagged: true };\n items.push(hyphen);\n }\n });\n } else {\n const b: TextBox = { type: 'box', width: measureFn(w), text: w };\n items.push(b);\n }\n });\n // Add \"finishing glue\" to space out final line.\n items.push({ type: 'glue', width: 0, stretch: MAX_COST, shrink: 0, text: '' });\n items.push(forcedBreak());\n\n return items;\n}\n\n/**\n * Helper for laying out a paragraph of text.\n *\n * @param text - The text to lay out\n * @param lineWidth - Width for each line\n * @param measure - Function which is called to measure each word or space in the input\n * @param hyphenate - Function which is called to split words at possible\n * hyphenation points\n */\nexport function layoutText(\n text: string,\n lineWidth: number | number[],\n measure: (word: string) => number,\n hyphenate: (word: string) => string[],\n) {\n let items: TextInputItem[];\n let breakpoints;\n let positions: PositionedItem[];\n\n try {\n items = layoutItemsFromString(text, measure);\n breakpoints = breakLines(items, lineWidth, {\n maxAdjustmentRatio: 1,\n });\n positions = positionItems(items, lineWidth, breakpoints);\n } catch (e) {\n if (e instanceof MaxAdjustmentExceededError) {\n items = layoutItemsFromString(text, measure, hyphenate);\n breakpoints = breakLines(items, lineWidth);\n positions = positionItems(items, lineWidth, breakpoints);\n } else {\n throw e;\n }\n }\n\n return { items, breakpoints, positions };\n}\n","/*\n * MIT License\n *\n * Copyright (c) 2018-2021 Robert Knight\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\ndeclare global {\n interface Range {\n intersectsNode(node: Node): boolean;\n }\n}\n\n/**\n * Return a list of `Text` nodes in `range`.\n *\n * `filter` is called with each node in document order in the subtree rooted\n * at `range.commonAncestorContainer`. If it returns false, that node and its\n * children are skipped.\n */\nexport function textNodesInRange(range: Range, filter: (n: Node) => boolean) {\n const root = range.commonAncestorContainer;\n const nodeIter = root.ownerDocument!.createTreeWalker(root, NodeFilter.SHOW_ALL, {\n acceptNode(node: Node) {\n if (filter(node)) {\n return NodeFilter.FILTER_ACCEPT;\n } else {\n return NodeFilter.FILTER_REJECT;\n }\n },\n });\n\n let currentNode: Node | null = nodeIter.currentNode;\n let nodes: Text[] = [];\n\n while (currentNode) {\n if (range.intersectsNode(currentNode) && currentNode instanceof Text) {\n nodes.push(currentNode);\n }\n currentNode = nodeIter.nextNode();\n }\n return nodes;\n}\n","/*\n * MIT License\n *\n * Copyright (c) 2018 Robert Knight\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nclass TextMetricsCache {\n private _fonts: Map<Element, string>;\n private _textWidths: Map<string, Map<string, number>>;\n\n constructor() {\n this._fonts = new Map();\n this._textWidths = new Map();\n }\n\n putFont(el: Element, cssFont: string) {\n this._fonts.set(el, cssFont);\n }\n\n cssFontForElement(el: Element) {\n return this._fonts.get(el);\n }\n\n putWidth(cssFont: string, word: string, width: number) {\n let widths = this._textWidths.get(cssFont);\n if (!widths) {\n widths = new Map();\n this._textWidths.set(cssFont, widths);\n }\n widths.set(word, width);\n }\n\n getWidth(cssFont: string, word: string) {\n const widths = this._textWidths.get(cssFont);\n if (!widths) {\n return null;\n }\n return widths.get(word);\n }\n}\n\n/**\n * Return the computed CSS `font` property value for an element.\n */\nfunction cssFontForElement(el: Element) {\n const style = getComputedStyle(el);\n\n // Safari and Chrome can synthesize a value for `font` for us.\n let font = style.font!;\n if (font.length > 0) {\n return font;\n }\n\n // Fall back to generating CSS font property value if browser (eg. Firefox)\n // does not synthesize it automatically.\n const { fontStyle, fontVariant, fontWeight, fontSize, fontFamily } = style;\n font = `${fontStyle!} ${fontVariant!} ${fontWeight!} ${fontSize!} ${fontFamily}`;\n return font;\n}\n\nlet measureCtx: CanvasRenderingContext2D;\n\n/**\n * Measure the width of `text` as it would appear if rendered within an\n * `Element` with a given computed `font` style.\n */\nfunction measureText(cssFont: string, text: string) {\n if (!measureCtx) {\n const canvas = document.createElement('canvas');\n measureCtx = canvas.getContext('2d')!;\n }\n\n // Capture as much of the style as possible. Note that some properties such\n // as `font-stretch`, `font-size-adjust` and `font-kerning` are not settable\n // through the CSS `font` property.\n //\n // Apparently in some browsers the canvas context's text style inherits\n // style properties from the `<canvas>` element.\n // See https://stackoverflow.com/a/8955835/434243\n measureCtx.font = cssFont;\n return measureCtx.measureText(text).width;\n}\n\n/** Measure the width of pieces of text in the DOM, with caching. */\nexport default class DOMTextMeasurer {\n private _cache: TextMetricsCache;\n\n constructor() {\n this._cache = new TextMetricsCache();\n }\n\n /**\n * Return the width of `text` rendered by a `Text` node child of `context`.\n */\n measure(context: Element, text: string) {\n let cssFont = this._cache.cssFontForElement(context);\n if (!cssFont) {\n cssFont = cssFontForElement(context);\n this._cache.putFont(context, cssFont);\n }\n let width = this._cache.getWidth(cssFont, text);\n if (!width) {\n width = measureText(cssFont, text);\n this._cache.putWidth(cssFont, text, width);\n }\n return width;\n }\n}\n","/*\n * MIT License\n *\n * Copyright (c) 2018-2021 Robert Knight\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport {\n breakLines,\n forcedBreak,\n InputItem,\n MaxAdjustmentExceededError,\n Box,\n Glue,\n Penalty,\n} from './layout';\nimport { textNodesInRange } from './util/range';\nimport DOMTextMeasurer from './util/dom-text-measurer';\n\nconst NODE_TAG = 'insertedByTexLinebreak';\n\ninterface NodeOffset {\n node: Node;\n start: number;\n end: number;\n}\n\ntype DOMBox = Box & NodeOffset;\ntype DOMGlue = Glue & NodeOffset;\ntype DOMPenalty = Penalty & NodeOffset;\ntype DOMItem = DOMBox | DOMGlue | DOMPenalty;\n\n/**\n * Add layout items for `node` to `items`.\n */\nfunction addItemsForTextNode(\n items: DOMItem[],\n node: Text,\n measureFn: (context: Element, word: string) => number,\n hyphenateFn?: (word: string) => string[],\n) {\n const text = node.nodeValue!;\n const el = node.parentNode! as Element;\n\n const spaceWidth = measureFn(el, ' ');\n const shrink = Math.max(0, spaceWidth - 3);\n const hyphenWidth = measureFn(el, '-');\n const isSpace = (word: string) => /\\s/.test(word.charAt(0));\n\n const chunks = text.split(/(\\s+)/).filter((w) => w.length > 0);\n let textOffset = 0;\n\n chunks.forEach((w) => {\n if (isSpace(w)) {\n const glue: DOMGlue = {\n type: 'glue',\n width: spaceWidth,\n shrink,\n stretch: spaceWidth,\n node,\n start: textOffset,\n end: textOffset + w.length,\n };\n items.push(glue);\n textOffset += w.length;\n return;\n }\n\n if (hyphenateFn) {\n const chunks = hyphenateFn(w);\n chunks.forEach((c, i) => {\n const box: DOMBox = {\n type: 'box',\n width: measureFn(el, c),\n node,\n start: textOffset,\n end: textOffset + c.length,\n };\n textOffset += c.length;\n items.push(box);\n if (i < chunks.length - 1) {\n const hyphen: DOMPenalty = {\n type: 'penalty',\n width: hyphenWidth,\n cost: 10,\n flagged: true,\n node,\n start: textOffset,\n end: textOffset,\n };\n items.push(hyphen);\n }\n });\n } else {\n const box: DOMBox = {\n type: 'box',\n width: measureFn(el, w),\n node,\n start: textOffset,\n end: textOffset + w.length,\n };\n textOffset += w.length;\n items.push(box);\n }\n });\n}\n\n/**\n * Add layout items for `element` and its descendants to `items`.\n */\nfunction addItemsForElement(\n items: DOMItem[],\n element: Element,\n measureFn: (context: Element, word: string) => number,\n hyphenateFn?: (word: string) => string[],\n) {\n const {\n display,\n width,\n paddingLeft,\n paddingRight,\n marginLeft,\n marginRight,\n borderLeftWidth,\n borderRightWidth,\n } = getComputedStyle(element);\n\n if (display === 'inline') {\n // Add box for margin/border/padding at start of box.\n const leftMargin =\n parseFloat(marginLeft!) + parseFloat(borderLeftWidth!) + parseFloat(paddingLeft!);\n if (leftMargin > 0) {\n items.push({ type: 'box', width: leftMargin, node: element, start: 0, end: 0 });\n }\n\n // Add items for child nodes.\n addItemsForNode(items, element, measureFn, hyphenateFn, false);\n\n // Add box for margin/border/padding at end of box.\n const rightMargin =\n parseFloat(marginRight!) + parseFloat(borderRightWidth!) + parseFloat(paddingRight!);\n if (rightMargin > 0) {\n const length = element.childNodes.length;\n items.push({ type: 'box', width: rightMargin, node: element, start: length, end: length });\n }\n } else {\n // Treat this item as an opaque box.\n items.push({\n type: 'box',\n width: parseFloat(width!),\n node: element,\n start: 0,\n end: 1,\n });\n }\n}\n\n/**\n * Add layout items for input to `breakLines` for `node` to `items`.\n *\n * This function, `addItemsForTextNode` and `addItemsForElement` take an\n * existing array as a first argument to avoid allocating a large number of\n * small arrays.\n */\nfunction addItemsForNode(\n items: DOMItem[],\n node: Node,\n measureFn: (context: Element, word: string) => number,\n hyphenateFn?: (word: string) => string[],\n addParagraphEnd = true,\n) {\n const children = Array.from(node.childNodes);\n\n children.forEach((child) => {\n if (child instanceof Text) {\n addItemsForTextNode(items, child, measureFn, hyphenateFn);\n } else if (child instanceof Element) {\n addItemsForElement(items, child, measureFn, hyphenateFn);\n }\n });\n\n if (addParagraphEnd) {\n const end = node.childNodes.length;\n\n // Add a synthetic glue that aborbs any left-over space at the end of the\n // last line.\n items.push({ type: 'glue', width: 0, shrink: 0, stretch: 1000, node, start: end, end });\n\n // Add a forced break to end the paragraph.\n items.push({ ...forcedBreak(), node, start: end, end });\n }\n}\n\nfunction elementLineWidth(el: HTMLElement) {\n const { width, boxSizing, paddingLeft, paddingRight } = getComputedStyle(el);\n let w = parseFloat(width!);\n if (boxSizing === 'border-box') {\n w -= parseFloat(paddingLeft!);\n w -= parseFloat(paddingRight!);\n }\n return w;\n}\n\n/**\n * Calculate the actual width of each line and the number of spaces that can be\n * stretched or shrunk to adjust the width.\n */\nfunction lineWidthsAndGlueCounts(items: InputItem[], breakpoints: number[]) {\n const widths: number[] = [];\n const glueCounts: number[] = [];\n\n for (let b = 0; b < breakpoints.length - 1; b++) {\n let actualWidth = 0;\n let glueCount = 0;\n\n const start = b === 0 ? breakpoints[b] : breakpoints[b] + 1;\n for (let p = start; p <= breakpoints[b + 1]; p++) {\n const item = items[p];\n if (item.type === 'box') {\n actualWidth += item.width;\n } else if (item.type === 'glue' && p !== start && p !== breakpoints[b + 1]) {\n actualWidth += item.width;\n ++glueCount;\n } else if (item.type === 'penalty' && p === breakpoints[b + 1]) {\n actualWidth += item.width;\n }\n }\n\n widths.push(actualWidth);\n glueCounts.push(glueCount);\n }\n\n return [widths, glueCounts];\n}\n\n/**\n * Mark a node as having been created by `justifyContent`.\n */\nfunction tagNode(node: Node) {\n (node as any)[NODE_TAG] = true;\n}\n\n/**\n * Return `true` if `node` was created by `justifyContent`.\n */\nfunction isTaggedNode(node: Node) {\n return node.hasOwnProperty(NODE_TAG);\n}\n\n/**\n * Return all descendants of `node` created by `justifyContent`.\n */\nfunction taggedChildren(node: Node): Node[] {\n const children = [];\n for (let i = 0; i < node.childNodes.length; i++) {\n const child = node.childNodes[i];\n if (isTaggedNode(child)) {\n children.push(child);\n }\n if (child.childNodes.length > 0) {\n children.push(...taggedChildren(child));\n }\n }\n return children;\n}\n\nfunction isTextOrInlineElement(node: Node) {\n if (node instanceof Text) {\n return true;\n } else if (node instanceof Element) {\n const style = getComputedStyle(node);\n return style.display === 'inline';\n } else {\n return false;\n }\n}\n\n/**\n * Wrap text nodes in a range and adjust the inter-word spacing.\n *\n * @param r - The range to wrap\n * @param wordSpacing - The additional spacing to add between words in pixels\n */\nfunction addWordSpacing(r: Range, wordSpacing: number) {\n // Collect all text nodes in range, skipping any non-inline elements and\n // their children because those are treated as opaque blocks by the line-\n // breaking step.\n const texts = textNodesInRange(r, isTextOrInlineElement);\n\n for (let t of texts) {\n const wrapper = document.createElement('span');\n tagNode(wrapper);\n wrapper.style.wordSpacing = `${wordSpacing}px`;\n t.parentNode!.replaceChild(wrapper, t);\n wrapper.appendChild(t);\n }\n\n return texts;\n}\n\n/**\n * Reverse the changes made to an element by `justifyContent`.\n */\nexport function unjustifyContent(el: HTMLElement) {\n // Find and remove all elements inserted by `justifyContent`.\n const tagged = taggedChildren(el);\n for (let node of tagged) {\n const parent = node.parentNode!;\n const children = Array.from(node.childNodes);\n children.forEach((child) => {\n parent.insertBefore(child, node);\n });\n parent.removeChild(node);\n }\n\n // Re-join text nodes that were split by `justifyContent`.\n el.normalize();\n}\n\ninterface ElementBreakpoints {\n el: HTMLElement;\n items: DOMItem[];\n breakpoints: number[];\n lineWidth: number;\n}\n\n/**\n * Justify an existing paragraph.\n *\n * Justify the contents of `elements`, using `hyphenateFn` to apply hyphenation if\n * necessary.\n *\n * To justify multiple paragraphs, it is more efficient to call `justifyContent`\n * once with all the elements to be processed, than to call `justifyContent`\n * separately for each element. Passing a list allows `justifyContent` to\n * optimize DOM manipulations.\n */\nexport function justifyContent(\n elements: HTMLElement | HTMLElement[],\n hyphenateFn?: (word: string) => string[],\n) {\n // To avoid layout thrashing, we batch DOM layout reads and writes in this\n // function. ie. we first measure the available width and compute linebreaks\n // for all elements and then afterwards modify all the elements.\n\n if (!Array.isArray(elements)) {\n elements = [elements];\n }\n\n // Undo the changes made by any previous justification of this content.\n elements.forEach((el) => {\n unjustifyContent(el);\n });\n\n // Calculate line-break positions given current element width and content.\n const measurer = new DOMTextMeasurer();\n const measure = measurer.measure.bind(measurer);\n\n const elementBreaks: ElementBreakpoints[] = [];\n elements.forEach((el) => {\n const lineWidth = elementLineWidth(el);\n let items: DOMItem[] = [];\n addItemsForNode(items, el, measure);\n let breakpoints;\n try {\n // First try without hyphenation but a maximum stretch-factor for each\n // space.\n breakpoints = breakLines(items, lineWidth, {\n maxAdjustmentRatio: 2.0,\n });\n } catch (e) {\n if (e instanceof MaxAdjustmentExceededError) {\n // Retry with hyphenation and unlimited stretching of each space.\n items = [];\n addItemsForNode(items, el, measure, hyphenateFn);\n breakpoints = breakLines(items, lineWidth);\n } else {\n throw e;\n }\n }\n elementBreaks.push({ el, items, breakpoints, lineWidth });\n });\n\n // Insert line-breaks and adjust inter-word spacing.\n elementBreaks.forEach(({ el, items, breakpoints, lineWidth }) => {\n const [actualWidths, glueCounts] = lineWidthsAndGlueCounts(items, breakpoints);\n\n // Create a `Range` for each line. We create the ranges before modifying the\n // contents so that node offsets in `items` are still valid at the point when\n // we create the Range.\n const endsWithHyphen: boolean[] = [];\n const lineRanges: Range[] = [];\n for (let b = 1; b < breakpoints.length; b++) {\n const prevBreakItem = items[breakpoints[b - 1]];\n const breakItem = items[breakpoints[b]];\n\n const r = document.createRange();\n if (b > 1) {\n r.setStart(prevBreakItem.node, prevBreakItem.end);\n } else {\n r.setStart(el, 0);\n }\n r.setEnd(breakItem.node, breakItem.start);\n lineRanges.push(r);\n endsWithHyphen.push(breakItem.type === 'penalty' && breakItem.flagged);\n }\n\n // Disable automatic line wrap.\n el.style.whiteSpace = 'nowrap';\n\n // Insert linebreaks.\n lineRanges.forEach((r, i) => {\n if (i === 0) {\n return;\n }\n const brEl = document.createElement('br');\n tagNode(brEl);\n\n // Insert linebreak. The browser will automatically adjust subsequent\n // ranges.\n r.insertNode(brEl);\n\n r.setStart(brEl.nextSibling!, 0);\n });\n\n // Adjust inter-word spacing on each line and add hyphenation if needed.\n lineRanges.forEach((r, i) => {\n const spaceDiff = lineWidth - actualWidths[i];\n const extraSpacePerGlue = spaceDiff / glueCounts[i];\n\n // If this is the final line and the natural spacing between words does\n // not need to be compressed, then don't try to expand the spacing to fill\n // the line.\n const isFinalLine = i === lineRanges.length - 1;\n if (isFinalLine && extraSpacePerGlue >= 0) {\n return;\n }\n\n const wrappedNodes = addWordSpacing(r, extraSpacePerGlue);\n if (endsWithHyphen[i] && wrappedNodes.length > 0) {\n const lastNode = wrappedNodes[wrappedNodes.length - 1];\n const hyphen = document.createTextNode('-');\n tagNode(hyphen);\n lastNode.parentNode!.appendChild(hyphen);\n }\n });\n });\n}\n","/**\n * @constructor\n * @param {!{patterns: !Object, leftmin: !number, rightmin: !number}} language The language pattern file. Compatible with Hyphenator.js.\n */\nfunction Hypher(language) {\n var exceptions = [],\n i = 0;\n /**\n * @type {!Hypher.TrieNode}\n */\n this.trie = this.createTrie(language['patterns']);\n\n /**\n * @type {!number}\n * @const\n */\n this.leftMin = language['leftmin'];\n\n /**\n * @type {!number}\n * @const\n */\n this.rightMin = language['rightmin'];\n\n /**\n * @type {!Object.<string, !Array.<string>>}\n */\n this.exceptions = {};\n\n if (language['exceptions']) {\n exceptions = language['exceptions'].split(/,\\s?/g);\n\n for (; i < exceptions.length; i += 1) {\n this.exceptions[exceptions[i].replace(/\\u2027/g, '').toLowerCase()] = new RegExp('(' + exceptions[i].split('\\u2027').join(')(') + ')', 'i');\n }\n }\n}\n\n/**\n * @typedef {{_points: !Array.<number>}}\n */\nHypher.TrieNode;\n\n/**\n * Creates a trie from a language pattern.\n * @private\n * @param {!Object} patternObject An object with language patterns.\n * @return {!Hypher.TrieNode} An object trie.\n */\nHypher.prototype.createTrie = function (patternObject) {\n var size = 0,\n i = 0,\n c = 0,\n p = 0,\n chars = null,\n points = null,\n codePoint = null,\n t = null,\n tree = {\n _points: []\n },\n patterns;\n\n for (size in patternObject) {\n if (patternObject.hasOwnProperty(size)) {\n patterns = patternObject[size].match(new RegExp('.{1,' + (+size) + '}', 'g'));\n\n for (i = 0; i < patterns.length; i += 1) {\n chars = patterns[i].replace(/[0-9]/g, '').split('');\n points = patterns[i].split(/\\D/);\n t = tree;\n\n for (c = 0; c < chars.length; c += 1) {\n codePoint = chars[c].charCodeAt(0);\n\n if (!t[codePoint]) {\n t[codePoint] = {};\n }\n t = t[codePoint];\n }\n\n t._points = [];\n\n for (p = 0; p < points.length; p += 1) {\n t._points[p] = points[p] || 0;\n }\n }\n }\n }\n return tree;\n};\n\n/**\n * Hyphenates a text.\n *\n * @param {!string} str The text to hyphenate.\n * @return {!string} The same text with soft hyphens inserted in the right positions.\n */\nHypher.prototype.hyphenateText = function (str, minLength) {\n minLength = minLength || 4;\n\n // Regexp(\"\\b\", \"g\") splits on word boundaries,\n // compound separators and ZWNJ so we don't need\n // any special cases for those characters. Unfortunately\n // it does not support unicode word boundaries, so\n // we implement it manually.\n var words = str.split(/([a-zA-Z0-9_\\u0027\\u00AD\\u00DF-\\u00EA\\u00EB\\u00EC-\\u00EF\\u00F1-\\u00F6\\u00F8-\\u00FD\\u0101\\u0103\\u0105\\u0107\\u0109\\u010D\\u010F\\u0111\\u0113\\u0117\\u0119\\u011B\\u011D\\u011F\\u0123\\u0125\\u012B\\u012F\\u0131\\u0135\\u0137\\u013C\\u013E\\u0142\\u0144\\u0146\\u0148\\u0151\\u0153\\u0155\\u0159\\u015B\\u015D\\u015F\\u0161\\u0165\\u016B\\u016D\\u016F\\u0171\\u0173\\u017A\\u017C\\u017E\\u017F\\u0219\\u021B\\u02BC\\u0390\\u03AC-\\u03CE\\u03F2\\u0401\\u0410-\\u044F\\u0451\\u0454\\u0456\\u0457\\u045E\\u0491\\u0531-\\u0556\\u0561-\\u0587\\u0902\\u0903\\u0905-\\u090B\\u090E-\\u0910\\u0912\\u0914-\\u0928\\u092A-\\u0939\\u093E-\\u0943\\u0946-\\u0948\\u094A-\\u094D\\u0982\\u0983\\u0985-\\u098B\\u098F\\u0990\\u0994-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BE-\\u09C3\\u09C7\\u09C8\\u09CB-\\u09CD\\u09D7\\u0A02\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A14-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A82\\u0A83\\u0A85-\\u0A8B\\u0A8F\\u0A90\\u0A94-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABE-\\u0AC3\\u0AC7\\u0AC8\\u0ACB-\\u0ACD\\u0B02\\u0B03\\u0B05-\\u0B0B\\u0B0F\\u0B10\\u0B14-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3E-\\u0B43\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B57\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB5\\u0BB7-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD7\\u0C02\\u0C03\\u0C05-\\u0C0B\\u0C0E-\\u0C10\\u0C12\\u0C14-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3E-\\u0C43\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C82\\u0C83\\u0C85-\\u0C8B\\u0C8E-\\u0C90\\u0C92\\u0C94-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBE-\\u0CC3\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0D02\\u0D03\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3E-\\u0D43\\u0D46-\\u0D48\\u0D4A-\\u0D4D\\u0D57\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u1F00-\\u1F07\\u1F10-\\u1F15\\u1F20-\\u1F27\\u1F30-\\u1F37\\u1F40-\\u1F45\\u1F50-\\u1F57\\u1F60-\\u1F67\\u1F70-\\u1F7D\\u1F80-\\u1F87\\u1F90-\\u1F97\\u1FA0-\\u1FA7\\u1FB2-\\u1FB4\\u1FB6\\u1FB7\\u1FBD\\u1FBF\\u1FC2-\\u1FC4\\u1FC6\\u1FC7\\u1FD2\\u1FD3\\u1FD6\\u1FD7\\u1FE2-\\u1FE7\\u1FF2-\\u1FF4\\u1FF6\\u1FF7\\u200D\\u2019]+)/gi);\n\n for (var i = 0; i < words.length; i += 1) {\n if (words[i].indexOf('/') !== -1) {\n // Don't insert a zero width space if the slash is at the beginning or end\n // of the text, or right after or before a space.\n if (i !== 0 && i !== words.length - 1 && !(/\\s+\\/|\\/\\s+/.test(words[i]))) {\n words[i] += '\\u200B';\n }\n } else if (words[i].length > minLength) {\n words[i] = this.hyphenate(words[i]).join('\\u00AD');\n }\n }\n return words.join('');\n};\n\n/**\n * Hyphenates a word.\n *\n * @param {!string} word The word to hyphenate\n * @return {!Array.<!string>} An array of word fragments indicating valid hyphenation points.\n */\nHypher.prototype.hyphenate = function (word) {\n var characters,\n characterPoints = [],\n originalCharacters,\n i,\n j,\n k,\n node,\n points = [],\n wordLength,\n lowerCaseWord = word.toLowerCase(),\n nodePoints,\n nodePointsLength,\n m = Math.max,\n trie = this.trie,\n result = [''];\n\n if (this.exceptions.hasOwnProperty(lowerCaseWord)) {\n return word.match(this.exceptions[lowerCaseWord]).slice(1);\n }\n\n if (word.indexOf('\\u00AD') !== -1) {\n return [word];\n }\n\n word = '_' + word + '_';\n\n characters = word.toLowerCase().split('');\n originalCharacters = word.split('');\n wordLength = characters.length;\n\n for (i = 0; i < wordLength; i += 1) {\n points[i] = 0;\n characterPoints[i] = characters[i].charCodeAt(0);\n }\n\n for (i = 0; i < wordLength; i += 1) {\n node = trie;\n for (j = i; j < wordLength; j += 1) {\n node = node[characterPoints[j]];\n\n if (node) {\n nodePoints = node._points;\n if (nodePoints) {\n for (k = 0, nodePointsLength = nodePoints.length; k < nodePointsLength; k += 1) {\n points[i + k] = m(points[i + k], nodePoints[k]);\n }\n }\n } else {\n break;\n }\n }\n }\n\n for (i = 1; i < wordLength - 1; i += 1) {\n if (i > this.leftMin && i < (wordLength - this.rightMin) && points[i] % 2) {\n result.push(originalCharacters[i]);\n } else {\n result[result.length - 1] += originalCharacters[i];\n }\n }\n\n return result;\n};\n\nmodule.exports = Hypher;\n","/*\n * MIT License\n *\n * Copyright (c) 2018 Robert Knight\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport Hypher from 'hypher';\n\nexport interface Patterns {\n id: string;\n leftmin: number;\n rightmin: number;\n patterns: {\n [key: string]: string;\n };\n}\n\n/**\n * Create a hyphenator that uses the given patterns.\n *\n * A wrapper around the `hypher` hyphenation library.\n */\nexport function createHyphenator(patterns: Patterns) {\n const hypher = new Hypher(patterns);\n return (word: string) => hypher.hyphenate(word);\n}\n"],"x_google_ignoreList":[5],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './layout';
2
+ export * from './helpers';
3
+ export * from './html';
4
+ export * from './hyphenate';