reamkit 0.1.0-alpha.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.
Files changed (162) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -0
  3. package/dist/esm/bidi/algorithm.d.ts +9 -0
  4. package/dist/esm/bidi/algorithm.js +458 -0
  5. package/dist/esm/bidi/char-types.d.ts +2 -0
  6. package/dist/esm/bidi/char-types.js +439 -0
  7. package/dist/esm/bidi/index.d.ts +14 -0
  8. package/dist/esm/bidi/index.js +25 -0
  9. package/dist/esm/converter/docx-to-pdf.d.ts +15 -0
  10. package/dist/esm/converter/docx-to-pdf.js +227 -0
  11. package/dist/esm/converter/index.d.ts +4 -0
  12. package/dist/esm/converter/index.js +2 -0
  13. package/dist/esm/converter/xlsx-to-pdf.d.ts +13 -0
  14. package/dist/esm/converter/xlsx-to-pdf.js +515 -0
  15. package/dist/esm/crypto/asn1.d.ts +23 -0
  16. package/dist/esm/crypto/asn1.js +123 -0
  17. package/dist/esm/crypto/cms.d.ts +9 -0
  18. package/dist/esm/crypto/cms.js +34 -0
  19. package/dist/esm/crypto/index.d.ts +2 -0
  20. package/dist/esm/crypto/index.js +1 -0
  21. package/dist/esm/document-model/index.d.ts +1 -0
  22. package/dist/esm/document-model/index.js +0 -0
  23. package/dist/esm/document-model/types.d.ts +403 -0
  24. package/dist/esm/font/arabic-joining.d.ts +4 -0
  25. package/dist/esm/font/arabic-joining.js +97 -0
  26. package/dist/esm/font/binary-reader.d.ts +13 -0
  27. package/dist/esm/font/binary-reader.js +44 -0
  28. package/dist/esm/font/font-registry.d.ts +19 -0
  29. package/dist/esm/font/font-registry.js +39 -0
  30. package/dist/esm/font/index.d.ts +7 -0
  31. package/dist/esm/font/opentype-layout.d.ts +21 -0
  32. package/dist/esm/font/opentype-layout.js +329 -0
  33. package/dist/esm/font/ttf-parser.d.ts +29 -0
  34. package/dist/esm/font/ttf-parser.js +290 -0
  35. package/dist/esm/font/ttf-subset.d.ts +3 -0
  36. package/dist/esm/font/ttf-subset.js +201 -0
  37. package/dist/esm/fonts/index.d.ts +2 -0
  38. package/dist/esm/fonts/remote-fonts.d.ts +13 -0
  39. package/dist/esm/fonts/remote-fonts.js +94 -0
  40. package/dist/esm/hyphenation/index.d.ts +10 -0
  41. package/dist/esm/hyphenation/index.js +38 -0
  42. package/dist/esm/hyphenation/liang.d.ts +10 -0
  43. package/dist/esm/hyphenation/liang.js +79 -0
  44. package/dist/esm/hyphenation/patterns-en-us.d.ts +2 -0
  45. package/dist/esm/hyphenation/patterns-en-us.js +5 -0
  46. package/dist/esm/hyphenation/patterns-ru.d.ts +2 -0
  47. package/dist/esm/hyphenation/patterns-ru.js +5 -0
  48. package/dist/esm/index.d.ts +12 -0
  49. package/dist/esm/index.js +13 -0
  50. package/dist/esm/line-breaker/index.d.ts +2 -0
  51. package/dist/esm/line-breaker/knuth-plass.d.ts +30 -0
  52. package/dist/esm/line-breaker/knuth-plass.js +172 -0
  53. package/dist/esm/numbering/index.d.ts +1 -0
  54. package/dist/esm/numbering/state.d.ts +6 -0
  55. package/dist/esm/numbering/state.js +83 -0
  56. package/dist/esm/ooxml/drawingml/chart-parser.d.ts +3 -0
  57. package/dist/esm/ooxml/drawingml/chart-parser.js +218 -0
  58. package/dist/esm/ooxml/drawingml/colors.d.ts +17 -0
  59. package/dist/esm/ooxml/drawingml/colors.js +125 -0
  60. package/dist/esm/ooxml/drawingml/theme-parser.d.ts +1 -0
  61. package/dist/esm/ooxml/drawingml/theme-parser.js +55 -0
  62. package/dist/esm/ooxml/index.d.ts +1 -0
  63. package/dist/esm/ooxml/math/omml-parser.d.ts +4 -0
  64. package/dist/esm/ooxml/math/omml-parser.js +217 -0
  65. package/dist/esm/ooxml/spreadsheet/cell-reference.d.ts +6 -0
  66. package/dist/esm/ooxml/spreadsheet/cell-reference.js +26 -0
  67. package/dist/esm/ooxml/spreadsheet/defined-name-ref.d.ts +11 -0
  68. package/dist/esm/ooxml/spreadsheet/defined-name-ref.js +64 -0
  69. package/dist/esm/ooxml/spreadsheet/index.d.ts +12 -0
  70. package/dist/esm/ooxml/spreadsheet/index.js +7 -0
  71. package/dist/esm/ooxml/spreadsheet/number-format.d.ts +1 -0
  72. package/dist/esm/ooxml/spreadsheet/number-format.js +438 -0
  73. package/dist/esm/ooxml/spreadsheet/shared-strings-parser.d.ts +1 -0
  74. package/dist/esm/ooxml/spreadsheet/shared-strings-parser.js +47 -0
  75. package/dist/esm/ooxml/spreadsheet/styles-parser.d.ts +52 -0
  76. package/dist/esm/ooxml/spreadsheet/styles-parser.js +225 -0
  77. package/dist/esm/ooxml/spreadsheet/workbook-parser.d.ts +16 -0
  78. package/dist/esm/ooxml/spreadsheet/workbook-parser.js +90 -0
  79. package/dist/esm/ooxml/spreadsheet/worksheet-parser.d.ts +60 -0
  80. package/dist/esm/ooxml/spreadsheet/worksheet-parser.js +311 -0
  81. package/dist/esm/ooxml/wordproc/document-parser.d.ts +9 -0
  82. package/dist/esm/ooxml/wordproc/document-parser.js +274 -0
  83. package/dist/esm/ooxml/wordproc/drawing-parser.d.ts +33 -0
  84. package/dist/esm/ooxml/wordproc/drawing-parser.js +374 -0
  85. package/dist/esm/ooxml/wordproc/font-table.d.ts +15 -0
  86. package/dist/esm/ooxml/wordproc/font-table.js +82 -0
  87. package/dist/esm/ooxml/wordproc/index.d.ts +10 -0
  88. package/dist/esm/ooxml/wordproc/index.js +9 -0
  89. package/dist/esm/ooxml/wordproc/numbering-parser.d.ts +3 -0
  90. package/dist/esm/ooxml/wordproc/numbering-parser.js +89 -0
  91. package/dist/esm/ooxml/wordproc/paragraph-properties.d.ts +2 -0
  92. package/dist/esm/ooxml/wordproc/paragraph-properties.js +85 -0
  93. package/dist/esm/ooxml/wordproc/po-helpers.d.ts +14 -0
  94. package/dist/esm/ooxml/wordproc/po-helpers.js +76 -0
  95. package/dist/esm/ooxml/wordproc/po-to-flat.d.ts +2 -0
  96. package/dist/esm/ooxml/wordproc/po-to-flat.js +28 -0
  97. package/dist/esm/ooxml/wordproc/run-properties.d.ts +2 -0
  98. package/dist/esm/ooxml/wordproc/run-properties.js +82 -0
  99. package/dist/esm/ooxml/wordproc/settings-parser.d.ts +5 -0
  100. package/dist/esm/ooxml/wordproc/settings-parser.js +26 -0
  101. package/dist/esm/ooxml/wordproc/styles-parser.d.ts +3 -0
  102. package/dist/esm/ooxml/wordproc/styles-parser.js +67 -0
  103. package/dist/esm/ooxml/wordproc/table-parser.d.ts +3 -0
  104. package/dist/esm/ooxml/wordproc/table-parser.js +162 -0
  105. package/dist/esm/ooxml/wordproc/text-extractor.d.ts +1 -0
  106. package/dist/esm/ooxml/wordproc/text-extractor.js +1 -0
  107. package/dist/esm/ooxml/wordproc/xml-helpers.d.ts +9 -0
  108. package/dist/esm/ooxml/wordproc/xml-helpers.js +36 -0
  109. package/dist/esm/opc/core-properties.d.ts +11 -0
  110. package/dist/esm/opc/core-properties.js +51 -0
  111. package/dist/esm/opc/index.d.ts +5 -0
  112. package/dist/esm/opc/index.js +3 -0
  113. package/dist/esm/opc/package.d.ts +26 -0
  114. package/dist/esm/opc/package.js +113 -0
  115. package/dist/esm/opc/relationship-types.d.ts +3 -0
  116. package/dist/esm/opc/relationship-types.js +4 -0
  117. package/dist/esm/opc/relationships.d.ts +7 -0
  118. package/dist/esm/opc/relationships.js +28 -0
  119. package/dist/esm/pdf/arc-to-bezier.d.ts +5 -0
  120. package/dist/esm/pdf/arc-to-bezier.js +58 -0
  121. package/dist/esm/pdf/builtin-fonts.d.ts +2 -0
  122. package/dist/esm/pdf/chart-geometry.d.ts +64 -0
  123. package/dist/esm/pdf/chart-geometry.js +674 -0
  124. package/dist/esm/pdf/cid-font.d.ts +15 -0
  125. package/dist/esm/pdf/cid-font.js +174 -0
  126. package/dist/esm/pdf/embedded-file.d.ts +11 -0
  127. package/dist/esm/pdf/embedded-file.js +23 -0
  128. package/dist/esm/pdf/icc-profile.d.ts +1 -0
  129. package/dist/esm/pdf/icc-profile.js +166 -0
  130. package/dist/esm/pdf/image-xobject.d.ts +13 -0
  131. package/dist/esm/pdf/image-xobject.js +335 -0
  132. package/dist/esm/pdf/index.d.ts +11 -0
  133. package/dist/esm/pdf/index.js +5 -0
  134. package/dist/esm/pdf/math-layout.d.ts +45 -0
  135. package/dist/esm/pdf/math-layout.js +1138 -0
  136. package/dist/esm/pdf/objects.d.ts +32 -0
  137. package/dist/esm/pdf/objects.js +54 -0
  138. package/dist/esm/pdf/preset-geometry.d.ts +6 -0
  139. package/dist/esm/pdf/preset-geometry.js +199 -0
  140. package/dist/esm/pdf/serialize.d.ts +3 -0
  141. package/dist/esm/pdf/serialize.js +54 -0
  142. package/dist/esm/pdf/signature.d.ts +25 -0
  143. package/dist/esm/pdf/signature.js +126 -0
  144. package/dist/esm/pdf/struct-tree.d.ts +31 -0
  145. package/dist/esm/pdf/struct-tree.js +109 -0
  146. package/dist/esm/pdf/styled-page-renderer.d.ts +43 -0
  147. package/dist/esm/pdf/styled-page-renderer.js +2317 -0
  148. package/dist/esm/pdf/text-encoding.d.ts +2 -0
  149. package/dist/esm/pdf/text-page-renderer.d.ts +16 -0
  150. package/dist/esm/pdf/text-page-renderer.js +4 -0
  151. package/dist/esm/pdf/vector-graphics.d.ts +46 -0
  152. package/dist/esm/pdf/vector-graphics.js +103 -0
  153. package/dist/esm/pdf/writer.d.ts +10 -0
  154. package/dist/esm/pdf/writer.js +81 -0
  155. package/dist/esm/pdf/xmp.d.ts +13 -0
  156. package/dist/esm/pdf/xmp.js +38 -0
  157. package/dist/esm/style-cascade/index.d.ts +3 -0
  158. package/dist/esm/style-cascade/resolver.d.ts +4 -0
  159. package/dist/esm/style-cascade/resolver.js +96 -0
  160. package/dist/esm/style-cascade/types.d.ts +29 -0
  161. package/dist/esm/style-cascade/types.js +26 -0
  162. package/package.json +90 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Krassavin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Ream
2
+
3
+ > DOCX & XLSX → PDF, from scratch — no LibreOffice, no headless Office, no commercial SDK.
4
+
5
+ Convert Word (`.docx`) and Excel (`.xlsx`) documents to PDF, **in the browser** —
6
+ implemented from the **ECMA-376** (OOXML) and **ISO 32000** (PDF) specifications,
7
+ with no wrapper around LibreOffice, headless Office, or any commercial SDK. Pure
8
+ TypeScript/JavaScript working on `Uint8Array` in and `Uint8Array` out, so it also
9
+ runs unchanged in Node.js, serverless, and edge runtimes.
10
+
11
+ ## Install
12
+
13
+ ```sh
14
+ npm install reamkit
15
+ ```
16
+
17
+ Runtime dependencies are minimal: `fflate` (ZIP/Deflate) and `fast-xml-parser`.
18
+
19
+ ## Usage
20
+
21
+ Give it the document bytes, get the PDF bytes back. No fonts to wire up — an open
22
+ substitute font (Roboto for sans, Tinos for serif, Cousine for monospace — the
23
+ same families LibreOffice substitutes) is fetched automatically based on the
24
+ document's referenced fonts:
25
+
26
+ ```ts
27
+ import { convertDocxToPdf } from 'reamkit';
28
+
29
+ // e.g. from an <input type="file"> or a fetch() — anything that yields bytes.
30
+ const docx = new Uint8Array(await file.arrayBuffer());
31
+
32
+ const pdf = await convertDocxToPdf(docx); // async — fetches a font if needed
33
+
34
+ // Hand the bytes to the browser: preview, download, upload, …
35
+ const url = URL.createObjectURL(new Blob([pdf], { type: 'application/pdf' }));
36
+ window.open(url);
37
+ ```
38
+
39
+ Excel works the same way via `convertXlsxToPdf`. The input/output are plain
40
+ `Uint8Array`s, so wiring this to files, the network, or disk is up to you.
41
+
42
+ ### Bring your own fonts (synchronous, no network)
43
+
44
+ To embed specific fonts — or to avoid the network entirely — pass the font bytes
45
+ in and use the synchronous variant:
46
+
47
+ ```ts
48
+ import { convertDocxToPdfSync } from 'reamkit';
49
+
50
+ const fonts = {
51
+ regular: new Uint8Array(await fetch('/fonts/MyFont-Regular.ttf').then((r) => r.arrayBuffer())),
52
+ bold: new Uint8Array(await fetch('/fonts/MyFont-Bold.ttf').then((r) => r.arrayBuffer())),
53
+ };
54
+ const pdf = convertDocxToPdfSync(docx, { fonts });
55
+ ```
56
+
57
+ The async `convertDocxToPdf` also accepts `fonts` (then it does no network I/O),
58
+ plus `fontFamily` to force a substitute and `fontFetch` to inject a custom
59
+ fetch implementation.
60
+
61
+ ### Options
62
+
63
+ Both converters accept (beyond `fonts` / `fontBytes`):
64
+
65
+ - `info` — PDF `/Info` metadata (`title`, `author`, `subject`, …). Also read
66
+ automatically from the document's `docProps/core.xml`.
67
+ - `pdfA: 'PDF/A-1b'` — emit an archival PDF/A-1b file (embedded sRGB
68
+ OutputIntent, XMP, deterministic `/ID`, subset-tagged fonts, no transparency).
69
+ - `hyphenator` — a Liang hyphenator (see below) for nicer justified text.
70
+
71
+ ### Hyphenation (optional)
72
+
73
+ ```ts
74
+ import { getHyphenator } from 'reamkit';
75
+ const hyphenator = await getHyphenator('en-us'); // or 'ru'
76
+ const pdf = await convertDocxToPdf(docx, { hyphenator });
77
+ ```
78
+
79
+ ### Advanced
80
+
81
+ `renderStyledPdf` drives the layout engine directly. The typed document model is
82
+ available from the `reamkit/document-model` subpath.
83
+
84
+ ## Scope
85
+
86
+ Implemented: WordprocessingML text/styles/tables/lists/sections/headers-footers/
87
+ images, SpreadsheetML grids/number-formats/dates, Type0+CIDFontType2 font
88
+ embedding with subsetting, Knuth-Plass line breaking, Liang hyphenation,
89
+ OpenType ligatures + kerning, BiDi (UAX #9), and PDF/A-1b.
90
+
91
+ See `handoff.md` for the full feature matrix and known limitations.
92
+
93
+ ## License
94
+
95
+ [MIT](./LICENSE) © Alex Krassavin
@@ -0,0 +1,9 @@
1
+ import { BidiClass } from './char-types.js';
2
+ export type Direction = 'ltr' | 'rtl' | 'auto';
3
+ export interface BidiResult {
4
+ readonly levels: ReadonlyArray<number>;
5
+ readonly paragraphLevel: number;
6
+ readonly types: ReadonlyArray<BidiClass>;
7
+ }
8
+ export declare function computeBidi(codePoints: ReadonlyArray<number>, dir: Direction): BidiResult;
9
+ export declare function reorderVisual(levels: ReadonlyArray<number>): Array<number>;
@@ -0,0 +1,458 @@
1
+ import { bidiClass } from "./char-types.js";
2
+ //#region src/bidi/algorithm.ts
3
+ var MAX_DEPTH = 125;
4
+ function matchingPDI(types, openIdx) {
5
+ let depth = 1;
6
+ for (let i = openIdx + 1; i < types.length; i++) {
7
+ const t = types[i];
8
+ if (t === "LRI" || t === "RLI" || t === "FSI") depth++;
9
+ else if (t === "PDI") {
10
+ depth--;
11
+ if (depth === 0) return i;
12
+ }
13
+ }
14
+ return types.length;
15
+ }
16
+ function computeBaseLevel(types, start, end) {
17
+ for (let i = start; i < end; i++) {
18
+ const t = types[i];
19
+ if (t === "LRI" || t === "RLI" || t === "FSI") {
20
+ i = matchingPDI(types, i);
21
+ continue;
22
+ }
23
+ if (t === "L") return 0;
24
+ if (t === "R" || t === "AL") return 1;
25
+ }
26
+ return 0;
27
+ }
28
+ function computeBidi(codePoints, dir) {
29
+ const n = codePoints.length;
30
+ const origTypes = new Array(n);
31
+ for (let i = 0; i < n; i++) origTypes[i] = bidiClass(codePoints[i]);
32
+ const paragraphLevel = dir === "ltr" ? 0 : dir === "rtl" ? 1 : computeBaseLevel(origTypes, 0, n);
33
+ const types = origTypes.slice();
34
+ const levels = new Array(n).fill(paragraphLevel);
35
+ const stack = [{
36
+ level: paragraphLevel,
37
+ override: "neutral",
38
+ isolate: false
39
+ }];
40
+ let overflowIsolate = 0;
41
+ let overflowEmbedding = 0;
42
+ let validIsolate = 0;
43
+ const nextOddLevel = (lvl) => lvl % 2 === 0 ? lvl + 1 : lvl + 2;
44
+ const nextEvenLevel = (lvl) => lvl % 2 === 0 ? lvl + 2 : lvl + 1;
45
+ const top = () => stack[stack.length - 1];
46
+ for (let i = 0; i < n; i++) {
47
+ const t = types[i];
48
+ switch (t) {
49
+ case "RLE":
50
+ case "LRE":
51
+ case "RLO":
52
+ case "LRO": {
53
+ const newLevel = t === "RLE" || t === "RLO" ? nextOddLevel(top().level) : nextEvenLevel(top().level);
54
+ levels[i] = top().level;
55
+ types[i] = "BN";
56
+ if (newLevel <= MAX_DEPTH && overflowIsolate === 0 && overflowEmbedding === 0) stack.push({
57
+ level: newLevel,
58
+ override: t === "RLO" ? "R" : t === "LRO" ? "L" : "neutral",
59
+ isolate: false
60
+ });
61
+ else if (overflowIsolate === 0) overflowEmbedding++;
62
+ break;
63
+ }
64
+ case "RLI":
65
+ case "LRI":
66
+ case "FSI": {
67
+ const ov = top().override;
68
+ levels[i] = top().level;
69
+ if (ov !== "neutral") types[i] = ov;
70
+ let isRTL = t === "RLI";
71
+ if (t === "FSI") {
72
+ const pdi = matchingPDI(types, i);
73
+ isRTL = computeBaseLevel(types, i + 1, pdi) === 1;
74
+ }
75
+ const newLevel = isRTL ? nextOddLevel(top().level) : nextEvenLevel(top().level);
76
+ if (newLevel <= MAX_DEPTH && overflowIsolate === 0 && overflowEmbedding === 0) {
77
+ validIsolate++;
78
+ stack.push({
79
+ level: newLevel,
80
+ override: "neutral",
81
+ isolate: true
82
+ });
83
+ } else overflowIsolate++;
84
+ break;
85
+ }
86
+ case "PDI": {
87
+ if (overflowIsolate > 0) overflowIsolate--;
88
+ else if (validIsolate > 0) {
89
+ overflowEmbedding = 0;
90
+ while (!top().isolate) stack.pop();
91
+ stack.pop();
92
+ validIsolate--;
93
+ }
94
+ const ov = top().override;
95
+ levels[i] = top().level;
96
+ if (ov !== "neutral") types[i] = ov;
97
+ break;
98
+ }
99
+ case "PDF":
100
+ levels[i] = top().level;
101
+ types[i] = "BN";
102
+ if (overflowIsolate > 0) {} else if (overflowEmbedding > 0) overflowEmbedding--;
103
+ else if (!top().isolate && stack.length >= 2) stack.pop();
104
+ break;
105
+ case "B":
106
+ levels[i] = paragraphLevel;
107
+ break;
108
+ default: {
109
+ const ov = top().override;
110
+ levels[i] = top().level;
111
+ if (ov !== "neutral") types[i] = ov;
112
+ break;
113
+ }
114
+ }
115
+ }
116
+ const sequences = buildIsolatingRunSequences(types, levels, paragraphLevel);
117
+ for (const seq of sequences) {
118
+ resolveWeakTypes(types, seq);
119
+ resolveNeutralTypes(types, levels, seq, codePoints);
120
+ resolveImplicitLevels(types, levels, seq);
121
+ }
122
+ applyL1(origTypes, types, levels, paragraphLevel);
123
+ return {
124
+ levels,
125
+ paragraphLevel,
126
+ types
127
+ };
128
+ }
129
+ function isRemoved(t) {
130
+ return t === "BN" || t === "RLE" || t === "LRE" || t === "RLO" || t === "LRO" || t === "PDF";
131
+ }
132
+ function buildIsolatingRunSequences(types, levels, paragraphLevel) {
133
+ const n = types.length;
134
+ const runs = [];
135
+ let current = [];
136
+ let currentLevel = -1;
137
+ for (let i = 0; i < n; i++) {
138
+ if (isRemoved(types[i])) continue;
139
+ if (current.length === 0) {
140
+ current = [i];
141
+ currentLevel = levels[i];
142
+ } else if (levels[i] === currentLevel) current.push(i);
143
+ else {
144
+ runs.push(current);
145
+ current = [i];
146
+ currentLevel = levels[i];
147
+ }
148
+ }
149
+ if (current.length > 0) runs.push(current);
150
+ const runByFirst = /* @__PURE__ */ new Map();
151
+ for (let r = 0; r < runs.length; r++) runByFirst.set(runs[r][0], r);
152
+ const used = new Array(runs.length).fill(false);
153
+ const sequences = [];
154
+ for (let r = 0; r < runs.length; r++) {
155
+ if (used[r]) continue;
156
+ const firstIdx = runs[r][0];
157
+ if (types[firstIdx] === "PDI" && matchesAnInitiator(types, firstIdx)) continue;
158
+ const seqIndices = [];
159
+ let cur = r;
160
+ for (;;) {
161
+ used[cur] = true;
162
+ seqIndices.push(...runs[cur]);
163
+ const lastIdx = runs[cur][runs[cur].length - 1];
164
+ const lastType = types[lastIdx];
165
+ if (lastType === "LRI" || lastType === "RLI" || lastType === "FSI") {
166
+ const pdi = matchingPDI(types, lastIdx);
167
+ if (pdi < n) {
168
+ const nextRun = runByFirst.get(pdi);
169
+ if (nextRun !== void 0 && !used[nextRun]) {
170
+ cur = nextRun;
171
+ continue;
172
+ }
173
+ }
174
+ }
175
+ break;
176
+ }
177
+ const seqLevel = levels[seqIndices[0]];
178
+ const prevLevel = levelBefore(types, levels, seqIndices[0], paragraphLevel);
179
+ const lastSeqIdx = seqIndices[seqIndices.length - 1];
180
+ const lastType = types[lastSeqIdx];
181
+ let nextLevel;
182
+ if (lastType === "LRI" || lastType === "RLI" || lastType === "FSI") nextLevel = paragraphLevel;
183
+ else nextLevel = levelAfter(types, levels, lastSeqIdx, paragraphLevel);
184
+ const sos = Math.max(seqLevel, prevLevel) % 2 === 0 ? "L" : "R";
185
+ const eos = Math.max(seqLevel, nextLevel) % 2 === 0 ? "L" : "R";
186
+ sequences.push({
187
+ indices: seqIndices,
188
+ sos,
189
+ eos
190
+ });
191
+ }
192
+ return sequences;
193
+ }
194
+ function matchesAnInitiator(types, pdiIdx) {
195
+ let depth = 1;
196
+ for (let i = pdiIdx - 1; i >= 0; i--) {
197
+ const t = types[i];
198
+ if (t === "PDI") depth++;
199
+ else if (t === "LRI" || t === "RLI" || t === "FSI") {
200
+ depth--;
201
+ if (depth === 0) return true;
202
+ }
203
+ }
204
+ return false;
205
+ }
206
+ function levelBefore(types, levels, idx, paragraphLevel) {
207
+ for (let i = idx - 1; i >= 0; i--) if (!isRemoved(types[i])) return levels[i];
208
+ return paragraphLevel;
209
+ }
210
+ function levelAfter(types, levels, idx, paragraphLevel) {
211
+ for (let i = idx + 1; i < types.length; i++) if (!isRemoved(types[i])) return levels[i];
212
+ return paragraphLevel;
213
+ }
214
+ function resolveWeakTypes(types, seq) {
215
+ const idx = seq.indices;
216
+ const isIsolateType = (t) => t === "LRI" || t === "RLI" || t === "FSI" || t === "PDI";
217
+ let prevType = seq.sos;
218
+ for (const i of idx) {
219
+ if (types[i] === "NSM") types[i] = isIsolateType(prevType) ? "ON" : prevType;
220
+ prevType = types[i];
221
+ }
222
+ let lastStrong = seq.sos;
223
+ for (const i of idx) {
224
+ const t = types[i];
225
+ if (t === "R" || t === "L" || t === "AL") lastStrong = t;
226
+ else if (t === "EN" && lastStrong === "AL") types[i] = "AN";
227
+ }
228
+ for (const i of idx) if (types[i] === "AL") types[i] = "R";
229
+ for (let k = 1; k < idx.length - 1; k++) {
230
+ const i = idx[k];
231
+ const t = types[i];
232
+ const prev = types[idx[k - 1]];
233
+ const next = types[idx[k + 1]];
234
+ if (t === "ES" && prev === "EN" && next === "EN") types[i] = "EN";
235
+ else if (t === "CS" && prev === "EN" && next === "EN") types[i] = "EN";
236
+ else if (t === "CS" && prev === "AN" && next === "AN") types[i] = "AN";
237
+ }
238
+ for (let k = 0; k < idx.length; k++) {
239
+ if (types[idx[k]] !== "ET") continue;
240
+ let runEnd = k;
241
+ while (runEnd < idx.length && types[idx[runEnd]] === "ET") runEnd++;
242
+ const before = k > 0 ? types[idx[k - 1]] : seq.sos;
243
+ const after = runEnd < idx.length ? types[idx[runEnd]] : seq.eos;
244
+ if (before === "EN" || after === "EN") for (let j = k; j < runEnd; j++) types[idx[j]] = "EN";
245
+ k = runEnd - 1;
246
+ }
247
+ for (const i of idx) {
248
+ const t = types[i];
249
+ if (t === "ES" || t === "ET" || t === "CS") types[i] = "ON";
250
+ }
251
+ lastStrong = seq.sos;
252
+ for (const i of idx) {
253
+ const t = types[i];
254
+ if (t === "R" || t === "L") lastStrong = t;
255
+ else if (t === "EN" && lastStrong === "L") types[i] = "L";
256
+ }
257
+ }
258
+ var BRACKET_OPEN_TO_CLOSE = new Map([
259
+ [40, 41],
260
+ [91, 93],
261
+ [123, 125],
262
+ [8261, 8262],
263
+ [8317, 8318],
264
+ [8333, 8334],
265
+ [8968, 8969],
266
+ [8970, 8971],
267
+ [9001, 9002],
268
+ [10214, 10215],
269
+ [10216, 10217],
270
+ [10218, 10219],
271
+ [10220, 10221],
272
+ [10222, 10223],
273
+ [10627, 10628],
274
+ [10629, 10630],
275
+ [10631, 10632],
276
+ [10633, 10634],
277
+ [10635, 10636],
278
+ [10637, 10638],
279
+ [10639, 10640],
280
+ [10641, 10642],
281
+ [10643, 10644],
282
+ [10645, 10646],
283
+ [10647, 10648],
284
+ [12296, 12297],
285
+ [12298, 12299],
286
+ [12300, 12301],
287
+ [12302, 12303],
288
+ [12304, 12305],
289
+ [12308, 12309],
290
+ [12310, 12311],
291
+ [12312, 12313],
292
+ [12314, 12315],
293
+ [65288, 65289],
294
+ [65339, 65341],
295
+ [65371, 65373],
296
+ [65375, 65376],
297
+ [65378, 65379]
298
+ ]);
299
+ function canonBracket(cp) {
300
+ if (cp === 9001) return 12296;
301
+ if (cp === 9002) return 12297;
302
+ return cp;
303
+ }
304
+ var CLOSE_BRACKETS = /* @__PURE__ */ new Set();
305
+ for (const c of BRACKET_OPEN_TO_CLOSE.values()) CLOSE_BRACKETS.add(canonBracket(c));
306
+ function resolvePairedBrackets(types, levels, seq, codePoints) {
307
+ const idx = seq.indices;
308
+ if (idx.length === 0) return;
309
+ const e = levels[idx[0]] % 2 === 0 ? "L" : "R";
310
+ const o = e === "L" ? "R" : "L";
311
+ const stack = [];
312
+ const pairs = [];
313
+ for (let p = 0; p < idx.length; p++) {
314
+ if (types[idx[p]] !== "ON") continue;
315
+ const cp = canonBracket(codePoints[idx[p]]);
316
+ const closing = BRACKET_OPEN_TO_CLOSE.get(cp);
317
+ if (closing !== void 0) {
318
+ if (stack.length === 63) break;
319
+ stack.push({
320
+ close: canonBracket(closing),
321
+ pos: p
322
+ });
323
+ } else if (CLOSE_BRACKETS.has(cp)) {
324
+ for (let s = stack.length - 1; s >= 0; s--) if (stack[s].close === cp) {
325
+ pairs.push({
326
+ open: stack[s].pos,
327
+ close: p
328
+ });
329
+ stack.length = s;
330
+ break;
331
+ }
332
+ }
333
+ }
334
+ pairs.sort((a, b) => a.open - b.open);
335
+ for (const { open, close } of pairs) {
336
+ let foundE = false;
337
+ let foundO = false;
338
+ for (let p = open + 1; p < close; p++) {
339
+ const d = directionOf(types[idx[p]]);
340
+ if (d === e) {
341
+ foundE = true;
342
+ break;
343
+ }
344
+ if (d === o) foundO = true;
345
+ }
346
+ let setDir = null;
347
+ if (foundE) setDir = e;
348
+ else if (foundO) {
349
+ let prev = "other";
350
+ for (let p = open - 1; p >= 0; p--) {
351
+ const d = directionOf(types[idx[p]]);
352
+ if (d !== "other") {
353
+ prev = d;
354
+ break;
355
+ }
356
+ }
357
+ if (prev === "other") prev = directionOf(seq.sos);
358
+ setDir = prev === o ? o : e;
359
+ }
360
+ if (setDir) {
361
+ types[idx[open]] = setDir;
362
+ types[idx[close]] = setDir;
363
+ }
364
+ }
365
+ }
366
+ function resolveNeutralTypes(types, levels, seq, codePoints) {
367
+ const idx = seq.indices;
368
+ const isNeutralOrIsolate = (t) => t === "B" || t === "S" || t === "WS" || t === "ON" || t === "LRI" || t === "RLI" || t === "FSI" || t === "PDI";
369
+ resolvePairedBrackets(types, levels, seq, codePoints);
370
+ let k = 0;
371
+ while (k < idx.length) {
372
+ if (!isNeutralOrIsolate(types[idx[k]])) {
373
+ k++;
374
+ continue;
375
+ }
376
+ let runEnd = k;
377
+ while (runEnd < idx.length && isNeutralOrIsolate(types[idx[runEnd]])) runEnd++;
378
+ const leftDir = directionOf(k > 0 ? types[idx[k - 1]] : seq.sos);
379
+ const rightDir = directionOf(runEnd < idx.length ? types[idx[runEnd]] : seq.eos);
380
+ let resolved;
381
+ if (leftDir === rightDir && (leftDir === "L" || leftDir === "R")) resolved = leftDir;
382
+ else resolved = levels[idx[k]] % 2 === 0 ? "L" : "R";
383
+ for (let j = k; j < runEnd; j++) types[idx[j]] = resolved;
384
+ k = runEnd;
385
+ }
386
+ }
387
+ function directionOf(t) {
388
+ if (t === "L") return "L";
389
+ if (t === "R" || t === "EN" || t === "AN") return "R";
390
+ return "other";
391
+ }
392
+ function resolveImplicitLevels(types, levels, seq) {
393
+ for (const i of seq.indices) {
394
+ const t = types[i];
395
+ const lvl = levels[i];
396
+ if (lvl % 2 === 0) {
397
+ if (t === "R") levels[i] = lvl + 1;
398
+ else if (t === "AN" || t === "EN") levels[i] = lvl + 2;
399
+ } else if (t === "L" || t === "EN" || t === "AN") levels[i] = lvl + 1;
400
+ }
401
+ }
402
+ function applyL1(origTypes, types, levels, paragraphLevel) {
403
+ const n = levels.length;
404
+ const isResettableWS = (t) => t === "WS" || t === "LRI" || t === "RLI" || t === "FSI" || t === "PDI" || isRemoved(t);
405
+ let i = 0;
406
+ while (i < n) {
407
+ const ot = origTypes[i];
408
+ if (ot === "B" || ot === "S") {
409
+ levels[i] = paragraphLevel;
410
+ let j = i - 1;
411
+ while (j >= 0 && isResettableWS(origTypes[j])) {
412
+ levels[j] = paragraphLevel;
413
+ j--;
414
+ }
415
+ }
416
+ i++;
417
+ }
418
+ let j = n - 1;
419
+ while (j >= 0 && isResettableWS(origTypes[j])) {
420
+ levels[j] = paragraphLevel;
421
+ j--;
422
+ }
423
+ }
424
+ function reorderVisual(levels) {
425
+ const n = levels.length;
426
+ const order = Array.from({ length: n }, (_, i) => i);
427
+ if (n === 0) return order;
428
+ let highest = 0;
429
+ let lowestOdd = MAX_DEPTH + 1;
430
+ for (const lvl of levels) {
431
+ if (lvl > highest) highest = lvl;
432
+ if (lvl % 2 === 1 && lvl < lowestOdd) lowestOdd = lvl;
433
+ }
434
+ for (let level = highest; level >= lowestOdd; level--) {
435
+ let k = 0;
436
+ while (k < n) {
437
+ if (levels[order[k]] < level) {
438
+ k++;
439
+ continue;
440
+ }
441
+ let runEnd = k;
442
+ while (runEnd < n && levels[order[runEnd]] >= level) runEnd++;
443
+ let a = k;
444
+ let b = runEnd - 1;
445
+ while (a < b) {
446
+ const tmp = order[a];
447
+ order[a] = order[b];
448
+ order[b] = tmp;
449
+ a++;
450
+ b--;
451
+ }
452
+ k = runEnd;
453
+ }
454
+ }
455
+ return order;
456
+ }
457
+ //#endregion
458
+ export { computeBidi, reorderVisual };
@@ -0,0 +1,2 @@
1
+ export type BidiClass = 'L' | 'R' | 'AL' | 'EN' | 'ES' | 'ET' | 'AN' | 'CS' | 'NSM' | 'BN' | 'B' | 'S' | 'WS' | 'ON' | 'LRE' | 'LRO' | 'RLE' | 'RLO' | 'PDF' | 'LRI' | 'RLI' | 'FSI' | 'PDI';
2
+ export declare function bidiClass(cp: number): BidiClass;