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
@@ -0,0 +1,21 @@
1
+ export type LigatureMap = ReadonlyMap<string, number>;
2
+ export type KerningMap = ReadonlyMap<string, number>;
3
+ export interface ArabicJoiningForms {
4
+ readonly init: ReadonlyMap<number, number>;
5
+ readonly medi: ReadonlyMap<number, number>;
6
+ readonly fina: ReadonlyMap<number, number>;
7
+ }
8
+ export declare const EMPTY_JOINING_FORMS: ArabicJoiningForms;
9
+ interface TableInfo {
10
+ readonly offset: number;
11
+ readonly length: number;
12
+ }
13
+ export declare function parseGsubLigatures(raw: Uint8Array, table: TableInfo | undefined): LigatureMap;
14
+ export declare function parseGposKerning(raw: Uint8Array, table: TableInfo | undefined): KerningMap;
15
+ export declare function parseGsubArabicForms(raw: Uint8Array, table: TableInfo | undefined): ArabicJoiningForms;
16
+ export interface ShapedRun {
17
+ readonly gids: Array<number>;
18
+ readonly advances: Array<number>;
19
+ }
20
+ export declare function shapeText(text: string, glyphForCodepoint: (cp: number) => number, advanceWidths: ReadonlyArray<number>, ligatures: LigatureMap, kerning: KerningMap, joiningForms?: ArabicJoiningForms): ShapedRun;
21
+ export {};
@@ -0,0 +1,329 @@
1
+ import { BigEndianReader } from "./binary-reader.js";
2
+ import { assignArabicForms } from "./arabic-joining.js";
3
+ //#region src/font/opentype-layout.ts
4
+ var EMPTY_JOINING_FORMS = {
5
+ init: /* @__PURE__ */ new Map(),
6
+ medi: /* @__PURE__ */ new Map(),
7
+ fina: /* @__PURE__ */ new Map()
8
+ };
9
+ function parseGsubLigatures(raw, table) {
10
+ if (!table) return /* @__PURE__ */ new Map();
11
+ try {
12
+ const lookupOffsets = collectLookupOffsets(raw, table, ["liga", "clig"]);
13
+ const ligatures = /* @__PURE__ */ new Map();
14
+ for (const lookupOffset of lookupOffsets) readLookup(raw, lookupOffset, (lookupType, subtableOffset) => {
15
+ if (lookupType === 4) parseLigatureSubtable(raw, subtableOffset, ligatures);
16
+ });
17
+ return ligatures;
18
+ } catch {
19
+ return /* @__PURE__ */ new Map();
20
+ }
21
+ }
22
+ function parseGposKerning(raw, table) {
23
+ if (!table) return /* @__PURE__ */ new Map();
24
+ try {
25
+ const lookupOffsets = collectLookupOffsets(raw, table, ["kern"]);
26
+ const kerning = /* @__PURE__ */ new Map();
27
+ for (const lookupOffset of lookupOffsets) readLookup(raw, lookupOffset, (lookupType, subtableOffset) => {
28
+ if (lookupType === 2) parsePairPosSubtable(raw, subtableOffset, kerning);
29
+ });
30
+ return kerning;
31
+ } catch {
32
+ return /* @__PURE__ */ new Map();
33
+ }
34
+ }
35
+ function parseGsubArabicForms(raw, table) {
36
+ if (!table) return EMPTY_JOINING_FORMS;
37
+ try {
38
+ const forFeature = (tag) => {
39
+ const map = /* @__PURE__ */ new Map();
40
+ for (const lookupOffset of collectLookupOffsets(raw, table, [tag])) readLookup(raw, lookupOffset, (lookupType, subtableOffset) => {
41
+ if (lookupType === 1) parseSingleSubstSubtable(raw, subtableOffset, map);
42
+ });
43
+ return map;
44
+ };
45
+ const forms = {
46
+ init: forFeature("init"),
47
+ medi: forFeature("medi"),
48
+ fina: forFeature("fina")
49
+ };
50
+ if (forms.init.size === 0 && forms.medi.size === 0 && forms.fina.size === 0) return EMPTY_JOINING_FORMS;
51
+ return forms;
52
+ } catch {
53
+ return EMPTY_JOINING_FORMS;
54
+ }
55
+ }
56
+ function parseSingleSubstSubtable(raw, subtableOffset, map) {
57
+ const r = new BigEndianReader(raw, subtableOffset);
58
+ const format = r.u16();
59
+ const coverage = readCoverage(raw, subtableOffset + r.u16());
60
+ if (format === 1) {
61
+ const raw16 = r.u16();
62
+ const delta = raw16 >= 32768 ? raw16 - 65536 : raw16;
63
+ for (const gid of coverage) map.set(gid, gid + delta & 65535);
64
+ } else if (format === 2) {
65
+ const count = r.u16();
66
+ for (let i = 0; i < count; i++) {
67
+ const sub = r.u16();
68
+ if (i < coverage.length) map.set(coverage[i], sub);
69
+ }
70
+ }
71
+ }
72
+ function collectLookupOffsets(raw, table, wantedFeatures) {
73
+ const base = table.offset;
74
+ const r = new BigEndianReader(raw, base);
75
+ r.u16();
76
+ r.u16();
77
+ r.u16();
78
+ const featureListOff = r.u16();
79
+ const lookupListOff = r.u16();
80
+ const featureListBase = base + featureListOff;
81
+ const featureWanted = /* @__PURE__ */ new Set();
82
+ {
83
+ const fr = new BigEndianReader(raw, featureListBase);
84
+ const featureCount = fr.u16();
85
+ for (let i = 0; i < featureCount; i++) {
86
+ const tag = fr.tag();
87
+ const featureOff = fr.u16();
88
+ if (wantedFeatures.includes(tag)) {
89
+ const fr2 = new BigEndianReader(raw, featureListBase + featureOff);
90
+ fr2.u16();
91
+ const lookupCount = fr2.u16();
92
+ for (let k = 0; k < lookupCount; k++) featureWanted.add(fr2.u16());
93
+ }
94
+ }
95
+ }
96
+ const lookupListBase = base + lookupListOff;
97
+ const lookupListReader = new BigEndianReader(raw, lookupListBase);
98
+ const lookupCount = lookupListReader.u16();
99
+ const lookupOffsets = [];
100
+ for (let i = 0; i < lookupCount; i++) {
101
+ const offset = lookupListReader.u16();
102
+ lookupOffsets.push(lookupListBase + offset);
103
+ }
104
+ const result = [];
105
+ const seen = /* @__PURE__ */ new Set();
106
+ for (const idx of featureWanted) {
107
+ const off = lookupOffsets[idx];
108
+ if (off !== void 0 && !seen.has(off)) {
109
+ seen.add(off);
110
+ result.push(off);
111
+ }
112
+ }
113
+ return result;
114
+ }
115
+ function readLookup(raw, lookupOffset, visit) {
116
+ const r = new BigEndianReader(raw, lookupOffset);
117
+ const lookupType = r.u16();
118
+ r.u16();
119
+ const subTableCount = r.u16();
120
+ for (let i = 0; i < subTableCount; i++) visit(lookupType, lookupOffset + r.u16());
121
+ }
122
+ function parseLigatureSubtable(raw, subtableOffset, out) {
123
+ const r = new BigEndianReader(raw, subtableOffset);
124
+ if (r.u16() !== 1) return;
125
+ const coverageOff = r.u16();
126
+ const ligatureSetCount = r.u16();
127
+ const ligatureSetOffs = new Array(ligatureSetCount);
128
+ for (let i = 0; i < ligatureSetCount; i++) ligatureSetOffs[i] = r.u16();
129
+ const coverageGlyphs = readCoverage(raw, subtableOffset + coverageOff);
130
+ for (let i = 0; i < ligatureSetCount; i++) {
131
+ const firstGlyph = coverageGlyphs[i];
132
+ if (firstGlyph === void 0) continue;
133
+ parseLigatureSet(raw, subtableOffset + ligatureSetOffs[i], firstGlyph, out);
134
+ }
135
+ }
136
+ function parseLigatureSet(raw, setOffset, firstGlyph, out) {
137
+ const r = new BigEndianReader(raw, setOffset);
138
+ const ligatureCount = r.u16();
139
+ const ligOffsets = new Array(ligatureCount);
140
+ for (let i = 0; i < ligatureCount; i++) ligOffsets[i] = r.u16();
141
+ for (let i = 0; i < ligatureCount; i++) {
142
+ const r2 = new BigEndianReader(raw, setOffset + ligOffsets[i]);
143
+ const ligGlyph = r2.u16();
144
+ const componentCount = r2.u16();
145
+ const components = [firstGlyph];
146
+ for (let k = 1; k < componentCount; k++) components.push(r2.u16());
147
+ out.set(components.join(","), ligGlyph);
148
+ }
149
+ }
150
+ function parsePairPosSubtable(raw, subtableOffset, out) {
151
+ const posFormat = new BigEndianReader(raw, subtableOffset).u16();
152
+ if (posFormat === 1) parsePairPosFormat1(raw, subtableOffset, out);
153
+ else if (posFormat === 2) parsePairPosFormat2(raw, subtableOffset, out);
154
+ }
155
+ function parsePairPosFormat1(raw, subtableOffset, out) {
156
+ const r = new BigEndianReader(raw, subtableOffset);
157
+ r.u16();
158
+ const coverageOff = r.u16();
159
+ const valueFormat1 = r.u16();
160
+ const valueFormat2 = r.u16();
161
+ const pairSetCount = r.u16();
162
+ const pairSetOffs = new Array(pairSetCount);
163
+ for (let i = 0; i < pairSetCount; i++) pairSetOffs[i] = r.u16();
164
+ const value1Size = valueRecordSize(valueFormat1);
165
+ const value2Size = valueRecordSize(valueFormat2);
166
+ const coverage = readCoverage(raw, subtableOffset + coverageOff);
167
+ for (let i = 0; i < pairSetCount; i++) {
168
+ const first = coverage[i];
169
+ if (first === void 0) continue;
170
+ const psr = new BigEndianReader(raw, subtableOffset + pairSetOffs[i]);
171
+ const pairCount = psr.u16();
172
+ for (let j = 0; j < pairCount; j++) {
173
+ const second = psr.u16();
174
+ const v1 = readValueRecordAdvance(psr, valueFormat1);
175
+ psr.skip(value1Size - bytesRead(valueFormat1));
176
+ const v2 = readValueRecordAdvance(psr, valueFormat2);
177
+ psr.skip(value2Size - bytesRead(valueFormat2));
178
+ const adj = v1 !== 0 ? v1 : v2;
179
+ if (adj !== 0) out.set(`${first},${second}`, adj);
180
+ }
181
+ }
182
+ }
183
+ function parsePairPosFormat2(raw, subtableOffset, out) {
184
+ const r = new BigEndianReader(raw, subtableOffset);
185
+ r.u16();
186
+ const coverageOff = r.u16();
187
+ const valueFormat1 = r.u16();
188
+ const valueFormat2 = r.u16();
189
+ const classDef1Off = r.u16();
190
+ const classDef2Off = r.u16();
191
+ const class1Count = r.u16();
192
+ const class2Count = r.u16();
193
+ const value1Size = valueRecordSize(valueFormat1);
194
+ const value2Size = valueRecordSize(valueFormat2);
195
+ const pairRecordSize = value1Size + value2Size;
196
+ const coverage = readCoverage(raw, subtableOffset + coverageOff);
197
+ const class1 = readClassDef(raw, subtableOffset + classDef1Off);
198
+ const class2 = readClassDef(raw, subtableOffset + classDef2Off);
199
+ const recordsBase = r.offset;
200
+ for (let i = 0; i < class1Count; i++) for (let j = 0; j < class2Count; j++) {
201
+ const pr = new BigEndianReader(raw, recordsBase + (i * class2Count + j) * pairRecordSize);
202
+ const v1 = readValueRecordAdvance(pr, valueFormat1);
203
+ pr.skip(value1Size - bytesRead(valueFormat1));
204
+ const v2 = readValueRecordAdvance(pr, valueFormat2);
205
+ pr.skip(value2Size - bytesRead(valueFormat2));
206
+ const adj = v1 !== 0 ? v1 : v2;
207
+ if (adj === 0) continue;
208
+ const firsts = matchingGlyphs(coverage, class1, i);
209
+ const seconds = matchingGlyphs(void 0, class2, j);
210
+ for (const a of firsts) for (const b of seconds) out.set(`${a},${b}`, adj);
211
+ }
212
+ }
213
+ function valueRecordSize(valueFormat) {
214
+ let bits = valueFormat;
215
+ let count = 0;
216
+ while (bits) {
217
+ count += bits & 1;
218
+ bits >>>= 1;
219
+ }
220
+ return count * 2;
221
+ }
222
+ function readValueRecordAdvance(r, valueFormat) {
223
+ if (valueFormat & 1) r.i16();
224
+ if (valueFormat & 2) r.i16();
225
+ if (valueFormat & 4) return r.i16();
226
+ return 0;
227
+ }
228
+ function bytesRead(valueFormat) {
229
+ let n = 0;
230
+ if (valueFormat & 1) n += 2;
231
+ if (valueFormat & 2) n += 2;
232
+ if (valueFormat & 4) n += 2;
233
+ return n;
234
+ }
235
+ function readCoverage(raw, offset) {
236
+ const r = new BigEndianReader(raw, offset);
237
+ const format = r.u16();
238
+ if (format === 1) {
239
+ const glyphCount = r.u16();
240
+ const out = new Array(glyphCount);
241
+ for (let i = 0; i < glyphCount; i++) out[i] = r.u16();
242
+ return out;
243
+ }
244
+ if (format === 2) {
245
+ const rangeCount = r.u16();
246
+ const out = [];
247
+ for (let i = 0; i < rangeCount; i++) {
248
+ const startGlyph = r.u16();
249
+ const endGlyph = r.u16();
250
+ r.u16();
251
+ for (let g = startGlyph; g <= endGlyph; g++) out.push(g);
252
+ }
253
+ return out;
254
+ }
255
+ return [];
256
+ }
257
+ function readClassDef(raw, offset) {
258
+ const r = new BigEndianReader(raw, offset);
259
+ const format = r.u16();
260
+ const out = /* @__PURE__ */ new Map();
261
+ if (format === 1) {
262
+ const startGlyph = r.u16();
263
+ const glyphCount = r.u16();
264
+ for (let i = 0; i < glyphCount; i++) {
265
+ const cls = r.u16();
266
+ if (cls !== 0) out.set(startGlyph + i, cls);
267
+ }
268
+ } else if (format === 2) {
269
+ const classRangeCount = r.u16();
270
+ for (let i = 0; i < classRangeCount; i++) {
271
+ const startGlyph = r.u16();
272
+ const endGlyph = r.u16();
273
+ const cls = r.u16();
274
+ if (cls !== 0) for (let g = startGlyph; g <= endGlyph; g++) out.set(g, cls);
275
+ }
276
+ }
277
+ return out;
278
+ }
279
+ function matchingGlyphs(coverage, classMap, cls) {
280
+ if (coverage) {
281
+ const out = [];
282
+ for (const g of coverage) if ((classMap.get(g) ?? 0) === cls) out.push(g);
283
+ return out;
284
+ }
285
+ const out = [];
286
+ for (const [gid, c] of classMap) if (c === cls) out.push(gid);
287
+ return out;
288
+ }
289
+ function shapeText(text, glyphForCodepoint, advanceWidths, ligatures, kerning, joiningForms = EMPTY_JOINING_FORMS) {
290
+ const cps = [];
291
+ for (const ch of text) cps.push(ch.codePointAt(0));
292
+ const initialGids = cps.map(glyphForCodepoint);
293
+ if (joiningForms.init.size + joiningForms.medi.size + joiningForms.fina.size > 0) {
294
+ const forms = assignArabicForms(cps);
295
+ for (let k = 0; k < initialGids.length; k++) {
296
+ const sub = (forms[k] === "init" ? joiningForms.init : forms[k] === "medi" ? joiningForms.medi : forms[k] === "fina" ? joiningForms.fina : void 0)?.get(initialGids[k]);
297
+ if (sub !== void 0) initialGids[k] = sub;
298
+ }
299
+ }
300
+ const ligated = [];
301
+ let i = 0;
302
+ while (i < initialGids.length) {
303
+ let bestLen = 1;
304
+ let bestOut = initialGids[i];
305
+ const maxTry = Math.min(4, initialGids.length - i);
306
+ for (let len = 2; len <= maxTry; len++) {
307
+ const key = initialGids.slice(i, i + len).join(",");
308
+ const out = ligatures.get(key);
309
+ if (out !== void 0 && len > bestLen) {
310
+ bestLen = len;
311
+ bestOut = out;
312
+ }
313
+ }
314
+ ligated.push(bestOut);
315
+ i += bestLen;
316
+ }
317
+ const advances = new Array(ligated.length);
318
+ for (let j = 0; j < ligated.length; j++) advances[j] = advanceWidths[ligated[j]] ?? 0;
319
+ for (let j = 1; j < ligated.length; j++) {
320
+ const adj = kerning.get(`${ligated[j - 1]},${ligated[j]}`);
321
+ if (adj !== void 0 && adj !== 0) advances[j - 1] += adj;
322
+ }
323
+ return {
324
+ gids: ligated,
325
+ advances
326
+ };
327
+ }
328
+ //#endregion
329
+ export { parseGposKerning, parseGsubArabicForms, parseGsubLigatures, shapeText };
@@ -0,0 +1,29 @@
1
+ import { ArabicJoiningForms, KerningMap, LigatureMap } from './opentype-layout.js';
2
+ export interface TtfTableInfo {
3
+ readonly offset: number;
4
+ readonly length: number;
5
+ }
6
+ export interface ParsedTtf {
7
+ readonly raw: Uint8Array;
8
+ readonly sfntVersion: number;
9
+ readonly tables: ReadonlyMap<string, TtfTableInfo>;
10
+ readonly postScriptName: string;
11
+ readonly unitsPerEm: number;
12
+ readonly fontBBox: readonly [number, number, number, number];
13
+ readonly ascender: number;
14
+ readonly descender: number;
15
+ readonly capHeight: number;
16
+ readonly xHeight: number;
17
+ readonly italicAngle: number;
18
+ readonly stemV: number;
19
+ readonly flags: number;
20
+ readonly numGlyphs: number;
21
+ readonly indexToLocFormat: 0 | 1;
22
+ readonly glyphOffsets: ReadonlyArray<number>;
23
+ readonly advanceWidths: ReadonlyArray<number>;
24
+ readonly glyphForCodepoint: (cp: number) => number;
25
+ readonly ligatures: LigatureMap;
26
+ readonly kerning: KerningMap;
27
+ readonly joiningForms: ArabicJoiningForms;
28
+ }
29
+ export declare function parseTtf(raw: Uint8Array): ParsedTtf;
@@ -0,0 +1,290 @@
1
+ import { BigEndianReader } from "./binary-reader.js";
2
+ import { parseGposKerning, parseGsubArabicForms, parseGsubLigatures } from "./opentype-layout.js";
3
+ //#region src/font/ttf-parser.ts
4
+ var SFNT_TRUETYPE = 65536;
5
+ var SFNT_OPENTYPE_CFF = 1330926671;
6
+ function parseTtf(raw) {
7
+ const r = new BigEndianReader(raw);
8
+ const sfntVersion = r.u32();
9
+ if (sfntVersion !== SFNT_TRUETYPE && sfntVersion !== SFNT_OPENTYPE_CFF) throw new Error(`Not a TrueType or OpenType font (sfntVersion=0x${sfntVersion.toString(16).padStart(8, "0")})`);
10
+ const numTables = r.u16();
11
+ r.skip(6);
12
+ const tables = /* @__PURE__ */ new Map();
13
+ for (let i = 0; i < numTables; i++) {
14
+ const tag = r.tag();
15
+ r.u32();
16
+ const offset = r.u32();
17
+ const length = r.u32();
18
+ tables.set(tag, {
19
+ offset,
20
+ length
21
+ });
22
+ }
23
+ const require = (tag) => {
24
+ const t = tables.get(tag);
25
+ if (!t) throw new Error(`TTF missing required table: ${tag}`);
26
+ return t;
27
+ };
28
+ const head = require("head");
29
+ r.seek(head.offset + 18);
30
+ const unitsPerEm = r.u16();
31
+ r.skip(16);
32
+ const xMin = r.i16();
33
+ const yMin = r.i16();
34
+ const xMax = r.i16();
35
+ const yMax = r.i16();
36
+ r.skip(6);
37
+ const indexToLocFormatRaw = r.i16();
38
+ if (indexToLocFormatRaw !== 0 && indexToLocFormatRaw !== 1) throw new Error(`Invalid head.indexToLocFormat: ${indexToLocFormatRaw}`);
39
+ const indexToLocFormat = indexToLocFormatRaw;
40
+ const hhea = require("hhea");
41
+ r.seek(hhea.offset + 4);
42
+ const ascender = r.i16();
43
+ const descender = r.i16();
44
+ r.skip(2);
45
+ r.skip(24);
46
+ const numberOfHMetrics = r.u16();
47
+ const maxp = require("maxp");
48
+ r.seek(maxp.offset + 4);
49
+ const numGlyphs = r.u16();
50
+ const hmtx = require("hmtx");
51
+ r.seek(hmtx.offset);
52
+ const advanceWidths = new Array(numGlyphs);
53
+ let lastAdvance = 0;
54
+ const longCount = Math.min(numberOfHMetrics, numGlyphs);
55
+ for (let i = 0; i < longCount; i++) {
56
+ lastAdvance = r.u16();
57
+ r.skip(2);
58
+ advanceWidths[i] = lastAdvance;
59
+ }
60
+ for (let i = longCount; i < numGlyphs; i++) advanceWidths[i] = lastAdvance;
61
+ const glyphForCodepoint = buildCmap(raw, require("cmap").offset);
62
+ const postScriptName = readPostScriptName(raw, require("name").offset);
63
+ let italicAngle = 0;
64
+ let isFixedPitch = false;
65
+ const post = tables.get("post");
66
+ if (post) {
67
+ r.seek(post.offset);
68
+ r.skip(4);
69
+ italicAngle = r.i32() / 65536;
70
+ r.skip(4);
71
+ isFixedPitch = r.u32() !== 0;
72
+ }
73
+ let capHeight = 0;
74
+ let xHeight = 0;
75
+ let isItalic = false;
76
+ let usWeightClass = 400;
77
+ const os2 = tables.get("OS/2");
78
+ if (os2) {
79
+ r.seek(os2.offset);
80
+ const os2Version = r.u16();
81
+ r.skip(2);
82
+ usWeightClass = r.u16();
83
+ r.seek(os2.offset + 62);
84
+ isItalic = (r.u16() & 1) !== 0;
85
+ if (os2Version >= 2 && os2.length >= 90) {
86
+ r.seek(os2.offset + 86);
87
+ xHeight = r.i16();
88
+ capHeight = r.i16();
89
+ }
90
+ }
91
+ if (capHeight === 0) capHeight = Math.round(ascender * .7);
92
+ if (xHeight === 0) xHeight = Math.round(ascender * .5);
93
+ let flags = 0;
94
+ if (isFixedPitch) flags |= 1;
95
+ flags |= 32;
96
+ if (isItalic || italicAngle !== 0) flags |= 64;
97
+ const stemV = usWeightClass > 500 ? 120 : 80;
98
+ const glyphOffsets = readGlyphOffsets(raw, tables, numGlyphs, indexToLocFormat);
99
+ const ligatures = parseGsubLigatures(raw, tables.get("GSUB"));
100
+ const kerning = parseGposKerning(raw, tables.get("GPOS"));
101
+ const joiningForms = parseGsubArabicForms(raw, tables.get("GSUB"));
102
+ return {
103
+ raw,
104
+ sfntVersion,
105
+ tables,
106
+ postScriptName,
107
+ unitsPerEm,
108
+ fontBBox: [
109
+ xMin,
110
+ yMin,
111
+ xMax,
112
+ yMax
113
+ ],
114
+ ascender,
115
+ descender,
116
+ capHeight,
117
+ xHeight,
118
+ italicAngle,
119
+ stemV,
120
+ flags,
121
+ numGlyphs,
122
+ indexToLocFormat,
123
+ glyphOffsets,
124
+ advanceWidths,
125
+ glyphForCodepoint,
126
+ ligatures,
127
+ kerning,
128
+ joiningForms
129
+ };
130
+ }
131
+ function readGlyphOffsets(raw, tables, numGlyphs, indexToLocFormat) {
132
+ const loca = tables.get("loca");
133
+ if (!loca) return [];
134
+ const r = new BigEndianReader(raw, loca.offset);
135
+ const out = new Array(numGlyphs + 1);
136
+ if (indexToLocFormat === 0) for (let i = 0; i <= numGlyphs; i++) out[i] = r.u16() * 2;
137
+ else for (let i = 0; i <= numGlyphs; i++) out[i] = r.u32();
138
+ return out;
139
+ }
140
+ function buildCmap(raw, cmapOffset) {
141
+ const r = new BigEndianReader(raw, cmapOffset);
142
+ r.u16();
143
+ const numTables = r.u16();
144
+ const subtables = [];
145
+ for (let i = 0; i < numTables; i++) {
146
+ const platform = r.u16();
147
+ const encoding = r.u16();
148
+ const absoluteOffset = cmapOffset + r.u32();
149
+ const format = new BigEndianReader(raw, absoluteOffset).u16();
150
+ subtables.push({
151
+ platform,
152
+ encoding,
153
+ absoluteOffset,
154
+ format
155
+ });
156
+ }
157
+ const priorities = [
158
+ {
159
+ platform: 3,
160
+ encoding: 10
161
+ },
162
+ {
163
+ platform: 0,
164
+ encoding: 4
165
+ },
166
+ {
167
+ platform: 0,
168
+ encoding: 6
169
+ },
170
+ {
171
+ platform: 3,
172
+ encoding: 1
173
+ },
174
+ {
175
+ platform: 0,
176
+ encoding: 3
177
+ },
178
+ {
179
+ platform: 0,
180
+ encoding: 1
181
+ },
182
+ {
183
+ platform: 0,
184
+ encoding: 0
185
+ }
186
+ ];
187
+ let chosen;
188
+ for (const want of priorities) {
189
+ chosen = subtables.find((s) => s.platform === want.platform && s.encoding === want.encoding);
190
+ if (chosen) break;
191
+ }
192
+ if (!chosen) chosen = subtables[0];
193
+ if (!chosen) return () => 0;
194
+ if (chosen.format === 4) return buildCmapFormat4(raw, chosen.absoluteOffset);
195
+ if (chosen.format === 12) return buildCmapFormat12(raw, chosen.absoluteOffset);
196
+ throw new Error(`Unsupported cmap subtable format: ${chosen.format}`);
197
+ }
198
+ function buildCmapFormat4(raw, offset) {
199
+ const r = new BigEndianReader(raw, offset);
200
+ r.u16();
201
+ r.u16();
202
+ r.u16();
203
+ const segCount = r.u16() >> 1;
204
+ r.skip(6);
205
+ const endCode = new Uint16Array(segCount);
206
+ for (let i = 0; i < segCount; i++) endCode[i] = r.u16();
207
+ r.u16();
208
+ const startCode = new Uint16Array(segCount);
209
+ for (let i = 0; i < segCount; i++) startCode[i] = r.u16();
210
+ const idDelta = new Int16Array(segCount);
211
+ for (let i = 0; i < segCount; i++) idDelta[i] = r.i16();
212
+ const idRangeOffsetPos = r.offset;
213
+ const idRangeOffset = new Uint16Array(segCount);
214
+ for (let i = 0; i < segCount; i++) idRangeOffset[i] = r.u16();
215
+ return (cp) => {
216
+ if (cp > 65535) return 0;
217
+ let seg = -1;
218
+ for (let i = 0; i < segCount; i++) if (endCode[i] >= cp) {
219
+ seg = i;
220
+ break;
221
+ }
222
+ if (seg < 0) return 0;
223
+ if (startCode[seg] > cp) return 0;
224
+ if (idRangeOffset[seg] === 0) return cp + idDelta[seg] & 65535;
225
+ const glyphId = new BigEndianReader(raw, idRangeOffsetPos + seg * 2 + idRangeOffset[seg] + (cp - startCode[seg]) * 2).u16();
226
+ if (glyphId === 0) return 0;
227
+ return glyphId + idDelta[seg] & 65535;
228
+ };
229
+ }
230
+ function buildCmapFormat12(raw, offset) {
231
+ const r = new BigEndianReader(raw, offset);
232
+ r.u16();
233
+ r.u16();
234
+ r.u32();
235
+ r.u32();
236
+ const numGroups = r.u32();
237
+ const starts = new Uint32Array(numGroups);
238
+ const ends = new Uint32Array(numGroups);
239
+ const startGids = new Uint32Array(numGroups);
240
+ for (let i = 0; i < numGroups; i++) {
241
+ starts[i] = r.u32();
242
+ ends[i] = r.u32();
243
+ startGids[i] = r.u32();
244
+ }
245
+ return (cp) => {
246
+ let lo = 0;
247
+ let hi = numGroups - 1;
248
+ while (lo <= hi) {
249
+ const mid = lo + hi >>> 1;
250
+ if (cp < starts[mid]) hi = mid - 1;
251
+ else if (cp > ends[mid]) lo = mid + 1;
252
+ else return startGids[mid] + (cp - starts[mid]);
253
+ }
254
+ return 0;
255
+ };
256
+ }
257
+ function readPostScriptName(raw, offset) {
258
+ const r = new BigEndianReader(raw, offset);
259
+ r.u16();
260
+ const count = r.u16();
261
+ const stringsBase = offset + r.u16();
262
+ const records = [];
263
+ for (let i = 0; i < count; i++) records.push({
264
+ platform: r.u16(),
265
+ encoding: r.u16(),
266
+ language: r.u16(),
267
+ nameId: r.u16(),
268
+ length: r.u16(),
269
+ stringOffset: r.u16()
270
+ });
271
+ const filters = [
272
+ (rec) => rec.platform === 3 && rec.encoding === 1 && rec.language === 1033,
273
+ (rec) => rec.platform === 3 && rec.encoding === 1,
274
+ (rec) => rec.platform === 1 && rec.encoding === 0 && rec.language === 0,
275
+ (rec) => rec.platform === 0,
276
+ () => true
277
+ ];
278
+ for (const filter of filters) {
279
+ const rec = records.find((x) => x.nameId === 6 && filter(x));
280
+ if (rec) return decodeNameRecord(raw, stringsBase + rec.stringOffset, rec.length, rec.platform);
281
+ }
282
+ throw new Error("TTF name table missing PostScript name (nameID 6)");
283
+ }
284
+ function decodeNameRecord(raw, offset, length, platform) {
285
+ const bytes = raw.subarray(offset, offset + length);
286
+ if (platform === 1) return new TextDecoder("macintosh", { fatal: false }).decode(bytes);
287
+ return new TextDecoder("utf-16be", { fatal: false }).decode(bytes);
288
+ }
289
+ //#endregion
290
+ export { parseTtf };
@@ -0,0 +1,3 @@
1
+ import { ParsedTtf } from './ttf-parser.js';
2
+ export declare function subsetTtf(parsed: ParsedTtf, usedGids: Iterable<number>): Uint8Array;
3
+ export declare function glyphClosure(parsed: ParsedTtf, usedGids: Iterable<number>): Set<number>;