pdfnative 1.2.0 → 1.3.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/README.md +41 -14
- package/dist/index.cjs +2753 -442
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +603 -29
- package/dist/index.d.ts +603 -29
- package/dist/index.js +2727 -443
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +2003 -121
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +2003 -121
- package/dist/worker/index.js.map +1 -1
- package/fonts/noto-color-emoji-data.d.ts +28 -0
- package/fonts/noto-color-emoji-data.js +26 -0
- package/fonts/noto-ethiopic-data.d.ts +13 -0
- package/fonts/noto-ethiopic-data.js +64 -0
- package/fonts/noto-khmer-data.d.ts +13 -0
- package/fonts/noto-khmer-data.js +64 -0
- package/fonts/noto-myanmar-data.d.ts +13 -0
- package/fonts/noto-myanmar-data.js +64 -0
- package/fonts/noto-sinhala-data.d.ts +13 -0
- package/fonts/noto-sinhala-data.js +64 -0
- package/fonts/noto-telugu-data.d.ts +13 -0
- package/fonts/noto-telugu-data.js +64 -0
- package/fonts/noto-tibetan-data.d.ts +13 -0
- package/fonts/noto-tibetan-data.js +64 -0
- package/package.json +22 -5
package/dist/worker/index.cjs
CHANGED
|
@@ -200,6 +200,33 @@ var BENGALI_START = 2432;
|
|
|
200
200
|
var BENGALI_END = 2559;
|
|
201
201
|
var TAMIL_START = 2944;
|
|
202
202
|
var TAMIL_END = 3071;
|
|
203
|
+
var TELUGU_START = 3072;
|
|
204
|
+
var TELUGU_END = 3199;
|
|
205
|
+
var ETHIOPIC_START = 4608;
|
|
206
|
+
var ETHIOPIC_END = 4991;
|
|
207
|
+
var ETHIOPIC_SUPPLEMENT_START = 4992;
|
|
208
|
+
var ETHIOPIC_SUPPLEMENT_END = 5023;
|
|
209
|
+
var ETHIOPIC_EXTENDED_START = 11648;
|
|
210
|
+
var ETHIOPIC_EXTENDED_END = 11743;
|
|
211
|
+
var ETHIOPIC_EXTENDED_A_START = 43776;
|
|
212
|
+
var ETHIOPIC_EXTENDED_A_END = 43823;
|
|
213
|
+
var SINHALA_START = 3456;
|
|
214
|
+
var SINHALA_END = 3583;
|
|
215
|
+
var SINHALA_VIRAMA = 3530;
|
|
216
|
+
var TIBETAN_START = 3840;
|
|
217
|
+
var TIBETAN_END = 4095;
|
|
218
|
+
var KHMER_START = 6016;
|
|
219
|
+
var KHMER_END = 6143;
|
|
220
|
+
var KHMER_SYMBOLS_START = 6624;
|
|
221
|
+
var KHMER_SYMBOLS_END = 6655;
|
|
222
|
+
var KHMER_COENG = 6098;
|
|
223
|
+
var MYANMAR_START = 4096;
|
|
224
|
+
var MYANMAR_END = 4255;
|
|
225
|
+
var MYANMAR_EXTENDED_A_START = 43616;
|
|
226
|
+
var MYANMAR_EXTENDED_A_END = 43647;
|
|
227
|
+
var MYANMAR_EXTENDED_B_START = 43488;
|
|
228
|
+
var MYANMAR_EXTENDED_B_END = 43519;
|
|
229
|
+
var MYANMAR_VIRAMA = 4153;
|
|
203
230
|
var EMOJI_RANGES = [
|
|
204
231
|
[127744, 128511],
|
|
205
232
|
// Miscellaneous Symbols and Pictographs
|
|
@@ -230,6 +257,9 @@ var EMOJI_RANGES = [
|
|
|
230
257
|
];
|
|
231
258
|
var FITZPATRICK_START = 127995;
|
|
232
259
|
var FITZPATRICK_END = 127999;
|
|
260
|
+
var ZWJ = 8205;
|
|
261
|
+
var VS15 = 65038;
|
|
262
|
+
var VS16 = 65039;
|
|
233
263
|
function isArabicCodepoint(cp) {
|
|
234
264
|
return cp >= ARABIC_START && cp <= ARABIC_END || cp >= ARABIC_SUPPLEMENT_START && cp <= ARABIC_SUPPLEMENT_END || cp >= ARABIC_EXTENDED_A_START && cp <= ARABIC_EXTENDED_A_END || cp >= ARABIC_PRES_A_START && cp <= ARABIC_PRES_A_END || cp >= ARABIC_PRES_B_START && cp <= ARABIC_PRES_B_END;
|
|
235
265
|
}
|
|
@@ -242,6 +272,24 @@ function isBengaliCodepoint(cp) {
|
|
|
242
272
|
function isTamilCodepoint(cp) {
|
|
243
273
|
return cp >= TAMIL_START && cp <= TAMIL_END;
|
|
244
274
|
}
|
|
275
|
+
function isTeluguCodepoint(cp) {
|
|
276
|
+
return cp >= TELUGU_START && cp <= TELUGU_END;
|
|
277
|
+
}
|
|
278
|
+
function isEthiopicCodepoint(cp) {
|
|
279
|
+
return cp >= ETHIOPIC_START && cp <= ETHIOPIC_END || cp >= ETHIOPIC_SUPPLEMENT_START && cp <= ETHIOPIC_SUPPLEMENT_END || cp >= ETHIOPIC_EXTENDED_START && cp <= ETHIOPIC_EXTENDED_END || cp >= ETHIOPIC_EXTENDED_A_START && cp <= ETHIOPIC_EXTENDED_A_END;
|
|
280
|
+
}
|
|
281
|
+
function isSinhalaCodepoint(cp) {
|
|
282
|
+
return cp >= SINHALA_START && cp <= SINHALA_END;
|
|
283
|
+
}
|
|
284
|
+
function isTibetanCodepoint(cp) {
|
|
285
|
+
return cp >= TIBETAN_START && cp <= TIBETAN_END;
|
|
286
|
+
}
|
|
287
|
+
function isKhmerCodepoint(cp) {
|
|
288
|
+
return cp >= KHMER_START && cp <= KHMER_END || cp >= KHMER_SYMBOLS_START && cp <= KHMER_SYMBOLS_END;
|
|
289
|
+
}
|
|
290
|
+
function isMyanmarCodepoint(cp) {
|
|
291
|
+
return cp >= MYANMAR_START && cp <= MYANMAR_END || cp >= MYANMAR_EXTENDED_A_START && cp <= MYANMAR_EXTENDED_A_END || cp >= MYANMAR_EXTENDED_B_START && cp <= MYANMAR_EXTENDED_B_END;
|
|
292
|
+
}
|
|
245
293
|
function isDevanagariCodepoint(cp) {
|
|
246
294
|
return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
|
|
247
295
|
}
|
|
@@ -252,6 +300,9 @@ function isEmojiCodepoint(cp) {
|
|
|
252
300
|
}
|
|
253
301
|
return false;
|
|
254
302
|
}
|
|
303
|
+
function isZeroWidthFormat(cp) {
|
|
304
|
+
return cp === ZWJ || cp === 8204 || cp === VS15 || cp === VS16 || cp >= FITZPATRICK_START && cp <= FITZPATRICK_END;
|
|
305
|
+
}
|
|
255
306
|
function containsArabic(text) {
|
|
256
307
|
for (let i = 0; i < text.length; ) {
|
|
257
308
|
const cp = text.codePointAt(i) ?? 0;
|
|
@@ -278,6 +329,36 @@ function containsTamil(str) {
|
|
|
278
329
|
}
|
|
279
330
|
return false;
|
|
280
331
|
}
|
|
332
|
+
function containsTelugu(str) {
|
|
333
|
+
for (let i = 0; i < str.length; i++) {
|
|
334
|
+
if (isTeluguCodepoint(str.charCodeAt(i))) return true;
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
function containsSinhala(str) {
|
|
339
|
+
for (let i = 0; i < str.length; i++) {
|
|
340
|
+
if (isSinhalaCodepoint(str.charCodeAt(i))) return true;
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
function containsTibetan(str) {
|
|
345
|
+
for (let i = 0; i < str.length; i++) {
|
|
346
|
+
if (isTibetanCodepoint(str.charCodeAt(i))) return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
function containsKhmer(str) {
|
|
351
|
+
for (let i = 0; i < str.length; i++) {
|
|
352
|
+
if (isKhmerCodepoint(str.charCodeAt(i))) return true;
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
function containsMyanmar(str) {
|
|
357
|
+
for (let i = 0; i < str.length; i++) {
|
|
358
|
+
if (isMyanmarCodepoint(str.charCodeAt(i))) return true;
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
281
362
|
function containsDevanagari(str) {
|
|
282
363
|
for (let i = 0; i < str.length; i++) {
|
|
283
364
|
if (isDevanagariCodepoint(str.charCodeAt(i))) return true;
|
|
@@ -289,6 +370,12 @@ function containsDevanagari(str) {
|
|
|
289
370
|
function detectCharLang(cp) {
|
|
290
371
|
if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
|
|
291
372
|
if (cp >= 2304 && cp <= 2431 || cp >= 43232 && cp <= 43263) return "hi";
|
|
373
|
+
if (cp >= 3072 && cp <= 3199) return "te";
|
|
374
|
+
if (isSinhalaCodepoint(cp)) return "si";
|
|
375
|
+
if (isTibetanCodepoint(cp)) return "bo";
|
|
376
|
+
if (isKhmerCodepoint(cp)) return "km";
|
|
377
|
+
if (isMyanmarCodepoint(cp)) return "my";
|
|
378
|
+
if (isEthiopicCodepoint(cp)) return "am";
|
|
292
379
|
if (cp >= 3584 && cp <= 3711) return "th";
|
|
293
380
|
if (cp >= 12352 && cp <= 12543) return "ja";
|
|
294
381
|
if (cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687) return "ko";
|
|
@@ -322,6 +409,10 @@ function splitTextByFont(str, fontEntries) {
|
|
|
322
409
|
i += charLen;
|
|
323
410
|
continue;
|
|
324
411
|
}
|
|
412
|
+
if (isZeroWidthFormat(normCp) && !fontEntries.some((fe) => fe.fontData.cmap[normCp])) {
|
|
413
|
+
i += charLen;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
325
416
|
let newEntry = null;
|
|
326
417
|
const charLang = detectCharLang(normCp);
|
|
327
418
|
if (charLang) {
|
|
@@ -626,18 +717,26 @@ function normalizeBidiEmbeddings(text) {
|
|
|
626
717
|
for (let i = 0; i < text.length; ) {
|
|
627
718
|
const cp = text.codePointAt(i) ?? 0;
|
|
628
719
|
const cpLen = cp > 65535 ? 2 : 1;
|
|
629
|
-
if (cp === LRE || cp ===
|
|
720
|
+
if (cp === LRE || cp === RLE) {
|
|
630
721
|
if (stack.length >= MAX_DEPTH) {
|
|
631
722
|
i += cpLen;
|
|
632
723
|
continue;
|
|
633
724
|
}
|
|
634
|
-
stack.push(
|
|
635
|
-
out.push(cp === LRE
|
|
725
|
+
stack.push("E");
|
|
726
|
+
out.push(cp === LRE ? LRI : RLI);
|
|
636
727
|
i += cpLen;
|
|
637
|
-
} else if (cp ===
|
|
638
|
-
if (stack.
|
|
639
|
-
|
|
728
|
+
} else if (cp === LRO || cp === RLO) {
|
|
729
|
+
if (stack.length >= MAX_DEPTH) {
|
|
730
|
+
i += cpLen;
|
|
731
|
+
continue;
|
|
640
732
|
}
|
|
733
|
+
stack.push("O");
|
|
734
|
+
out.push(cp);
|
|
735
|
+
i += cpLen;
|
|
736
|
+
} else if (cp === PDF_CP) {
|
|
737
|
+
const frame = stack.pop();
|
|
738
|
+
if (frame === "E") out.push(PDI);
|
|
739
|
+
else if (frame === "O") out.push(PDF_CP);
|
|
641
740
|
i += cpLen;
|
|
642
741
|
} else {
|
|
643
742
|
out.push(cp);
|
|
@@ -648,8 +747,95 @@ function normalizeBidiEmbeddings(text) {
|
|
|
648
747
|
for (let i = 0; i < out.length; i++) result += String.fromCodePoint(out[i]);
|
|
649
748
|
return result;
|
|
650
749
|
}
|
|
750
|
+
function matchingPdfIndex(codePoints, openCp) {
|
|
751
|
+
let depth = 1;
|
|
752
|
+
for (let j = openCp + 1; j < codePoints.length; j++) {
|
|
753
|
+
const cj = codePoints[j];
|
|
754
|
+
if (cj === 8234 || cj === 8235 || cj === 8237 || cj === 8238) depth++;
|
|
755
|
+
else if (cj === 8236) {
|
|
756
|
+
depth--;
|
|
757
|
+
if (depth === 0) return j;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return -1;
|
|
761
|
+
}
|
|
762
|
+
function matchingPdiIndex(codePoints, openCp) {
|
|
763
|
+
let depth = 1;
|
|
764
|
+
for (let j = openCp + 1; j < codePoints.length; j++) {
|
|
765
|
+
const cj = codePoints[j];
|
|
766
|
+
if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
|
|
767
|
+
else if (cj === 8297) {
|
|
768
|
+
depth--;
|
|
769
|
+
if (depth === 0) return j;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return -1;
|
|
773
|
+
}
|
|
774
|
+
function tryResolveOverrides(text, forcedLevel) {
|
|
775
|
+
const LRE = 8234, RLE = 8235, LRO = 8237, RLO = 8238;
|
|
776
|
+
if (text.indexOf("\u202D") === -1 && text.indexOf("\u202E") === -1) return null;
|
|
777
|
+
const codePoints = [];
|
|
778
|
+
const cpToStr = [];
|
|
779
|
+
for (let i2 = 0; i2 < text.length; ) {
|
|
780
|
+
cpToStr.push(i2);
|
|
781
|
+
const cp = text.codePointAt(i2) ?? 0;
|
|
782
|
+
codePoints.push(cp);
|
|
783
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
784
|
+
}
|
|
785
|
+
cpToStr.push(text.length);
|
|
786
|
+
const len = codePoints.length;
|
|
787
|
+
const spans = [];
|
|
788
|
+
let i = 0;
|
|
789
|
+
while (i < len) {
|
|
790
|
+
const cp = codePoints[i];
|
|
791
|
+
if (cp === LRO || cp === RLO) {
|
|
792
|
+
const close = matchingPdfIndex(codePoints, i);
|
|
793
|
+
if (close === -1) {
|
|
794
|
+
i++;
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
spans.push({ open: i, close, rtl: cp === RLO });
|
|
798
|
+
i = close + 1;
|
|
799
|
+
} else if (cp === LRE || cp === RLE) {
|
|
800
|
+
const close = matchingPdfIndex(codePoints, i);
|
|
801
|
+
i = close === -1 ? i + 1 : close + 1;
|
|
802
|
+
} else if (cp === 8294 || cp === 8295 || cp === 8296) {
|
|
803
|
+
const close = matchingPdiIndex(codePoints, i);
|
|
804
|
+
i = close === -1 ? i + 1 : close + 1;
|
|
805
|
+
} else {
|
|
806
|
+
i++;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (spans.length === 0) return null;
|
|
810
|
+
const out = [];
|
|
811
|
+
const emitGap = (cpStart, cpEnd) => {
|
|
812
|
+
if (cpStart >= cpEnd) return;
|
|
813
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
814
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
815
|
+
const segRuns = forcedLevel === void 0 ? resolveBidiRuns(segText) : resolveBidiRunsForced(segText, forcedLevel);
|
|
816
|
+
for (const r of segRuns) {
|
|
817
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
let cursor = 0;
|
|
821
|
+
for (const span of spans) {
|
|
822
|
+
emitGap(cursor, span.open);
|
|
823
|
+
const innerRaw = text.substring(cpToStr[span.open + 1], cpToStr[span.close]);
|
|
824
|
+
const inner = stripBidiControls(innerRaw);
|
|
825
|
+
if (inner) {
|
|
826
|
+
const level = span.rtl ? 1 : 0;
|
|
827
|
+
const runText = span.rtl ? reverseString(inner) : inner;
|
|
828
|
+
out.push({ text: runText, level, start: cpToStr[span.open + 1] });
|
|
829
|
+
}
|
|
830
|
+
cursor = span.close + 1;
|
|
831
|
+
}
|
|
832
|
+
emitGap(cursor, len);
|
|
833
|
+
return out;
|
|
834
|
+
}
|
|
651
835
|
function resolveBidiRuns(text) {
|
|
652
836
|
if (!text) return [];
|
|
837
|
+
const overrideRuns = tryResolveOverrides(text);
|
|
838
|
+
if (overrideRuns) return overrideRuns;
|
|
653
839
|
const normalized = normalizeBidiEmbeddings(text);
|
|
654
840
|
if (normalized !== text) {
|
|
655
841
|
return resolveBidiRuns(normalized);
|
|
@@ -712,6 +898,8 @@ function resolveBidiRuns(text) {
|
|
|
712
898
|
}
|
|
713
899
|
function resolveBidiRunsForced(text, forcedLevel) {
|
|
714
900
|
if (!text) return [];
|
|
901
|
+
const overrideRuns = tryResolveOverrides(text, forcedLevel);
|
|
902
|
+
if (overrideRuns) return overrideRuns;
|
|
715
903
|
const codePoints = [];
|
|
716
904
|
const cpToStr = [];
|
|
717
905
|
for (let i = 0; i < text.length; ) {
|
|
@@ -800,6 +988,7 @@ function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
|
|
|
800
988
|
function containsRTL(text) {
|
|
801
989
|
for (let i = 0; i < text.length; ) {
|
|
802
990
|
const cp = text.codePointAt(i) ?? 0;
|
|
991
|
+
if (cp === 8237 || cp === 8238) return true;
|
|
803
992
|
const t = classifyBidiType(cp);
|
|
804
993
|
if (t === "R" || t === "AL") return true;
|
|
805
994
|
i += cp > 65535 ? 2 : 1;
|
|
@@ -881,6 +1070,57 @@ function pdfString(str) {
|
|
|
881
1070
|
const s = toWinAnsi(stripBidiControls(str));
|
|
882
1071
|
return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
|
|
883
1072
|
}
|
|
1073
|
+
var WINANSI_HIGH_TO_UNICODE = {
|
|
1074
|
+
128: 8364,
|
|
1075
|
+
130: 8218,
|
|
1076
|
+
131: 402,
|
|
1077
|
+
132: 8222,
|
|
1078
|
+
133: 8230,
|
|
1079
|
+
134: 8224,
|
|
1080
|
+
135: 8225,
|
|
1081
|
+
136: 710,
|
|
1082
|
+
137: 8240,
|
|
1083
|
+
138: 352,
|
|
1084
|
+
139: 8249,
|
|
1085
|
+
140: 338,
|
|
1086
|
+
142: 381,
|
|
1087
|
+
145: 8216,
|
|
1088
|
+
146: 8217,
|
|
1089
|
+
147: 8220,
|
|
1090
|
+
148: 8221,
|
|
1091
|
+
149: 8226,
|
|
1092
|
+
150: 8211,
|
|
1093
|
+
151: 8212,
|
|
1094
|
+
152: 732,
|
|
1095
|
+
153: 8482,
|
|
1096
|
+
154: 353,
|
|
1097
|
+
155: 8250,
|
|
1098
|
+
156: 339,
|
|
1099
|
+
158: 382,
|
|
1100
|
+
159: 376
|
|
1101
|
+
};
|
|
1102
|
+
function buildWinAnsiToUnicodeCMap() {
|
|
1103
|
+
const entries = [];
|
|
1104
|
+
const hex2 = (n2) => n2.toString(16).toUpperCase().padStart(2, "0");
|
|
1105
|
+
const hex4 = (n2) => n2.toString(16).toUpperCase().padStart(4, "0");
|
|
1106
|
+
for (let b = 32; b <= 255; b++) {
|
|
1107
|
+
if (b >= 127 && b <= 159) {
|
|
1108
|
+
const u = WINANSI_HIGH_TO_UNICODE[b];
|
|
1109
|
+
if (u === void 0) continue;
|
|
1110
|
+
entries.push(`<${hex2(b)}> <${hex4(u)}>`);
|
|
1111
|
+
} else {
|
|
1112
|
+
entries.push(`<${hex2(b)}> <${hex4(b)}>`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
const blocks = [];
|
|
1116
|
+
for (let i = 0; i < entries.length; i += 100) {
|
|
1117
|
+
const chunk = entries.slice(i, i + 100);
|
|
1118
|
+
blocks.push(`${chunk.length} beginbfchar
|
|
1119
|
+
${chunk.join("\n")}
|
|
1120
|
+
endbfchar`);
|
|
1121
|
+
}
|
|
1122
|
+
return "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<20> <FF>\nendcodespacerange\n" + blocks.join("\n") + "\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend";
|
|
1123
|
+
}
|
|
884
1124
|
function truncate(str, max) {
|
|
885
1125
|
if (!str || str.length <= max) return str || "";
|
|
886
1126
|
if (max <= 1) return "\u2026";
|
|
@@ -956,6 +1196,73 @@ function tryLigature(gids, ligatures) {
|
|
|
956
1196
|
return null;
|
|
957
1197
|
}
|
|
958
1198
|
|
|
1199
|
+
// src/shaping/use-lite.ts
|
|
1200
|
+
function devanagariCategory(cp) {
|
|
1201
|
+
if (cp === 2305 || cp === 2306) return "Mabv";
|
|
1202
|
+
if (cp === 2307) return "Mpst";
|
|
1203
|
+
if (cp >= 2308 && cp <= 2324) return "V";
|
|
1204
|
+
if (cp >= 2325 && cp <= 2361) return "B";
|
|
1205
|
+
if (cp === 2362) return "Mabv";
|
|
1206
|
+
if (cp === 2363) return "Mpst";
|
|
1207
|
+
if (cp === 2364) return "Mblw";
|
|
1208
|
+
if (cp === 2365) return "O";
|
|
1209
|
+
if (cp === 2366) return "Mpst";
|
|
1210
|
+
if (cp >= 2367 && cp <= 2368) return cp === 2367 ? "Mpre" : "Mpst";
|
|
1211
|
+
if (cp >= 2369 && cp <= 2376) return "Mblw";
|
|
1212
|
+
if (cp >= 2377 && cp <= 2380) return "Mpst";
|
|
1213
|
+
if (cp === 2381) return "H";
|
|
1214
|
+
if (cp >= 2382 && cp <= 2383) return "Mpre";
|
|
1215
|
+
if (cp === 2384) return "O";
|
|
1216
|
+
if (cp >= 2385 && cp <= 2391) return "Mabv";
|
|
1217
|
+
if (cp >= 2392 && cp <= 2401) return "B";
|
|
1218
|
+
if (cp >= 2402 && cp <= 2403) return "Mblw";
|
|
1219
|
+
if (cp >= 2406 && cp <= 2415) return "N";
|
|
1220
|
+
return "O";
|
|
1221
|
+
}
|
|
1222
|
+
function bengaliCategory(cp) {
|
|
1223
|
+
if (cp === 2433) return "Mabv";
|
|
1224
|
+
if (cp === 2434) return "Mpst";
|
|
1225
|
+
if (cp === 2435) return "Mpst";
|
|
1226
|
+
if (cp >= 2437 && cp <= 2452) return "V";
|
|
1227
|
+
if (cp >= 2453 && cp <= 2489) return "B";
|
|
1228
|
+
if (cp === 2492) return "Mblw";
|
|
1229
|
+
if (cp === 2493) return "O";
|
|
1230
|
+
if (cp === 2494) return "Mpst";
|
|
1231
|
+
if (cp >= 2495 && cp <= 2496) return cp === 2495 ? "Mpre" : "Mpst";
|
|
1232
|
+
if (cp >= 2497 && cp <= 2500) return "Mblw";
|
|
1233
|
+
if (cp >= 2503 && cp <= 2504) return "Mpre";
|
|
1234
|
+
if (cp >= 2507 && cp <= 2508) return "Mpre";
|
|
1235
|
+
if (cp === 2509) return "H";
|
|
1236
|
+
if (cp === 2510) return "B";
|
|
1237
|
+
if (cp === 2519) return "Mpst";
|
|
1238
|
+
if (cp >= 2524 && cp <= 2527) return "B";
|
|
1239
|
+
if (cp >= 2528 && cp <= 2531) return "O";
|
|
1240
|
+
if (cp >= 2534 && cp <= 2543) return "N";
|
|
1241
|
+
return "O";
|
|
1242
|
+
}
|
|
1243
|
+
function tamilCategory(cp) {
|
|
1244
|
+
if (cp === 2946) return "Mabv";
|
|
1245
|
+
if (cp >= 2949 && cp <= 2964) return "V";
|
|
1246
|
+
if (cp >= 2965 && cp <= 3001) return "B";
|
|
1247
|
+
if (cp === 3006) return "Mpst";
|
|
1248
|
+
if (cp === 3007) return "Mpst";
|
|
1249
|
+
if (cp >= 3008 && cp <= 3010) return "Mpst";
|
|
1250
|
+
if (cp >= 3014 && cp <= 3016) return "Mpre";
|
|
1251
|
+
if (cp >= 3018 && cp <= 3020) return "Mpre";
|
|
1252
|
+
if (cp === 3021) return "H";
|
|
1253
|
+
if (cp === 3031) return "Mpst";
|
|
1254
|
+
if (cp >= 3046 && cp <= 3055) return "N";
|
|
1255
|
+
return "O";
|
|
1256
|
+
}
|
|
1257
|
+
function classifyUseCategory(cp) {
|
|
1258
|
+
if (cp === 8204) return "ZWNJ";
|
|
1259
|
+
if (cp === 8205) return "ZWJ";
|
|
1260
|
+
if (cp >= 2304 && cp <= 2431) return devanagariCategory(cp);
|
|
1261
|
+
if (cp >= 2432 && cp <= 2559) return bengaliCategory(cp);
|
|
1262
|
+
if (cp >= 2944 && cp <= 3071) return tamilCategory(cp);
|
|
1263
|
+
return "O";
|
|
1264
|
+
}
|
|
1265
|
+
|
|
959
1266
|
// src/shaping/bengali-shaper.ts
|
|
960
1267
|
var HALANT = 2509;
|
|
961
1268
|
var NUKTA = 2492;
|
|
@@ -997,6 +1304,10 @@ function buildBengaliClusters(str) {
|
|
|
997
1304
|
while (i < cps.length) {
|
|
998
1305
|
const cp = cps[i];
|
|
999
1306
|
const type = bengaliCharType(cp);
|
|
1307
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
1308
|
+
i++;
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1000
1311
|
if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
|
|
1001
1312
|
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
1002
1313
|
i++;
|
|
@@ -1024,13 +1335,24 @@ function buildBengaliClusters(str) {
|
|
|
1024
1335
|
i++;
|
|
1025
1336
|
}
|
|
1026
1337
|
if (i < cps.length && cps[i] === HALANT) {
|
|
1027
|
-
|
|
1338
|
+
let j = i + 1;
|
|
1339
|
+
let zwnj = false;
|
|
1340
|
+
if (j < cps.length) {
|
|
1341
|
+
const jc = classifyUseCategory(cps[j]);
|
|
1342
|
+
if (jc === "ZWJ") {
|
|
1343
|
+
j++;
|
|
1344
|
+
} else if (jc === "ZWNJ") {
|
|
1345
|
+
j++;
|
|
1346
|
+
zwnj = true;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
if (!zwnj && j < cps.length && isConsonant(cps[j])) {
|
|
1028
1350
|
syllable.push(cps[i]);
|
|
1029
|
-
i
|
|
1351
|
+
i = j;
|
|
1030
1352
|
continue;
|
|
1031
1353
|
} else {
|
|
1032
1354
|
syllable.push(cps[i]);
|
|
1033
|
-
i
|
|
1355
|
+
i = j;
|
|
1034
1356
|
break;
|
|
1035
1357
|
}
|
|
1036
1358
|
}
|
|
@@ -1267,6 +1589,10 @@ function buildTamilClusters(str) {
|
|
|
1267
1589
|
while (i < cps.length) {
|
|
1268
1590
|
const cp = cps[i];
|
|
1269
1591
|
const type = tamilCharType(cp);
|
|
1592
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
1593
|
+
i++;
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1270
1596
|
if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
|
|
1271
1597
|
clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
|
|
1272
1598
|
i++;
|
|
@@ -1283,13 +1609,24 @@ function buildTamilClusters(str) {
|
|
|
1283
1609
|
syllable.push(cc);
|
|
1284
1610
|
i++;
|
|
1285
1611
|
if (i < cps.length && cps[i] === PULLI) {
|
|
1286
|
-
|
|
1612
|
+
let j = i + 1;
|
|
1613
|
+
let zwnj = false;
|
|
1614
|
+
if (j < cps.length) {
|
|
1615
|
+
const jc = classifyUseCategory(cps[j]);
|
|
1616
|
+
if (jc === "ZWJ") {
|
|
1617
|
+
j++;
|
|
1618
|
+
} else if (jc === "ZWNJ") {
|
|
1619
|
+
j++;
|
|
1620
|
+
zwnj = true;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
if (!zwnj && j < cps.length && isConsonant2(cps[j])) {
|
|
1287
1624
|
syllable.push(cps[i]);
|
|
1288
|
-
i
|
|
1625
|
+
i = j;
|
|
1289
1626
|
continue;
|
|
1290
1627
|
} else {
|
|
1291
1628
|
syllable.push(cps[i]);
|
|
1292
|
-
i
|
|
1629
|
+
i = j;
|
|
1293
1630
|
break;
|
|
1294
1631
|
}
|
|
1295
1632
|
}
|
|
@@ -1401,76 +1738,1084 @@ function shapeTamilText(str, fontData) {
|
|
|
1401
1738
|
}
|
|
1402
1739
|
}
|
|
1403
1740
|
}
|
|
1404
|
-
const clusterGids = [];
|
|
1405
|
-
const clusterEndIdx = [];
|
|
1406
|
-
let matraStart = codepoints.length;
|
|
1741
|
+
const clusterGids = [];
|
|
1742
|
+
const clusterEndIdx = [];
|
|
1743
|
+
let matraStart = codepoints.length;
|
|
1744
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1745
|
+
const ct = tamilCharType(codepoints[ci]);
|
|
1746
|
+
if (ct === 0 || ct === 7) {
|
|
1747
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1748
|
+
clusterEndIdx.push(ci);
|
|
1749
|
+
} else if (ct >= 2) {
|
|
1750
|
+
matraStart = ci;
|
|
1751
|
+
break;
|
|
1752
|
+
} else if (ct < 0) {
|
|
1753
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1754
|
+
} else if (ct === 1) {
|
|
1755
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
let ligConsumed = 0;
|
|
1759
|
+
const ligResult = tryLig(clusterGids);
|
|
1760
|
+
if (ligResult) {
|
|
1761
|
+
emitGlyph(ligResult.resultGid, false);
|
|
1762
|
+
baseGid = ligResult.resultGid;
|
|
1763
|
+
ligConsumed = ligResult.consumed;
|
|
1764
|
+
let gi = ligConsumed;
|
|
1765
|
+
while (gi < clusterGids.length) {
|
|
1766
|
+
const subSeq = clusterGids.slice(gi);
|
|
1767
|
+
const subLig = tryLig(subSeq);
|
|
1768
|
+
if (subLig) {
|
|
1769
|
+
emitGlyph(subLig.resultGid, false);
|
|
1770
|
+
gi += subLig.consumed;
|
|
1771
|
+
} else {
|
|
1772
|
+
const origCi = clusterEndIdx[gi];
|
|
1773
|
+
const ct = tamilCharType(codepoints[origCi]);
|
|
1774
|
+
if (ct === 7) {
|
|
1775
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1776
|
+
} else {
|
|
1777
|
+
emitGlyph(clusterGids[gi], false);
|
|
1778
|
+
}
|
|
1779
|
+
gi++;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
} else {
|
|
1783
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
1784
|
+
const cp = codepoints[ci];
|
|
1785
|
+
const ct = tamilCharType(cp);
|
|
1786
|
+
if (ct === 0) {
|
|
1787
|
+
emitGlyph(resolveGid(cp), false);
|
|
1788
|
+
} else if (ct === 7) {
|
|
1789
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1794
|
+
const cp = codepoints[ci];
|
|
1795
|
+
const ct = tamilCharType(cp);
|
|
1796
|
+
if (ct === 4) continue;
|
|
1797
|
+
if (ct === 2 || ct === 3) {
|
|
1798
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1799
|
+
} else if (ct === 5) {
|
|
1800
|
+
emitGlyph(resolveGid(cp), false);
|
|
1801
|
+
} else if (ct === 6) {
|
|
1802
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1803
|
+
} else if (ct === 9) {
|
|
1804
|
+
emitGlyph(resolveGid(cp), false);
|
|
1805
|
+
} else {
|
|
1806
|
+
emitGlyph(resolveGid(cp), false);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
for (const postCp of splitPostComponents) {
|
|
1810
|
+
emitGlyph(resolveGid(postCp), false);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
return shaped;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// src/shaping/telugu-shaper.ts
|
|
1817
|
+
var VIRAMA = 3149;
|
|
1818
|
+
function teluguCharType(cp) {
|
|
1819
|
+
if (cp === VIRAMA) return 7;
|
|
1820
|
+
if (cp >= 3072 && cp <= 3076) return 6;
|
|
1821
|
+
if (cp >= 3077 && cp <= 3092) return 1;
|
|
1822
|
+
if (cp === 3133) return 1;
|
|
1823
|
+
if (cp >= 3093 && cp <= 3129) return 0;
|
|
1824
|
+
if (cp >= 3160 && cp <= 3162) return 0;
|
|
1825
|
+
if (cp === 3134) return 5;
|
|
1826
|
+
if (cp === 3135) return 2;
|
|
1827
|
+
if (cp === 3136) return 2;
|
|
1828
|
+
if (cp === 3137) return 5;
|
|
1829
|
+
if (cp === 3138) return 5;
|
|
1830
|
+
if (cp === 3139) return 2;
|
|
1831
|
+
if (cp === 3140) return 2;
|
|
1832
|
+
if (cp === 3142) return 2;
|
|
1833
|
+
if (cp === 3143) return 2;
|
|
1834
|
+
if (cp === 3144) return 2;
|
|
1835
|
+
if (cp === 3146) return 5;
|
|
1836
|
+
if (cp === 3147) return 5;
|
|
1837
|
+
if (cp === 3148) return 5;
|
|
1838
|
+
if (cp === 3170 || cp === 3171) return 3;
|
|
1839
|
+
if (cp === 3157 || cp === 3158) return 6;
|
|
1840
|
+
if (cp === 3168 || cp === 3169) return 1;
|
|
1841
|
+
if (cp >= 3174 && cp <= 3183) return 9;
|
|
1842
|
+
if (cp >= 3192 && cp <= 3199) return 9;
|
|
1843
|
+
return -1;
|
|
1844
|
+
}
|
|
1845
|
+
function isConsonant3(cp) {
|
|
1846
|
+
return teluguCharType(cp) === 0;
|
|
1847
|
+
}
|
|
1848
|
+
function buildTeluguClusters(str) {
|
|
1849
|
+
const clusters = [];
|
|
1850
|
+
const cps = [];
|
|
1851
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
1852
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
1853
|
+
cps.push(cp);
|
|
1854
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
1855
|
+
}
|
|
1856
|
+
let i = 0;
|
|
1857
|
+
while (i < cps.length) {
|
|
1858
|
+
const cp = cps[i];
|
|
1859
|
+
const type = teluguCharType(cp);
|
|
1860
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
1861
|
+
i++;
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
if (type < 0 || cp < TELUGU_START || cp > TELUGU_END) {
|
|
1865
|
+
clusters.push({ codepoints: [cp], baseIndex: 0 });
|
|
1866
|
+
i++;
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
const syllable = [];
|
|
1870
|
+
let lastConsonantIdx = -1;
|
|
1871
|
+
while (i < cps.length) {
|
|
1872
|
+
const cc = cps[i];
|
|
1873
|
+
const ct = teluguCharType(cc);
|
|
1874
|
+
if (ct === 0) {
|
|
1875
|
+
lastConsonantIdx = syllable.length;
|
|
1876
|
+
syllable.push(cc);
|
|
1877
|
+
i++;
|
|
1878
|
+
if (i < cps.length && cps[i] === VIRAMA) {
|
|
1879
|
+
let j = i + 1;
|
|
1880
|
+
let zwnj = false;
|
|
1881
|
+
if (j < cps.length) {
|
|
1882
|
+
const jc = classifyUseCategory(cps[j]);
|
|
1883
|
+
if (jc === "ZWJ") {
|
|
1884
|
+
j++;
|
|
1885
|
+
} else if (jc === "ZWNJ") {
|
|
1886
|
+
j++;
|
|
1887
|
+
zwnj = true;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
if (!zwnj && j < cps.length && isConsonant3(cps[j])) {
|
|
1891
|
+
syllable.push(cps[i]);
|
|
1892
|
+
i = j;
|
|
1893
|
+
continue;
|
|
1894
|
+
} else {
|
|
1895
|
+
syllable.push(cps[i]);
|
|
1896
|
+
i = j;
|
|
1897
|
+
break;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
break;
|
|
1901
|
+
} else {
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1906
|
+
while (i < cps.length) {
|
|
1907
|
+
const ct = teluguCharType(cps[i]);
|
|
1908
|
+
if (ct >= 2 && ct <= 5) {
|
|
1909
|
+
syllable.push(cps[i]);
|
|
1910
|
+
i++;
|
|
1911
|
+
} else {
|
|
1912
|
+
break;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
while (i < cps.length && teluguCharType(cps[i]) === 6) {
|
|
1916
|
+
syllable.push(cps[i]);
|
|
1917
|
+
i++;
|
|
1918
|
+
}
|
|
1919
|
+
if (syllable.length === 0) {
|
|
1920
|
+
syllable.push(cps[i] ?? 32);
|
|
1921
|
+
i++;
|
|
1922
|
+
}
|
|
1923
|
+
clusters.push({ codepoints: syllable, baseIndex: baseIdx });
|
|
1924
|
+
}
|
|
1925
|
+
return clusters;
|
|
1926
|
+
}
|
|
1927
|
+
function shapeTeluguText(str, fontData) {
|
|
1928
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1929
|
+
const shaped = [];
|
|
1930
|
+
function resolveGid(cp) {
|
|
1931
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1932
|
+
return cmap[normCp] || 0;
|
|
1933
|
+
}
|
|
1934
|
+
function tryLig(gids) {
|
|
1935
|
+
return tryLigature(gids, ligatures);
|
|
1936
|
+
}
|
|
1937
|
+
function getAdv(gid) {
|
|
1938
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1939
|
+
}
|
|
1940
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
1941
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
1942
|
+
if (!base) return null;
|
|
1943
|
+
return base[markClass] ?? null;
|
|
1944
|
+
}
|
|
1945
|
+
function getMarkAnchor2(markGid) {
|
|
1946
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
1947
|
+
if (!mark) return null;
|
|
1948
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1949
|
+
}
|
|
1950
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
1951
|
+
if (isZero && baseGid !== void 0) {
|
|
1952
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
1953
|
+
if (markAnchor) {
|
|
1954
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
1955
|
+
if (baseAnchorPt) {
|
|
1956
|
+
const baseAdv = getAdv(baseGid);
|
|
1957
|
+
shaped.push({
|
|
1958
|
+
gid,
|
|
1959
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
1960
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
1961
|
+
isZeroAdvance: true
|
|
1962
|
+
});
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
1967
|
+
} else {
|
|
1968
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const clusters = buildTeluguClusters(str);
|
|
1972
|
+
for (const cluster of clusters) {
|
|
1973
|
+
const { codepoints } = cluster;
|
|
1974
|
+
let baseGid = 0;
|
|
1975
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1976
|
+
const ct = teluguCharType(codepoints[ci]);
|
|
1977
|
+
if (ct === 0) {
|
|
1978
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
1979
|
+
} else if (ct >= 2) {
|
|
1980
|
+
break;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
const clusterGids = [];
|
|
1984
|
+
const clusterEndIdx = [];
|
|
1985
|
+
let matraStart = codepoints.length;
|
|
1986
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1987
|
+
const ct = teluguCharType(codepoints[ci]);
|
|
1988
|
+
if (ct === 0 || ct === 7) {
|
|
1989
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1990
|
+
clusterEndIdx.push(ci);
|
|
1991
|
+
} else if (ct >= 2) {
|
|
1992
|
+
matraStart = ci;
|
|
1993
|
+
break;
|
|
1994
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1995
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
const ligResult = tryLig(clusterGids);
|
|
1999
|
+
if (ligResult) {
|
|
2000
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2001
|
+
baseGid = ligResult.resultGid;
|
|
2002
|
+
let gi = ligResult.consumed;
|
|
2003
|
+
while (gi < clusterGids.length) {
|
|
2004
|
+
const subSeq = clusterGids.slice(gi);
|
|
2005
|
+
const subLig = tryLig(subSeq);
|
|
2006
|
+
if (subLig) {
|
|
2007
|
+
emitGlyph(subLig.resultGid, false);
|
|
2008
|
+
gi += subLig.consumed;
|
|
2009
|
+
} else {
|
|
2010
|
+
const origCi = clusterEndIdx[gi];
|
|
2011
|
+
const ct = teluguCharType(codepoints[origCi]);
|
|
2012
|
+
if (ct === 7) {
|
|
2013
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
2014
|
+
} else {
|
|
2015
|
+
emitGlyph(clusterGids[gi], false);
|
|
2016
|
+
}
|
|
2017
|
+
gi++;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
} else {
|
|
2021
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
2022
|
+
const cp = codepoints[ci];
|
|
2023
|
+
const ct = teluguCharType(cp);
|
|
2024
|
+
if (ct === 0) {
|
|
2025
|
+
emitGlyph(resolveGid(cp), false);
|
|
2026
|
+
} else if (ct === 7) {
|
|
2027
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
2032
|
+
const cp = codepoints[ci];
|
|
2033
|
+
const ct = teluguCharType(cp);
|
|
2034
|
+
if (ct === 2 || ct === 3) {
|
|
2035
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
2036
|
+
} else if (ct === 5) {
|
|
2037
|
+
emitGlyph(resolveGid(cp), false);
|
|
2038
|
+
} else if (ct === 6) {
|
|
2039
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
2040
|
+
} else if (ct === 9) {
|
|
2041
|
+
emitGlyph(resolveGid(cp), false);
|
|
2042
|
+
} else {
|
|
2043
|
+
emitGlyph(resolveGid(cp), false);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return shaped;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
// src/shaping/sinhala-shaper.ts
|
|
2051
|
+
var VIRAMA2 = SINHALA_VIRAMA;
|
|
2052
|
+
var TWO_PART_VOWELS = {
|
|
2053
|
+
3546: [3545, 3530],
|
|
2054
|
+
// ේ (ee)
|
|
2055
|
+
3548: [3545, 3535],
|
|
2056
|
+
// ො (o)
|
|
2057
|
+
3549: [3545, 3535, 3530],
|
|
2058
|
+
// ෝ (oo)
|
|
2059
|
+
3550: [3545, 3551]
|
|
2060
|
+
// ෞ (au)
|
|
2061
|
+
};
|
|
2062
|
+
function sinhalaCharType(cp) {
|
|
2063
|
+
if (cp === VIRAMA2) return 7;
|
|
2064
|
+
if (cp >= 3458 && cp <= 3460) return 6;
|
|
2065
|
+
if (cp >= 3461 && cp <= 3478) return 1;
|
|
2066
|
+
if (cp >= 3482 && cp <= 3526) return 0;
|
|
2067
|
+
if (cp === 3535) return 5;
|
|
2068
|
+
if (cp === 3536 || cp === 3537) return 5;
|
|
2069
|
+
if (cp === 3538 || cp === 3539) return 2;
|
|
2070
|
+
if (cp === 3540 || cp === 3542) return 3;
|
|
2071
|
+
if (cp === 3544) return 5;
|
|
2072
|
+
if (cp === 3545 || cp === 3547) return 4;
|
|
2073
|
+
if (cp === 3551) return 5;
|
|
2074
|
+
if (cp === 3570 || cp === 3571) return 5;
|
|
2075
|
+
if (cp >= 3558 && cp <= 3567) return 9;
|
|
2076
|
+
return -1;
|
|
2077
|
+
}
|
|
2078
|
+
function isConsonant4(cp) {
|
|
2079
|
+
return sinhalaCharType(cp) === 0;
|
|
2080
|
+
}
|
|
2081
|
+
function buildSinhalaClusters(str) {
|
|
2082
|
+
const clusters = [];
|
|
2083
|
+
const cps = [];
|
|
2084
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
2085
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
2086
|
+
const decomp = TWO_PART_VOWELS[cp];
|
|
2087
|
+
if (decomp) {
|
|
2088
|
+
for (const d of decomp) cps.push(d);
|
|
2089
|
+
} else {
|
|
2090
|
+
cps.push(cp);
|
|
2091
|
+
}
|
|
2092
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
2093
|
+
}
|
|
2094
|
+
let i = 0;
|
|
2095
|
+
while (i < cps.length) {
|
|
2096
|
+
const cp = cps[i];
|
|
2097
|
+
const type = sinhalaCharType(cp);
|
|
2098
|
+
if (classifyUseCategory(cp) === "ZWNJ") {
|
|
2099
|
+
i++;
|
|
2100
|
+
continue;
|
|
2101
|
+
}
|
|
2102
|
+
if (type < 0 || cp < SINHALA_START || cp > SINHALA_END) {
|
|
2103
|
+
clusters.push({ codepoints: [cp], baseIndex: 0 });
|
|
2104
|
+
i++;
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
2107
|
+
const syllable = [];
|
|
2108
|
+
let lastConsonantIdx = -1;
|
|
2109
|
+
while (i < cps.length) {
|
|
2110
|
+
const cc = cps[i];
|
|
2111
|
+
const ct = sinhalaCharType(cc);
|
|
2112
|
+
if (ct === 0) {
|
|
2113
|
+
lastConsonantIdx = syllable.length;
|
|
2114
|
+
syllable.push(cc);
|
|
2115
|
+
i++;
|
|
2116
|
+
if (i < cps.length && cps[i] === VIRAMA2) {
|
|
2117
|
+
let j = i + 1;
|
|
2118
|
+
let zwnj = false;
|
|
2119
|
+
let zwj = false;
|
|
2120
|
+
if (j < cps.length) {
|
|
2121
|
+
const jc = classifyUseCategory(cps[j]);
|
|
2122
|
+
if (jc === "ZWJ") {
|
|
2123
|
+
j++;
|
|
2124
|
+
zwj = true;
|
|
2125
|
+
} else if (jc === "ZWNJ") {
|
|
2126
|
+
j++;
|
|
2127
|
+
zwnj = true;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
if (!zwnj && j < cps.length && isConsonant4(cps[j])) {
|
|
2131
|
+
syllable.push(cps[i]);
|
|
2132
|
+
if (zwj) syllable.push(8205);
|
|
2133
|
+
i = j;
|
|
2134
|
+
continue;
|
|
2135
|
+
} else {
|
|
2136
|
+
syllable.push(cps[i]);
|
|
2137
|
+
i = j;
|
|
2138
|
+
break;
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
break;
|
|
2142
|
+
} else {
|
|
2143
|
+
break;
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
2147
|
+
while (i < cps.length) {
|
|
2148
|
+
const ct = sinhalaCharType(cps[i]);
|
|
2149
|
+
if (ct >= 2 && ct <= 5) {
|
|
2150
|
+
syllable.push(cps[i]);
|
|
2151
|
+
i++;
|
|
2152
|
+
} else {
|
|
2153
|
+
break;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
while (i < cps.length && sinhalaCharType(cps[i]) === 6) {
|
|
2157
|
+
syllable.push(cps[i]);
|
|
2158
|
+
i++;
|
|
2159
|
+
}
|
|
2160
|
+
if (syllable.length === 0) {
|
|
2161
|
+
syllable.push(cps[i] ?? 32);
|
|
2162
|
+
i++;
|
|
2163
|
+
}
|
|
2164
|
+
clusters.push({ codepoints: syllable, baseIndex: baseIdx });
|
|
2165
|
+
}
|
|
2166
|
+
return clusters;
|
|
2167
|
+
}
|
|
2168
|
+
function shapeSinhalaText(str, fontData) {
|
|
2169
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
2170
|
+
const shaped = [];
|
|
2171
|
+
function resolveGid(cp) {
|
|
2172
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
2173
|
+
return cmap[normCp] || 0;
|
|
2174
|
+
}
|
|
2175
|
+
function tryLig(gids) {
|
|
2176
|
+
return tryLigature(gids, ligatures);
|
|
2177
|
+
}
|
|
2178
|
+
function getAdv(gid) {
|
|
2179
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
2180
|
+
}
|
|
2181
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
2182
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
2183
|
+
if (!base) return null;
|
|
2184
|
+
return base[markClass] ?? null;
|
|
2185
|
+
}
|
|
2186
|
+
function getMarkAnchor2(markGid) {
|
|
2187
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
2188
|
+
if (!mark) return null;
|
|
2189
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
2190
|
+
}
|
|
2191
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
2192
|
+
if (isZero && baseGid !== void 0) {
|
|
2193
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
2194
|
+
if (markAnchor) {
|
|
2195
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
2196
|
+
if (baseAnchorPt) {
|
|
2197
|
+
const baseAdv = getAdv(baseGid);
|
|
2198
|
+
shaped.push({
|
|
2199
|
+
gid,
|
|
2200
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
2201
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
2202
|
+
isZeroAdvance: true
|
|
2203
|
+
});
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
2208
|
+
} else {
|
|
2209
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
const clusters = buildSinhalaClusters(str);
|
|
2213
|
+
for (const cluster of clusters) {
|
|
2214
|
+
const { codepoints } = cluster;
|
|
2215
|
+
let baseGid = 0;
|
|
2216
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2217
|
+
const ct = sinhalaCharType(codepoints[ci]);
|
|
2218
|
+
if (ct === 0) {
|
|
2219
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
2220
|
+
} else if (ct >= 2 && ct !== 7) {
|
|
2221
|
+
break;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2225
|
+
if (sinhalaCharType(codepoints[ci]) === 4) {
|
|
2226
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
const clusterGids = [];
|
|
2230
|
+
const clusterEndIdx = [];
|
|
2231
|
+
let matraStart = codepoints.length;
|
|
2232
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2233
|
+
const cp = codepoints[ci];
|
|
2234
|
+
const ct = sinhalaCharType(cp);
|
|
2235
|
+
if (ct === 0 || ct === 7 || cp === 8205) {
|
|
2236
|
+
clusterGids.push(resolveGid(cp));
|
|
2237
|
+
clusterEndIdx.push(ci);
|
|
2238
|
+
} else if (ct >= 2 && ct <= 6) {
|
|
2239
|
+
matraStart = ci;
|
|
2240
|
+
break;
|
|
2241
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
2242
|
+
emitGlyph(resolveGid(cp), false);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
const ligResult = tryLig(clusterGids);
|
|
2246
|
+
if (ligResult) {
|
|
2247
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2248
|
+
baseGid = ligResult.resultGid;
|
|
2249
|
+
let gi = ligResult.consumed;
|
|
2250
|
+
while (gi < clusterGids.length) {
|
|
2251
|
+
const subSeq = clusterGids.slice(gi);
|
|
2252
|
+
const subLig = tryLig(subSeq);
|
|
2253
|
+
if (subLig) {
|
|
2254
|
+
emitGlyph(subLig.resultGid, false);
|
|
2255
|
+
gi += subLig.consumed;
|
|
2256
|
+
} else {
|
|
2257
|
+
const origCi = clusterEndIdx[gi];
|
|
2258
|
+
const ct = sinhalaCharType(codepoints[origCi]);
|
|
2259
|
+
if (ct === 7) emitGlyph(clusterGids[gi], true, baseGid);
|
|
2260
|
+
else emitGlyph(clusterGids[gi], false);
|
|
2261
|
+
gi++;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
} else {
|
|
2265
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
2266
|
+
const cp = codepoints[ci];
|
|
2267
|
+
const ct = sinhalaCharType(cp);
|
|
2268
|
+
if (ct === 0) emitGlyph(resolveGid(cp), false);
|
|
2269
|
+
else if (ct === 7) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2270
|
+
else ;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
2274
|
+
const cp = codepoints[ci];
|
|
2275
|
+
const ct = sinhalaCharType(cp);
|
|
2276
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2277
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2278
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
return shaped;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
// src/shaping/tibetan-shaper.ts
|
|
2285
|
+
function tibetanCharType(cp) {
|
|
2286
|
+
if (cp >= 3984 && cp <= 4028) return 8;
|
|
2287
|
+
if (cp === 3972) return 6;
|
|
2288
|
+
if (cp >= 3904 && cp <= 3948) return 0;
|
|
2289
|
+
if (cp === 3954 || cp === 3962 || cp === 3963 || cp === 3964 || cp === 3965 || cp === 3968 || cp === 3969 || cp === 3971 || cp === 3970 || cp === 3966) return 2;
|
|
2290
|
+
if (cp === 3953 || cp === 3956 || cp === 3955 || cp === 3957 || cp === 3864 || cp === 3865 || cp === 3893 || cp === 3895 || cp === 3897) return 3;
|
|
2291
|
+
if (cp === 3967) return 5;
|
|
2292
|
+
if (cp === 3840 || cp >= 3976 && cp <= 3980) return 1;
|
|
2293
|
+
if (cp >= 3872 && cp <= 3891) return 9;
|
|
2294
|
+
if (cp >= 3841 && cp <= 3863) return 9;
|
|
2295
|
+
if (cp >= 3898 && cp <= 3903) return 9;
|
|
2296
|
+
if (cp >= 4030 && cp <= 4047) return 9;
|
|
2297
|
+
if (cp === 3892 || cp === 3894 || cp === 3896) return 9;
|
|
2298
|
+
if (cp === 3973) return 9;
|
|
2299
|
+
return -1;
|
|
2300
|
+
}
|
|
2301
|
+
function buildTibetanStacks(str) {
|
|
2302
|
+
const stacks = [];
|
|
2303
|
+
const cps = [];
|
|
2304
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
2305
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
2306
|
+
cps.push(cp);
|
|
2307
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
2308
|
+
}
|
|
2309
|
+
let i = 0;
|
|
2310
|
+
while (i < cps.length) {
|
|
2311
|
+
const cp = cps[i];
|
|
2312
|
+
const type = tibetanCharType(cp);
|
|
2313
|
+
if (type < 0 || cp < TIBETAN_START || cp > TIBETAN_END) {
|
|
2314
|
+
stacks.push({ codepoints: [cp] });
|
|
2315
|
+
i++;
|
|
2316
|
+
continue;
|
|
2317
|
+
}
|
|
2318
|
+
if (type !== 0 && type !== 1) {
|
|
2319
|
+
stacks.push({ codepoints: [cp] });
|
|
2320
|
+
i++;
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
const stack = [cp];
|
|
2324
|
+
i++;
|
|
2325
|
+
while (i < cps.length && tibetanCharType(cps[i]) === 8) {
|
|
2326
|
+
stack.push(cps[i]);
|
|
2327
|
+
i++;
|
|
2328
|
+
}
|
|
2329
|
+
while (i < cps.length) {
|
|
2330
|
+
const ct = tibetanCharType(cps[i]);
|
|
2331
|
+
if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
|
|
2332
|
+
stack.push(cps[i]);
|
|
2333
|
+
i++;
|
|
2334
|
+
} else {
|
|
2335
|
+
break;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
stacks.push({ codepoints: stack });
|
|
2339
|
+
}
|
|
2340
|
+
return stacks;
|
|
2341
|
+
}
|
|
2342
|
+
function shapeTibetanText(str, fontData) {
|
|
2343
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
2344
|
+
const shaped = [];
|
|
2345
|
+
function resolveGid(cp) {
|
|
2346
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
2347
|
+
return cmap[normCp] || 0;
|
|
2348
|
+
}
|
|
2349
|
+
function tryLig(gids) {
|
|
2350
|
+
return tryLigature(gids, ligatures);
|
|
2351
|
+
}
|
|
2352
|
+
function getAdv(gid) {
|
|
2353
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
2354
|
+
}
|
|
2355
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
2356
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
2357
|
+
if (!base) return null;
|
|
2358
|
+
return base[markClass] ?? null;
|
|
2359
|
+
}
|
|
2360
|
+
function getMarkAnchor2(markGid) {
|
|
2361
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
2362
|
+
if (!mark) return null;
|
|
2363
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
2364
|
+
}
|
|
2365
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
2366
|
+
if (isZero && baseGid !== void 0) {
|
|
2367
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
2368
|
+
if (markAnchor) {
|
|
2369
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
2370
|
+
if (baseAnchorPt) {
|
|
2371
|
+
const baseAdv = getAdv(baseGid);
|
|
2372
|
+
shaped.push({
|
|
2373
|
+
gid,
|
|
2374
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
2375
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
2376
|
+
isZeroAdvance: true
|
|
2377
|
+
});
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
2382
|
+
} else {
|
|
2383
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
const stacks = buildTibetanStacks(str);
|
|
2387
|
+
for (const stack of stacks) {
|
|
2388
|
+
const { codepoints } = stack;
|
|
2389
|
+
const stackGids = [];
|
|
2390
|
+
const stackEndIdx = [];
|
|
2391
|
+
let markStart = codepoints.length;
|
|
2392
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2393
|
+
const ct = tibetanCharType(codepoints[ci]);
|
|
2394
|
+
if (ct === 0 || ct === 8 || ct === 1 || ct === 6) {
|
|
2395
|
+
stackGids.push(resolveGid(codepoints[ci]));
|
|
2396
|
+
stackEndIdx.push(ci);
|
|
2397
|
+
} else if (ct === 2 || ct === 3 || ct === 5) {
|
|
2398
|
+
markStart = ci;
|
|
2399
|
+
break;
|
|
2400
|
+
} else if (ct < 0 || ct === 9) {
|
|
2401
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
let baseGid = stackGids.length > 0 ? stackGids[0] : 0;
|
|
2405
|
+
const ligResult = tryLig(stackGids);
|
|
2406
|
+
if (ligResult) {
|
|
2407
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2408
|
+
baseGid = ligResult.resultGid;
|
|
2409
|
+
let gi = ligResult.consumed;
|
|
2410
|
+
while (gi < stackGids.length) {
|
|
2411
|
+
const subSeq = stackGids.slice(gi);
|
|
2412
|
+
const subLig = tryLig(subSeq);
|
|
2413
|
+
if (subLig) {
|
|
2414
|
+
emitGlyph(subLig.resultGid, false);
|
|
2415
|
+
gi += subLig.consumed;
|
|
2416
|
+
} else {
|
|
2417
|
+
const origCi = stackEndIdx[gi];
|
|
2418
|
+
const ct = tibetanCharType(codepoints[origCi]);
|
|
2419
|
+
if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
|
|
2420
|
+
else emitGlyph(stackGids[gi], false);
|
|
2421
|
+
gi++;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
} else {
|
|
2425
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
2426
|
+
const origCi = stackEndIdx[gi];
|
|
2427
|
+
const ct = tibetanCharType(codepoints[origCi]);
|
|
2428
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
2429
|
+
else if (ct === 8 || ct === 6) emitGlyph(stackGids[gi], true, baseGid);
|
|
2430
|
+
else emitGlyph(stackGids[gi], false);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
2434
|
+
const cp = codepoints[ci];
|
|
2435
|
+
const ct = tibetanCharType(cp);
|
|
2436
|
+
if (ct === 2 || ct === 3) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2437
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2438
|
+
else emitGlyph(resolveGid(cp), false);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
return shaped;
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
// src/shaping/khmer-shaper.ts
|
|
2445
|
+
var COENG = KHMER_COENG;
|
|
2446
|
+
var TWO_PART_VOWELS2 = {
|
|
2447
|
+
6078: [6081, 6070],
|
|
2448
|
+
// ើ
|
|
2449
|
+
6079: [6081, 6072],
|
|
2450
|
+
// ឿ (approx: e + y-like)
|
|
2451
|
+
6080: [6081, 6073],
|
|
2452
|
+
// ៀ (approx)
|
|
2453
|
+
6084: [6081, 6070],
|
|
2454
|
+
// ោ (e + aa)
|
|
2455
|
+
6085: [6081, 6070]
|
|
2456
|
+
// ៅ (e + aa-like)
|
|
2457
|
+
};
|
|
2458
|
+
function khmerCharType(cp) {
|
|
2459
|
+
if (cp === COENG) return 7;
|
|
2460
|
+
if (cp >= 6016 && cp <= 6050) return 0;
|
|
2461
|
+
if (cp >= 6051 && cp <= 6069) return 1;
|
|
2462
|
+
if (cp === 6081 || cp === 6082 || cp === 6083) return 4;
|
|
2463
|
+
if (cp === 6071 || cp === 6072 || cp === 6073 || cp === 6074 || cp === 6075 || cp === 6077) return 2;
|
|
2464
|
+
if (cp === 6076) return 3;
|
|
2465
|
+
if (cp === 6070 || cp === 6084 || cp === 6085) return 5;
|
|
2466
|
+
if (cp >= 6086 && cp <= 6097) return 6;
|
|
2467
|
+
if (cp === 6091 || cp === 6093 || cp === 6094 || cp === 6095 || cp === 6096) return 6;
|
|
2468
|
+
if (cp === 6109) return 6;
|
|
2469
|
+
if (cp >= 6112 && cp <= 6121) return 9;
|
|
2470
|
+
if (cp >= 6100 && cp <= 6108) return 9;
|
|
2471
|
+
if (cp >= 6128 && cp <= 6137) return 9;
|
|
2472
|
+
return -1;
|
|
2473
|
+
}
|
|
2474
|
+
function isConsonant5(cp) {
|
|
2475
|
+
return khmerCharType(cp) === 0;
|
|
2476
|
+
}
|
|
2477
|
+
function buildKhmerClusters(str) {
|
|
2478
|
+
const clusters = [];
|
|
2479
|
+
const cps = [];
|
|
2480
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
2481
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
2482
|
+
const decomp = TWO_PART_VOWELS2[cp];
|
|
2483
|
+
if (decomp) {
|
|
2484
|
+
for (const d of decomp) cps.push(d);
|
|
2485
|
+
} else cps.push(cp);
|
|
2486
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
2487
|
+
}
|
|
2488
|
+
let i = 0;
|
|
2489
|
+
while (i < cps.length) {
|
|
2490
|
+
const cp = cps[i];
|
|
2491
|
+
const type = khmerCharType(cp);
|
|
2492
|
+
if (type < 0 || cp < KHMER_START || cp > KHMER_END) {
|
|
2493
|
+
clusters.push({ codepoints: [cp] });
|
|
2494
|
+
i++;
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
if (type !== 0 && type !== 1) {
|
|
2498
|
+
clusters.push({ codepoints: [cp] });
|
|
2499
|
+
i++;
|
|
2500
|
+
continue;
|
|
2501
|
+
}
|
|
2502
|
+
const cluster = [cp];
|
|
2503
|
+
i++;
|
|
2504
|
+
while (i < cps.length && cps[i] === COENG) {
|
|
2505
|
+
if (i + 1 < cps.length && isConsonant5(cps[i + 1])) {
|
|
2506
|
+
cluster.push(cps[i]);
|
|
2507
|
+
cluster.push(cps[i + 1]);
|
|
2508
|
+
i += 2;
|
|
2509
|
+
} else {
|
|
2510
|
+
cluster.push(cps[i]);
|
|
2511
|
+
i++;
|
|
2512
|
+
break;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
while (i < cps.length) {
|
|
2516
|
+
const ct = khmerCharType(cps[i]);
|
|
2517
|
+
if (ct >= 2 && ct <= 6) {
|
|
2518
|
+
cluster.push(cps[i]);
|
|
2519
|
+
i++;
|
|
2520
|
+
} else break;
|
|
2521
|
+
}
|
|
2522
|
+
clusters.push({ codepoints: cluster });
|
|
2523
|
+
}
|
|
2524
|
+
return clusters;
|
|
2525
|
+
}
|
|
2526
|
+
function shapeKhmerText(str, fontData) {
|
|
2527
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
2528
|
+
const shaped = [];
|
|
2529
|
+
function resolveGid(cp) {
|
|
2530
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
2531
|
+
return cmap[normCp] || 0;
|
|
2532
|
+
}
|
|
2533
|
+
function tryLig(gids) {
|
|
2534
|
+
return tryLigature(gids, ligatures);
|
|
2535
|
+
}
|
|
2536
|
+
function getAdv(gid) {
|
|
2537
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
2538
|
+
}
|
|
2539
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
2540
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
2541
|
+
if (!base) return null;
|
|
2542
|
+
return base[markClass] ?? null;
|
|
2543
|
+
}
|
|
2544
|
+
function getMarkAnchor2(markGid) {
|
|
2545
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
2546
|
+
if (!mark) return null;
|
|
2547
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
2548
|
+
}
|
|
2549
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
2550
|
+
if (isZero && baseGid !== void 0) {
|
|
2551
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
2552
|
+
if (markAnchor) {
|
|
2553
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
2554
|
+
if (baseAnchorPt) {
|
|
2555
|
+
const baseAdv = getAdv(baseGid);
|
|
2556
|
+
shaped.push({
|
|
2557
|
+
gid,
|
|
2558
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
2559
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
2560
|
+
isZeroAdvance: true
|
|
2561
|
+
});
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
2566
|
+
} else {
|
|
2567
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
const clusters = buildKhmerClusters(str);
|
|
2571
|
+
for (const cluster of clusters) {
|
|
2572
|
+
const { codepoints } = cluster;
|
|
2573
|
+
let baseGid = 0;
|
|
2574
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2575
|
+
const ct = khmerCharType(codepoints[ci]);
|
|
2576
|
+
if (ct === 0 || ct === 1) {
|
|
2577
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
2578
|
+
break;
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2582
|
+
if (khmerCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
|
|
2583
|
+
}
|
|
2584
|
+
const stackGids = [];
|
|
2585
|
+
const stackEndIdx = [];
|
|
2586
|
+
let markStart = codepoints.length;
|
|
2587
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2588
|
+
const cp = codepoints[ci];
|
|
2589
|
+
const ct = khmerCharType(cp);
|
|
2590
|
+
if (ct === 0 || ct === 1 || ct === 7) {
|
|
2591
|
+
stackGids.push(resolveGid(cp));
|
|
2592
|
+
stackEndIdx.push(ci);
|
|
2593
|
+
} else if (ct >= 2 && ct <= 6) {
|
|
2594
|
+
markStart = ci;
|
|
2595
|
+
break;
|
|
2596
|
+
} else {
|
|
2597
|
+
emitGlyph(resolveGid(cp), false);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
const ligResult = tryLig(stackGids);
|
|
2601
|
+
if (ligResult) {
|
|
2602
|
+
emitGlyph(ligResult.resultGid, false);
|
|
2603
|
+
baseGid = ligResult.resultGid;
|
|
2604
|
+
let gi = ligResult.consumed;
|
|
2605
|
+
while (gi < stackGids.length) {
|
|
2606
|
+
const subLig = tryLig(stackGids.slice(gi));
|
|
2607
|
+
if (subLig) {
|
|
2608
|
+
emitGlyph(subLig.resultGid, false);
|
|
2609
|
+
gi += subLig.consumed;
|
|
2610
|
+
} else {
|
|
2611
|
+
const origCi = stackEndIdx[gi];
|
|
2612
|
+
const ct = khmerCharType(codepoints[origCi]);
|
|
2613
|
+
if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
|
|
2614
|
+
else emitGlyph(stackGids[gi], false);
|
|
2615
|
+
gi++;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
} else {
|
|
2619
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
2620
|
+
const origCi = stackEndIdx[gi];
|
|
2621
|
+
const ct = khmerCharType(codepoints[origCi]);
|
|
2622
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
2623
|
+
else if (ct === 7) emitGlyph(stackGids[gi], true, baseGid);
|
|
2624
|
+
else emitGlyph(stackGids[gi], true, baseGid);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
2628
|
+
const cp = codepoints[ci];
|
|
2629
|
+
const ct = khmerCharType(cp);
|
|
2630
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2631
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2632
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
return shaped;
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
// src/shaping/myanmar-shaper.ts
|
|
2639
|
+
var VIRAMA3 = MYANMAR_VIRAMA;
|
|
2640
|
+
var MEDIAL_RA = 4156;
|
|
2641
|
+
function myanmarCharType(cp) {
|
|
2642
|
+
if (cp === VIRAMA3) return 7;
|
|
2643
|
+
if (cp === MEDIAL_RA) return 4;
|
|
2644
|
+
if (cp === 4155 || cp === 4157 || cp === 4158) return 8;
|
|
2645
|
+
if (cp >= 4096 && cp <= 4138) return 0;
|
|
2646
|
+
if (cp === 4159) return 0;
|
|
2647
|
+
if (cp >= 4176 && cp <= 4181) return 0;
|
|
2648
|
+
if (cp === 4141 || cp === 4142 || cp === 4146 || cp === 4150 || cp === 4147 || cp === 4148 || cp === 4149) return 2;
|
|
2649
|
+
if (cp === 4143 || cp === 4144 || cp === 4151) return 3;
|
|
2650
|
+
if (cp === 4139 || cp === 4140) return 5;
|
|
2651
|
+
if (cp === 4145) return 4;
|
|
2652
|
+
if (cp === 4154) return 6;
|
|
2653
|
+
if (cp === 4152) return 5;
|
|
2654
|
+
if (cp >= 4160 && cp <= 4169) return 9;
|
|
2655
|
+
if (cp >= 4240 && cp <= 4249) return 9;
|
|
2656
|
+
if (cp === 4170 || cp === 4171) return 9;
|
|
2657
|
+
if (cp >= 4172 && cp <= 4175) return 9;
|
|
2658
|
+
return -1;
|
|
2659
|
+
}
|
|
2660
|
+
function isConsonant6(cp) {
|
|
2661
|
+
return myanmarCharType(cp) === 0;
|
|
2662
|
+
}
|
|
2663
|
+
function buildMyanmarClusters(str) {
|
|
2664
|
+
const clusters = [];
|
|
2665
|
+
const cps = [];
|
|
2666
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
2667
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
2668
|
+
cps.push(cp);
|
|
2669
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
2670
|
+
}
|
|
2671
|
+
let i = 0;
|
|
2672
|
+
while (i < cps.length) {
|
|
2673
|
+
const cp = cps[i];
|
|
2674
|
+
const type = myanmarCharType(cp);
|
|
2675
|
+
if (type < 0 || cp < MYANMAR_START || cp > MYANMAR_END) {
|
|
2676
|
+
clusters.push({ codepoints: [cp] });
|
|
2677
|
+
i++;
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
if (type !== 0) {
|
|
2681
|
+
clusters.push({ codepoints: [cp] });
|
|
2682
|
+
i++;
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
2685
|
+
const cluster = [cp];
|
|
2686
|
+
i++;
|
|
2687
|
+
while (i < cps.length && cps[i] === VIRAMA3) {
|
|
2688
|
+
if (i + 1 < cps.length && isConsonant6(cps[i + 1])) {
|
|
2689
|
+
cluster.push(cps[i]);
|
|
2690
|
+
cluster.push(cps[i + 1]);
|
|
2691
|
+
i += 2;
|
|
2692
|
+
} else {
|
|
2693
|
+
cluster.push(cps[i]);
|
|
2694
|
+
i++;
|
|
2695
|
+
break;
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
while (i < cps.length && myanmarCharType(cps[i]) === 8) {
|
|
2699
|
+
cluster.push(cps[i]);
|
|
2700
|
+
i++;
|
|
2701
|
+
}
|
|
2702
|
+
while (i < cps.length) {
|
|
2703
|
+
const ct = myanmarCharType(cps[i]);
|
|
2704
|
+
if (ct >= 2 && ct <= 6) {
|
|
2705
|
+
cluster.push(cps[i]);
|
|
2706
|
+
i++;
|
|
2707
|
+
} else break;
|
|
2708
|
+
}
|
|
2709
|
+
clusters.push({ codepoints: cluster });
|
|
2710
|
+
}
|
|
2711
|
+
return clusters;
|
|
2712
|
+
}
|
|
2713
|
+
function shapeMyanmarText(str, fontData) {
|
|
2714
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
2715
|
+
const shaped = [];
|
|
2716
|
+
function resolveGid(cp) {
|
|
2717
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
2718
|
+
return cmap[normCp] || 0;
|
|
2719
|
+
}
|
|
2720
|
+
function tryLig(gids) {
|
|
2721
|
+
return tryLigature(gids, ligatures);
|
|
2722
|
+
}
|
|
2723
|
+
function getAdv(gid) {
|
|
2724
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
2725
|
+
}
|
|
2726
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
2727
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
2728
|
+
if (!base) return null;
|
|
2729
|
+
return base[markClass] ?? null;
|
|
2730
|
+
}
|
|
2731
|
+
function getMarkAnchor2(markGid) {
|
|
2732
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
2733
|
+
if (!mark) return null;
|
|
2734
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
2735
|
+
}
|
|
2736
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
2737
|
+
if (isZero && baseGid !== void 0) {
|
|
2738
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
2739
|
+
if (markAnchor) {
|
|
2740
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
2741
|
+
if (baseAnchorPt) {
|
|
2742
|
+
const baseAdv = getAdv(baseGid);
|
|
2743
|
+
shaped.push({
|
|
2744
|
+
gid,
|
|
2745
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
2746
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
2747
|
+
isZeroAdvance: true
|
|
2748
|
+
});
|
|
2749
|
+
return;
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
2753
|
+
} else {
|
|
2754
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
const clusters = buildMyanmarClusters(str);
|
|
2758
|
+
for (const cluster of clusters) {
|
|
2759
|
+
const { codepoints } = cluster;
|
|
2760
|
+
let baseGid = 0;
|
|
1407
2761
|
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1411
|
-
clusterEndIdx.push(ci);
|
|
1412
|
-
} else if (ct >= 2) {
|
|
1413
|
-
matraStart = ci;
|
|
2762
|
+
if (myanmarCharType(codepoints[ci]) === 0) {
|
|
2763
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
1414
2764
|
break;
|
|
1415
|
-
} else if (ct < 0) {
|
|
1416
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1417
|
-
} else if (ct === 1) {
|
|
1418
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1419
2765
|
}
|
|
1420
2766
|
}
|
|
1421
|
-
let
|
|
1422
|
-
|
|
2767
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2768
|
+
if (myanmarCharType(codepoints[ci]) === 4) emitGlyph(resolveGid(codepoints[ci]), false);
|
|
2769
|
+
}
|
|
2770
|
+
const stackGids = [];
|
|
2771
|
+
const stackEndIdx = [];
|
|
2772
|
+
let markStart = codepoints.length;
|
|
2773
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
2774
|
+
const cp = codepoints[ci];
|
|
2775
|
+
const ct = myanmarCharType(cp);
|
|
2776
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
2777
|
+
stackGids.push(resolveGid(cp));
|
|
2778
|
+
stackEndIdx.push(ci);
|
|
2779
|
+
} else if (ct === 2 || ct === 3 || ct === 5 || ct === 6) {
|
|
2780
|
+
markStart = ci;
|
|
2781
|
+
break;
|
|
2782
|
+
} else if (ct === 4) ; else {
|
|
2783
|
+
emitGlyph(resolveGid(cp), false);
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
const ligResult = tryLig(stackGids);
|
|
1423
2787
|
if (ligResult) {
|
|
1424
2788
|
emitGlyph(ligResult.resultGid, false);
|
|
1425
2789
|
baseGid = ligResult.resultGid;
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
const subSeq = clusterGids.slice(gi);
|
|
1430
|
-
const subLig = tryLig(subSeq);
|
|
2790
|
+
let gi = ligResult.consumed;
|
|
2791
|
+
while (gi < stackGids.length) {
|
|
2792
|
+
const subLig = tryLig(stackGids.slice(gi));
|
|
1431
2793
|
if (subLig) {
|
|
1432
2794
|
emitGlyph(subLig.resultGid, false);
|
|
1433
2795
|
gi += subLig.consumed;
|
|
1434
2796
|
} else {
|
|
1435
|
-
const origCi =
|
|
1436
|
-
const ct =
|
|
1437
|
-
if (ct === 7)
|
|
1438
|
-
|
|
1439
|
-
} else {
|
|
1440
|
-
emitGlyph(clusterGids[gi], false);
|
|
1441
|
-
}
|
|
2797
|
+
const origCi = stackEndIdx[gi];
|
|
2798
|
+
const ct = myanmarCharType(codepoints[origCi]);
|
|
2799
|
+
if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
|
|
2800
|
+
else emitGlyph(stackGids[gi], false);
|
|
1442
2801
|
gi++;
|
|
1443
2802
|
}
|
|
1444
2803
|
}
|
|
1445
2804
|
} else {
|
|
1446
|
-
for (let
|
|
1447
|
-
const
|
|
1448
|
-
const ct =
|
|
1449
|
-
if (
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1453
|
-
}
|
|
2805
|
+
for (let gi = 0; gi < stackGids.length; gi++) {
|
|
2806
|
+
const origCi = stackEndIdx[gi];
|
|
2807
|
+
const ct = myanmarCharType(codepoints[origCi]);
|
|
2808
|
+
if (gi === 0) emitGlyph(stackGids[gi], false);
|
|
2809
|
+
else if (ct === 7 || ct === 8) emitGlyph(stackGids[gi], true, baseGid);
|
|
2810
|
+
else emitGlyph(stackGids[gi], false);
|
|
1454
2811
|
}
|
|
1455
2812
|
}
|
|
1456
|
-
for (let ci =
|
|
2813
|
+
for (let ci = markStart; ci < codepoints.length; ci++) {
|
|
1457
2814
|
const cp = codepoints[ci];
|
|
1458
|
-
const ct =
|
|
1459
|
-
if (ct ===
|
|
1460
|
-
if (ct ===
|
|
1461
|
-
|
|
1462
|
-
} else if (ct === 5) {
|
|
1463
|
-
emitGlyph(resolveGid(cp), false);
|
|
1464
|
-
} else if (ct === 6) {
|
|
1465
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1466
|
-
} else if (ct === 9) {
|
|
1467
|
-
emitGlyph(resolveGid(cp), false);
|
|
1468
|
-
} else {
|
|
1469
|
-
emitGlyph(resolveGid(cp), false);
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
for (const postCp of splitPostComponents) {
|
|
1473
|
-
emitGlyph(resolveGid(postCp), false);
|
|
2815
|
+
const ct = myanmarCharType(cp);
|
|
2816
|
+
if (ct === 2 || ct === 3 || ct === 6) emitGlyph(resolveGid(cp), true, baseGid);
|
|
2817
|
+
else if (ct === 5) emitGlyph(resolveGid(cp), false);
|
|
2818
|
+
else if (ct === 4) ; else emitGlyph(resolveGid(cp), false);
|
|
1474
2819
|
}
|
|
1475
2820
|
}
|
|
1476
2821
|
return shaped;
|
|
@@ -1522,7 +2867,7 @@ function devanagariCharType(cp) {
|
|
|
1522
2867
|
if (cp === 2384) return 1;
|
|
1523
2868
|
return -1;
|
|
1524
2869
|
}
|
|
1525
|
-
function
|
|
2870
|
+
function isConsonant7(cp) {
|
|
1526
2871
|
return devanagariCharType(cp) === 0;
|
|
1527
2872
|
}
|
|
1528
2873
|
function buildDevanagariClusters(str) {
|
|
@@ -1537,6 +2882,10 @@ function buildDevanagariClusters(str) {
|
|
|
1537
2882
|
while (i < cps.length) {
|
|
1538
2883
|
const cp = cps[i];
|
|
1539
2884
|
const type = devanagariCharType(cp);
|
|
2885
|
+
if (classifyUseCategory(cp) === "ZWJ" || classifyUseCategory(cp) === "ZWNJ") {
|
|
2886
|
+
i++;
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
1540
2889
|
if (type < 0 || cp < DEVANAGARI_START || cp > DEVANAGARI_END) {
|
|
1541
2890
|
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
1542
2891
|
i++;
|
|
@@ -1545,7 +2894,7 @@ function buildDevanagariClusters(str) {
|
|
|
1545
2894
|
const syllable = [];
|
|
1546
2895
|
let hasReph = false;
|
|
1547
2896
|
const preMatras = [];
|
|
1548
|
-
if (
|
|
2897
|
+
if (isConsonant7(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant7(cps[i + 2])) {
|
|
1549
2898
|
hasReph = true;
|
|
1550
2899
|
syllable.push(cp, cps[i + 1]);
|
|
1551
2900
|
i += 2;
|
|
@@ -1563,13 +2912,24 @@ function buildDevanagariClusters(str) {
|
|
|
1563
2912
|
i++;
|
|
1564
2913
|
}
|
|
1565
2914
|
if (i < cps.length && cps[i] === HALANT2) {
|
|
1566
|
-
|
|
2915
|
+
let j = i + 1;
|
|
2916
|
+
let zwnj = false;
|
|
2917
|
+
if (j < cps.length) {
|
|
2918
|
+
const jc = classifyUseCategory(cps[j]);
|
|
2919
|
+
if (jc === "ZWJ") {
|
|
2920
|
+
j++;
|
|
2921
|
+
} else if (jc === "ZWNJ") {
|
|
2922
|
+
j++;
|
|
2923
|
+
zwnj = true;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
if (!zwnj && j < cps.length && isConsonant7(cps[j])) {
|
|
1567
2927
|
syllable.push(cps[i]);
|
|
1568
|
-
i
|
|
2928
|
+
i = j;
|
|
1569
2929
|
continue;
|
|
1570
2930
|
} else {
|
|
1571
2931
|
syllable.push(cps[i]);
|
|
1572
|
-
i
|
|
2932
|
+
i = j;
|
|
1573
2933
|
break;
|
|
1574
2934
|
}
|
|
1575
2935
|
}
|
|
@@ -1927,6 +3287,406 @@ function shapeArabicText(str, fontData) {
|
|
|
1927
3287
|
return glyphs;
|
|
1928
3288
|
}
|
|
1929
3289
|
|
|
3290
|
+
// src/fonts/font-loader.ts
|
|
3291
|
+
var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
|
|
3292
|
+
function getDecodedFontBytes(fontData) {
|
|
3293
|
+
const cached = _fontBinaryCache.get(fontData);
|
|
3294
|
+
if (cached) return cached;
|
|
3295
|
+
let bytes;
|
|
3296
|
+
if (typeof atob === "function") {
|
|
3297
|
+
const binaryStr = atob(fontData.ttfBase64);
|
|
3298
|
+
bytes = new Uint8Array(binaryStr.length);
|
|
3299
|
+
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
3300
|
+
} else {
|
|
3301
|
+
const buf = globalThis["Buffer"];
|
|
3302
|
+
bytes = buf.from(fontData.ttfBase64, "base64");
|
|
3303
|
+
}
|
|
3304
|
+
_fontBinaryCache.set(fontData, bytes);
|
|
3305
|
+
return bytes;
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
// src/fonts/glyf-outline.ts
|
|
3309
|
+
function readTableDirectory(view) {
|
|
3310
|
+
const numTables = view.getUint16(4);
|
|
3311
|
+
const tables = {};
|
|
3312
|
+
for (let i = 0; i < numTables; i++) {
|
|
3313
|
+
const rec = 12 + i * 16;
|
|
3314
|
+
const tag = String.fromCharCode(
|
|
3315
|
+
view.getUint8(rec),
|
|
3316
|
+
view.getUint8(rec + 1),
|
|
3317
|
+
view.getUint8(rec + 2),
|
|
3318
|
+
view.getUint8(rec + 3)
|
|
3319
|
+
);
|
|
3320
|
+
tables[tag] = { offset: view.getUint32(rec + 8), length: view.getUint32(rec + 12) };
|
|
3321
|
+
}
|
|
3322
|
+
return tables;
|
|
3323
|
+
}
|
|
3324
|
+
function parseGlyfFont(bytes) {
|
|
3325
|
+
if (bytes.length < 12) return null;
|
|
3326
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
3327
|
+
const tables = readTableDirectory(view);
|
|
3328
|
+
const head = tables["head"];
|
|
3329
|
+
const maxp = tables["maxp"];
|
|
3330
|
+
const loca = tables["loca"];
|
|
3331
|
+
const glyf = tables["glyf"];
|
|
3332
|
+
if (!head || !maxp || !loca || !glyf) return null;
|
|
3333
|
+
const unitsPerEm = view.getUint16(head.offset + 18) || 1e3;
|
|
3334
|
+
const locaFormat = view.getInt16(head.offset + 50);
|
|
3335
|
+
const numGlyphs = view.getUint16(maxp.offset + 4);
|
|
3336
|
+
const locaOffsets = new Array(numGlyphs + 1);
|
|
3337
|
+
for (let i = 0; i <= numGlyphs; i++) {
|
|
3338
|
+
locaOffsets[i] = locaFormat === 0 ? view.getUint16(loca.offset + i * 2) * 2 : view.getUint32(loca.offset + i * 4);
|
|
3339
|
+
}
|
|
3340
|
+
return { view, glyfOffset: glyf.offset, locaOffsets, unitsPerEm };
|
|
3341
|
+
}
|
|
3342
|
+
function transformPoint(p, a, b, c, d, e, f) {
|
|
3343
|
+
return {
|
|
3344
|
+
x: a * p.x + c * p.y + e,
|
|
3345
|
+
y: b * p.x + d * p.y + f,
|
|
3346
|
+
onCurve: p.onCurve
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
function readF2Dot14(view, pos) {
|
|
3350
|
+
return view.getInt16(pos) / 16384;
|
|
3351
|
+
}
|
|
3352
|
+
function extractGlyphContours(font, gid, depth = 0) {
|
|
3353
|
+
if (depth > 8) return [];
|
|
3354
|
+
const { view, glyfOffset, locaOffsets } = font;
|
|
3355
|
+
if (gid < 0 || gid + 1 >= locaOffsets.length) return [];
|
|
3356
|
+
const start = locaOffsets[gid];
|
|
3357
|
+
const end = locaOffsets[gid + 1];
|
|
3358
|
+
if (end <= start) return [];
|
|
3359
|
+
const base = glyfOffset + start;
|
|
3360
|
+
const numberOfContours = view.getInt16(base);
|
|
3361
|
+
if (numberOfContours < 0) {
|
|
3362
|
+
return extractCompositeContours(font, base, depth);
|
|
3363
|
+
}
|
|
3364
|
+
return extractSimpleContours(view, base, numberOfContours);
|
|
3365
|
+
}
|
|
3366
|
+
function extractSimpleContours(view, base, numberOfContours) {
|
|
3367
|
+
let pos = base + 10;
|
|
3368
|
+
const endPts = new Array(numberOfContours);
|
|
3369
|
+
for (let i = 0; i < numberOfContours; i++) {
|
|
3370
|
+
endPts[i] = view.getUint16(pos);
|
|
3371
|
+
pos += 2;
|
|
3372
|
+
}
|
|
3373
|
+
const numPoints = numberOfContours > 0 ? endPts[numberOfContours - 1] + 1 : 0;
|
|
3374
|
+
const instrLen = view.getUint16(pos);
|
|
3375
|
+
pos += 2 + instrLen;
|
|
3376
|
+
const flags = new Array(numPoints);
|
|
3377
|
+
for (let i = 0; i < numPoints; ) {
|
|
3378
|
+
const flag = view.getUint8(pos++);
|
|
3379
|
+
flags[i++] = flag;
|
|
3380
|
+
if (flag & 8) {
|
|
3381
|
+
let repeat = view.getUint8(pos++);
|
|
3382
|
+
while (repeat-- > 0 && i < numPoints) flags[i++] = flag;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
const xs = new Array(numPoints);
|
|
3386
|
+
let x = 0;
|
|
3387
|
+
for (let i = 0; i < numPoints; i++) {
|
|
3388
|
+
const flag = flags[i];
|
|
3389
|
+
if (flag & 2) {
|
|
3390
|
+
const dx = view.getUint8(pos++);
|
|
3391
|
+
x += flag & 16 ? dx : -dx;
|
|
3392
|
+
} else if (!(flag & 16)) {
|
|
3393
|
+
x += view.getInt16(pos);
|
|
3394
|
+
pos += 2;
|
|
3395
|
+
}
|
|
3396
|
+
xs[i] = x;
|
|
3397
|
+
}
|
|
3398
|
+
const ys = new Array(numPoints);
|
|
3399
|
+
let y = 0;
|
|
3400
|
+
for (let i = 0; i < numPoints; i++) {
|
|
3401
|
+
const flag = flags[i];
|
|
3402
|
+
if (flag & 4) {
|
|
3403
|
+
const dy = view.getUint8(pos++);
|
|
3404
|
+
y += flag & 32 ? dy : -dy;
|
|
3405
|
+
} else if (!(flag & 32)) {
|
|
3406
|
+
y += view.getInt16(pos);
|
|
3407
|
+
pos += 2;
|
|
3408
|
+
}
|
|
3409
|
+
ys[i] = y;
|
|
3410
|
+
}
|
|
3411
|
+
const contours = [];
|
|
3412
|
+
let startPt = 0;
|
|
3413
|
+
for (let c = 0; c < numberOfContours; c++) {
|
|
3414
|
+
const endPt = endPts[c];
|
|
3415
|
+
const contour = [];
|
|
3416
|
+
for (let i = startPt; i <= endPt; i++) {
|
|
3417
|
+
contour.push({ x: xs[i], y: ys[i], onCurve: (flags[i] & 1) !== 0 });
|
|
3418
|
+
}
|
|
3419
|
+
if (contour.length > 0) contours.push(contour);
|
|
3420
|
+
startPt = endPt + 1;
|
|
3421
|
+
}
|
|
3422
|
+
return contours;
|
|
3423
|
+
}
|
|
3424
|
+
function extractCompositeContours(font, base, depth) {
|
|
3425
|
+
const { view } = font;
|
|
3426
|
+
let pos = base + 10;
|
|
3427
|
+
const out = [];
|
|
3428
|
+
while (true) {
|
|
3429
|
+
const flags = view.getUint16(pos);
|
|
3430
|
+
pos += 2;
|
|
3431
|
+
const componentGid = view.getUint16(pos);
|
|
3432
|
+
pos += 2;
|
|
3433
|
+
let arg1;
|
|
3434
|
+
let arg2;
|
|
3435
|
+
if (flags & 1) {
|
|
3436
|
+
arg1 = view.getInt16(pos);
|
|
3437
|
+
pos += 2;
|
|
3438
|
+
arg2 = view.getInt16(pos);
|
|
3439
|
+
pos += 2;
|
|
3440
|
+
} else {
|
|
3441
|
+
arg1 = view.getInt8(pos);
|
|
3442
|
+
pos += 1;
|
|
3443
|
+
arg2 = view.getInt8(pos);
|
|
3444
|
+
pos += 1;
|
|
3445
|
+
}
|
|
3446
|
+
let a = 1, b = 0, c = 0, d = 1;
|
|
3447
|
+
if (flags & 8) {
|
|
3448
|
+
a = d = readF2Dot14(view, pos);
|
|
3449
|
+
pos += 2;
|
|
3450
|
+
} else if (flags & 64) {
|
|
3451
|
+
a = readF2Dot14(view, pos);
|
|
3452
|
+
pos += 2;
|
|
3453
|
+
d = readF2Dot14(view, pos);
|
|
3454
|
+
pos += 2;
|
|
3455
|
+
} else if (flags & 128) {
|
|
3456
|
+
a = readF2Dot14(view, pos);
|
|
3457
|
+
pos += 2;
|
|
3458
|
+
b = readF2Dot14(view, pos);
|
|
3459
|
+
pos += 2;
|
|
3460
|
+
c = readF2Dot14(view, pos);
|
|
3461
|
+
pos += 2;
|
|
3462
|
+
d = readF2Dot14(view, pos);
|
|
3463
|
+
pos += 2;
|
|
3464
|
+
}
|
|
3465
|
+
const e = flags & 2 ? arg1 : 0;
|
|
3466
|
+
const f = flags & 2 ? arg2 : 0;
|
|
3467
|
+
const sub = extractGlyphContours(font, componentGid, depth + 1);
|
|
3468
|
+
for (const contour of sub) {
|
|
3469
|
+
out.push(contour.map((p) => transformPoint(p, a, b, c, d, e, f)));
|
|
3470
|
+
}
|
|
3471
|
+
if (!(flags & 32)) break;
|
|
3472
|
+
}
|
|
3473
|
+
return out;
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
// src/core/pdf-color-glyph.ts
|
|
3477
|
+
var ID = [1, 0, 0, 1, 0, 0];
|
|
3478
|
+
function n(v) {
|
|
3479
|
+
if (!Number.isFinite(v)) return "0";
|
|
3480
|
+
if (Number.isInteger(v)) return String(v);
|
|
3481
|
+
let s = v.toFixed(3);
|
|
3482
|
+
s = s.replace(/0+$/, "").replace(/\.$/, "");
|
|
3483
|
+
return s === "-0" ? "0" : s;
|
|
3484
|
+
}
|
|
3485
|
+
function tx(m, x, y) {
|
|
3486
|
+
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
3487
|
+
}
|
|
3488
|
+
function ch(v) {
|
|
3489
|
+
return n(Math.max(0, Math.min(1, v / 255)));
|
|
3490
|
+
}
|
|
3491
|
+
function contoursToPath(contours, m = ID) {
|
|
3492
|
+
const ops = [];
|
|
3493
|
+
for (const contour of contours) {
|
|
3494
|
+
if (contour.length === 0) continue;
|
|
3495
|
+
const pts = contour.slice();
|
|
3496
|
+
let startIdx = pts.findIndex((p) => p.onCurve);
|
|
3497
|
+
let start;
|
|
3498
|
+
if (startIdx < 0) {
|
|
3499
|
+
const a = pts[0], b = pts[pts.length - 1];
|
|
3500
|
+
start = [(a.x + b.x) / 2, (a.y + b.y) / 2];
|
|
3501
|
+
startIdx = 0;
|
|
3502
|
+
} else {
|
|
3503
|
+
start = [pts[startIdx].x, pts[startIdx].y];
|
|
3504
|
+
}
|
|
3505
|
+
const [sx, sy] = tx(m, start[0], start[1]);
|
|
3506
|
+
ops.push(`${n(sx)} ${n(sy)} m`);
|
|
3507
|
+
const len = pts.length;
|
|
3508
|
+
let curX = start[0], curY = start[1];
|
|
3509
|
+
let i = 1;
|
|
3510
|
+
while (i <= len) {
|
|
3511
|
+
const p = pts[(startIdx + i) % len];
|
|
3512
|
+
if (p.onCurve) {
|
|
3513
|
+
const [px, py] = tx(m, p.x, p.y);
|
|
3514
|
+
ops.push(`${n(px)} ${n(py)} l`);
|
|
3515
|
+
curX = p.x;
|
|
3516
|
+
curY = p.y;
|
|
3517
|
+
i++;
|
|
3518
|
+
} else {
|
|
3519
|
+
const next = pts[(startIdx + i + 1) % len];
|
|
3520
|
+
let endX, endY;
|
|
3521
|
+
let consumed;
|
|
3522
|
+
if (next.onCurve) {
|
|
3523
|
+
endX = next.x;
|
|
3524
|
+
endY = next.y;
|
|
3525
|
+
consumed = 2;
|
|
3526
|
+
} else {
|
|
3527
|
+
endX = (p.x + next.x) / 2;
|
|
3528
|
+
endY = (p.y + next.y) / 2;
|
|
3529
|
+
consumed = 1;
|
|
3530
|
+
}
|
|
3531
|
+
const c1x = curX + 2 / 3 * (p.x - curX);
|
|
3532
|
+
const c1y = curY + 2 / 3 * (p.y - curY);
|
|
3533
|
+
const c2x = endX + 2 / 3 * (p.x - endX);
|
|
3534
|
+
const c2y = endY + 2 / 3 * (p.y - endY);
|
|
3535
|
+
const [a1, b1] = tx(m, c1x, c1y);
|
|
3536
|
+
const [a2, b2] = tx(m, c2x, c2y);
|
|
3537
|
+
const [ex, ey] = tx(m, endX, endY);
|
|
3538
|
+
ops.push(`${n(a1)} ${n(b1)} ${n(a2)} ${n(b2)} ${n(ex)} ${n(ey)} c`);
|
|
3539
|
+
curX = endX;
|
|
3540
|
+
curY = endY;
|
|
3541
|
+
i += consumed;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
ops.push("h");
|
|
3545
|
+
}
|
|
3546
|
+
return ops.join("\n");
|
|
3547
|
+
}
|
|
3548
|
+
function buildGradientFunction(stops) {
|
|
3549
|
+
const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
|
|
3550
|
+
if (sorted.length === 0) return "<< /FunctionType 2 /Domain [0 1] /C0 [0 0 0] /C1 [0 0 0] /N 1 >>";
|
|
3551
|
+
if (sorted.length === 1) {
|
|
3552
|
+
const c = sorted[0].color;
|
|
3553
|
+
return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /C1 [${ch(c[0])} ${ch(c[1])} ${ch(c[2])}] /N 1 >>`;
|
|
3554
|
+
}
|
|
3555
|
+
if (sorted.length === 2) {
|
|
3556
|
+
const a = sorted[0].color, b = sorted[1].color;
|
|
3557
|
+
return `<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`;
|
|
3558
|
+
}
|
|
3559
|
+
const subFns = [];
|
|
3560
|
+
const bounds = [];
|
|
3561
|
+
const encode = [];
|
|
3562
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
3563
|
+
const a = sorted[i].color, b = sorted[i + 1].color;
|
|
3564
|
+
subFns.push(`<< /FunctionType 2 /Domain [0 1] /C0 [${ch(a[0])} ${ch(a[1])} ${ch(a[2])}] /C1 [${ch(b[0])} ${ch(b[1])} ${ch(b[2])}] /N 1 >>`);
|
|
3565
|
+
encode.push("0 1");
|
|
3566
|
+
if (i > 0) bounds.push(n(Math.max(0, Math.min(1, sorted[i].offset))));
|
|
3567
|
+
}
|
|
3568
|
+
return `<< /FunctionType 3 /Domain [0 1] /Functions [${subFns.join(" ")}] /Bounds [${bounds.join(" ")}] /Encode [${encode.join(" ")}] >>`;
|
|
3569
|
+
}
|
|
3570
|
+
function extendFlags(extend) {
|
|
3571
|
+
return extend === "pad" ? "[true true]" : "[true true]";
|
|
3572
|
+
}
|
|
3573
|
+
function linearShadingDict(p, m) {
|
|
3574
|
+
const [x0, y0] = tx(m, p.p0[0], p.p0[1]);
|
|
3575
|
+
const [x1, y1] = tx(m, p.p1[0], p.p1[1]);
|
|
3576
|
+
return `<< /ShadingType 2 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(x1)} ${n(y1)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
|
|
3577
|
+
}
|
|
3578
|
+
function radialShadingDict(p, m) {
|
|
3579
|
+
const [x0, y0] = tx(m, p.c0[0], p.c0[1]);
|
|
3580
|
+
const [x1, y1] = tx(m, p.c1[0], p.c1[1]);
|
|
3581
|
+
const sx = Math.hypot(m[0], m[1]);
|
|
3582
|
+
const sy = Math.hypot(m[2], m[3]);
|
|
3583
|
+
const s = (sx + sy) / 2 || 1;
|
|
3584
|
+
return `<< /ShadingType 3 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(p.r0 * s)} ${n(x1)} ${n(y1)} ${n(p.r1 * s)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
|
|
3585
|
+
}
|
|
3586
|
+
function renderColorGlyph(glyph, outlines, unitsPerEm) {
|
|
3587
|
+
const body = [];
|
|
3588
|
+
const shadings = [];
|
|
3589
|
+
const extGStates = [];
|
|
3590
|
+
const alphaMap = /* @__PURE__ */ new Map();
|
|
3591
|
+
let shadingIdx = 0;
|
|
3592
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
3593
|
+
for (const layer of glyph.layers) {
|
|
3594
|
+
const m = layer.transform ?? ID;
|
|
3595
|
+
const contours = outlines(layer.glyphId);
|
|
3596
|
+
if (contours.length === 0) continue;
|
|
3597
|
+
const path = contoursToPath(contours, m);
|
|
3598
|
+
for (const contour of contours) {
|
|
3599
|
+
for (const pt of contour) {
|
|
3600
|
+
const [px, py] = tx(m, pt.x, pt.y);
|
|
3601
|
+
if (px < minX) minX = px;
|
|
3602
|
+
if (py < minY) minY = py;
|
|
3603
|
+
if (px > maxX) maxX = px;
|
|
3604
|
+
if (py > maxY) maxY = py;
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
if (layer.paint.kind === "solid") {
|
|
3608
|
+
const c = layer.paint.color;
|
|
3609
|
+
const alpha = c[3] / 255;
|
|
3610
|
+
body.push("q");
|
|
3611
|
+
if (alpha < 0.999) {
|
|
3612
|
+
let gs = alphaMap.get(c[3]);
|
|
3613
|
+
if (!gs) {
|
|
3614
|
+
gs = `GsA${alphaMap.size}`;
|
|
3615
|
+
alphaMap.set(c[3], gs);
|
|
3616
|
+
extGStates.push({ name: gs, dict: `<< /ca ${n(alpha)} /CA ${n(alpha)} >>` });
|
|
3617
|
+
}
|
|
3618
|
+
body.push(`/${gs} gs`);
|
|
3619
|
+
}
|
|
3620
|
+
body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
|
|
3621
|
+
body.push(path);
|
|
3622
|
+
body.push("f");
|
|
3623
|
+
body.push("Q");
|
|
3624
|
+
} else {
|
|
3625
|
+
const name = `Sh${shadingIdx++}`;
|
|
3626
|
+
const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
|
|
3627
|
+
shadings.push({ name, dict });
|
|
3628
|
+
body.push("q");
|
|
3629
|
+
body.push(path);
|
|
3630
|
+
body.push("W n");
|
|
3631
|
+
body.push(`/${name} sh`);
|
|
3632
|
+
body.push("Q");
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
const bbox = Number.isFinite(minX) ? [Math.floor(minX) - 1, Math.floor(minY) - 1, Math.ceil(maxX) + 1, Math.ceil(maxY) + 1] : [0, 0, unitsPerEm, unitsPerEm];
|
|
3636
|
+
return {
|
|
3637
|
+
content: body.join("\n"),
|
|
3638
|
+
bbox,
|
|
3639
|
+
shadings,
|
|
3640
|
+
extGStates
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
// src/core/color-emoji.ts
|
|
3645
|
+
function createColorEmojiCollector() {
|
|
3646
|
+
const forms = [];
|
|
3647
|
+
const nameByGlyph = /* @__PURE__ */ new WeakMap();
|
|
3648
|
+
const glyfByFont = /* @__PURE__ */ new WeakMap();
|
|
3649
|
+
function glyfFor(fontData) {
|
|
3650
|
+
let g = glyfByFont.get(fontData);
|
|
3651
|
+
if (g === void 0) {
|
|
3652
|
+
g = parseGlyfFont(getDecodedFontBytes(fontData));
|
|
3653
|
+
glyfByFont.set(fontData, g);
|
|
3654
|
+
}
|
|
3655
|
+
return g;
|
|
3656
|
+
}
|
|
3657
|
+
function useGlyph(fontData, gid) {
|
|
3658
|
+
const colorGlyph = fontData.colorGlyphs?.[gid];
|
|
3659
|
+
if (!colorGlyph) return null;
|
|
3660
|
+
let perFont = nameByGlyph.get(fontData);
|
|
3661
|
+
if (!perFont) {
|
|
3662
|
+
perFont = /* @__PURE__ */ new Map();
|
|
3663
|
+
nameByGlyph.set(fontData, perFont);
|
|
3664
|
+
}
|
|
3665
|
+
const cached = perFont.get(gid);
|
|
3666
|
+
if (cached) return cached;
|
|
3667
|
+
const glyf = glyfFor(fontData);
|
|
3668
|
+
if (!glyf) return null;
|
|
3669
|
+
const rendered = renderColorGlyph(
|
|
3670
|
+
colorGlyph,
|
|
3671
|
+
(baseGid) => extractGlyphContours(glyf, baseGid),
|
|
3672
|
+
fontData.metrics.unitsPerEm
|
|
3673
|
+
);
|
|
3674
|
+
if (rendered.content.trim() === "") return null;
|
|
3675
|
+
const name = `CEm${forms.length}`;
|
|
3676
|
+
const resParts = [];
|
|
3677
|
+
if (rendered.shadings.length > 0) {
|
|
3678
|
+
resParts.push(`/Shading << ${rendered.shadings.map((s) => `/${s.name} ${s.dict}`).join(" ")} >>`);
|
|
3679
|
+
}
|
|
3680
|
+
if (rendered.extGStates.length > 0) {
|
|
3681
|
+
resParts.push(`/ExtGState << ${rendered.extGStates.map((g) => `/${g.name} ${g.dict}`).join(" ")} >>`);
|
|
3682
|
+
}
|
|
3683
|
+
forms.push({ name, content: rendered.content, resources: resParts.join(" "), bbox: rendered.bbox });
|
|
3684
|
+
perFont.set(gid, name);
|
|
3685
|
+
return name;
|
|
3686
|
+
}
|
|
3687
|
+
return { useGlyph, forms };
|
|
3688
|
+
}
|
|
3689
|
+
|
|
1930
3690
|
// src/core/encoding-context.ts
|
|
1931
3691
|
function isWinAnsi(cp) {
|
|
1932
3692
|
if (cp >= 32 && cp <= 126 || cp >= 160 && cp <= 255) return true;
|
|
@@ -2031,13 +3791,14 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
|
|
|
2031
3791
|
if (mode === "hel") flushHel();
|
|
2032
3792
|
return result;
|
|
2033
3793
|
}
|
|
2034
|
-
function createEncodingContext(fontEntries, pdfA = false) {
|
|
3794
|
+
function createEncodingContext(fontEntries, pdfA = false, normalize = false) {
|
|
3795
|
+
const _norm = normalize ? (s) => s.normalize(normalize) : (s) => s;
|
|
2035
3796
|
if (!fontEntries || fontEntries.length === 0) {
|
|
2036
3797
|
return {
|
|
2037
3798
|
isUnicode: false,
|
|
2038
3799
|
fontEntries: [],
|
|
2039
|
-
ps: pdfString,
|
|
2040
|
-
tw: helveticaWidth,
|
|
3800
|
+
ps: normalize ? (s) => pdfString(_norm(s)) : pdfString,
|
|
3801
|
+
tw: normalize ? (s, sz) => helveticaWidth(_norm(s), sz) : helveticaWidth,
|
|
2041
3802
|
textRuns: () => [],
|
|
2042
3803
|
f1: "/F1",
|
|
2043
3804
|
f2: "/F2"
|
|
@@ -2050,6 +3811,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2050
3811
|
const s = _usedGids.get(fontRef);
|
|
2051
3812
|
if (s) s.add(gid);
|
|
2052
3813
|
}
|
|
3814
|
+
const _colorEmoji = fontEntries.some((fe) => fe.fontData.colorGlyphs) ? createColorEmojiCollector() : void 0;
|
|
2053
3815
|
return {
|
|
2054
3816
|
isUnicode: true,
|
|
2055
3817
|
fontEntries,
|
|
@@ -2059,8 +3821,10 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2059
3821
|
getUsedGids() {
|
|
2060
3822
|
return _usedGids;
|
|
2061
3823
|
},
|
|
3824
|
+
colorEmoji: _colorEmoji,
|
|
2062
3825
|
textRuns(str, sz) {
|
|
2063
3826
|
if (!str) return [];
|
|
3827
|
+
str = _norm(str);
|
|
2064
3828
|
str = stripBidiControls(str);
|
|
2065
3829
|
if (!str) return [];
|
|
2066
3830
|
if (containsRTL(str)) {
|
|
@@ -2127,6 +3891,56 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2127
3891
|
}
|
|
2128
3892
|
}
|
|
2129
3893
|
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3894
|
+
} else if (containsTelugu(fRun.text)) {
|
|
3895
|
+
const shaped = shapeTeluguText(fRun.text, fd);
|
|
3896
|
+
let designW = 0;
|
|
3897
|
+
for (const g of shaped) {
|
|
3898
|
+
_trackGid(fontRef, g.gid);
|
|
3899
|
+
if (!g.isZeroAdvance) {
|
|
3900
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3904
|
+
} else if (containsSinhala(fRun.text)) {
|
|
3905
|
+
const shaped = shapeSinhalaText(fRun.text, fd);
|
|
3906
|
+
let designW = 0;
|
|
3907
|
+
for (const g of shaped) {
|
|
3908
|
+
_trackGid(fontRef, g.gid);
|
|
3909
|
+
if (!g.isZeroAdvance) {
|
|
3910
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3914
|
+
} else if (containsTibetan(fRun.text)) {
|
|
3915
|
+
const shaped = shapeTibetanText(fRun.text, fd);
|
|
3916
|
+
let designW = 0;
|
|
3917
|
+
for (const g of shaped) {
|
|
3918
|
+
_trackGid(fontRef, g.gid);
|
|
3919
|
+
if (!g.isZeroAdvance) {
|
|
3920
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3924
|
+
} else if (containsKhmer(fRun.text)) {
|
|
3925
|
+
const shaped = shapeKhmerText(fRun.text, fd);
|
|
3926
|
+
let designW = 0;
|
|
3927
|
+
for (const g of shaped) {
|
|
3928
|
+
_trackGid(fontRef, g.gid);
|
|
3929
|
+
if (!g.isZeroAdvance) {
|
|
3930
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
3934
|
+
} else if (containsMyanmar(fRun.text)) {
|
|
3935
|
+
const shaped = shapeMyanmarText(fRun.text, fd);
|
|
3936
|
+
let designW = 0;
|
|
3937
|
+
for (const g of shaped) {
|
|
3938
|
+
_trackGid(fontRef, g.gid);
|
|
3939
|
+
if (!g.isZeroAdvance) {
|
|
3940
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
2130
3944
|
} else if (containsDevanagari(fRun.text)) {
|
|
2131
3945
|
const shaped = shapeDevanagariText(fRun.text, fd);
|
|
2132
3946
|
let designW = 0;
|
|
@@ -2184,6 +3998,61 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2184
3998
|
}
|
|
2185
3999
|
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
2186
4000
|
}
|
|
4001
|
+
if (containsTelugu(run.text)) {
|
|
4002
|
+
const shaped = shapeTeluguText(run.text, fd);
|
|
4003
|
+
let designW = 0;
|
|
4004
|
+
for (const g of shaped) {
|
|
4005
|
+
_trackGid(fontRef, g.gid);
|
|
4006
|
+
if (!g.isZeroAdvance) {
|
|
4007
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4011
|
+
}
|
|
4012
|
+
if (containsSinhala(run.text)) {
|
|
4013
|
+
const shaped = shapeSinhalaText(run.text, fd);
|
|
4014
|
+
let designW = 0;
|
|
4015
|
+
for (const g of shaped) {
|
|
4016
|
+
_trackGid(fontRef, g.gid);
|
|
4017
|
+
if (!g.isZeroAdvance) {
|
|
4018
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4022
|
+
}
|
|
4023
|
+
if (containsTibetan(run.text)) {
|
|
4024
|
+
const shaped = shapeTibetanText(run.text, fd);
|
|
4025
|
+
let designW = 0;
|
|
4026
|
+
for (const g of shaped) {
|
|
4027
|
+
_trackGid(fontRef, g.gid);
|
|
4028
|
+
if (!g.isZeroAdvance) {
|
|
4029
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4033
|
+
}
|
|
4034
|
+
if (containsKhmer(run.text)) {
|
|
4035
|
+
const shaped = shapeKhmerText(run.text, fd);
|
|
4036
|
+
let designW = 0;
|
|
4037
|
+
for (const g of shaped) {
|
|
4038
|
+
_trackGid(fontRef, g.gid);
|
|
4039
|
+
if (!g.isZeroAdvance) {
|
|
4040
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4044
|
+
}
|
|
4045
|
+
if (containsMyanmar(run.text)) {
|
|
4046
|
+
const shaped = shapeMyanmarText(run.text, fd);
|
|
4047
|
+
let designW = 0;
|
|
4048
|
+
for (const g of shaped) {
|
|
4049
|
+
_trackGid(fontRef, g.gid);
|
|
4050
|
+
if (!g.isZeroAdvance) {
|
|
4051
|
+
designW += fd.widths[g.gid] !== void 0 ? fd.widths[g.gid] : fd.defaultWidth;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
4055
|
+
}
|
|
2187
4056
|
if (containsDevanagari(run.text)) {
|
|
2188
4057
|
const shaped = shapeDevanagariText(run.text, fd);
|
|
2189
4058
|
let designW = 0;
|
|
@@ -2200,6 +4069,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2200
4069
|
},
|
|
2201
4070
|
ps(str) {
|
|
2202
4071
|
if (!str) return "<>";
|
|
4072
|
+
str = _norm(str);
|
|
2203
4073
|
str = stripBidiControls(str);
|
|
2204
4074
|
if (!str) return "<>";
|
|
2205
4075
|
const { cmap } = primary.fontData;
|
|
@@ -2228,7 +4098,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2228
4098
|
}
|
|
2229
4099
|
return `<${hex2.toUpperCase()}>`;
|
|
2230
4100
|
}
|
|
2231
|
-
if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsDevanagari(str)) {
|
|
4101
|
+
if (!containsThai(str) && !containsBengali(str) && !containsTamil(str) && !containsTelugu(str) && !containsSinhala(str) && !containsTibetan(str) && !containsKhmer(str) && !containsMyanmar(str) && !containsDevanagari(str)) {
|
|
2232
4102
|
let hex2 = "";
|
|
2233
4103
|
for (let i = 0; i < str.length; i++) {
|
|
2234
4104
|
const rawCp = str.codePointAt(i) ?? 0;
|
|
@@ -2240,7 +4110,7 @@ function createEncodingContext(fontEntries, pdfA = false) {
|
|
|
2240
4110
|
}
|
|
2241
4111
|
return `<${hex2.toUpperCase()}>`;
|
|
2242
4112
|
}
|
|
2243
|
-
const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : shapeDevanagariText;
|
|
4113
|
+
const shapeFn = containsThai(str) ? shapeThaiText : containsBengali(str) ? shapeBengaliText : containsTamil(str) ? shapeTamilText : containsTelugu(str) ? shapeTeluguText : containsSinhala(str) ? shapeSinhalaText : containsTibetan(str) ? shapeTibetanText : containsKhmer(str) ? shapeKhmerText : containsMyanmar(str) ? shapeMyanmarText : shapeDevanagariText;
|
|
2244
4114
|
const shaped = shapeFn(str, primary.fontData);
|
|
2245
4115
|
let hex = "";
|
|
2246
4116
|
for (const g of shaped) {
|
|
@@ -2322,24 +4192,6 @@ function buildSubsetWidthArray(widths, usedGids) {
|
|
|
2322
4192
|
return parts.join(" ");
|
|
2323
4193
|
}
|
|
2324
4194
|
|
|
2325
|
-
// src/fonts/font-loader.ts
|
|
2326
|
-
var _fontBinaryCache = /* @__PURE__ */ new WeakMap();
|
|
2327
|
-
function getDecodedFontBytes(fontData) {
|
|
2328
|
-
const cached = _fontBinaryCache.get(fontData);
|
|
2329
|
-
if (cached) return cached;
|
|
2330
|
-
let bytes;
|
|
2331
|
-
if (typeof atob === "function") {
|
|
2332
|
-
const binaryStr = atob(fontData.ttfBase64);
|
|
2333
|
-
bytes = new Uint8Array(binaryStr.length);
|
|
2334
|
-
for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
|
|
2335
|
-
} else {
|
|
2336
|
-
const buf = globalThis["Buffer"];
|
|
2337
|
-
bytes = buf.from(fontData.ttfBase64, "base64");
|
|
2338
|
-
}
|
|
2339
|
-
_fontBinaryCache.set(fontData, bytes);
|
|
2340
|
-
return bytes;
|
|
2341
|
-
}
|
|
2342
|
-
|
|
2343
4195
|
// src/fonts/font-subsetter.ts
|
|
2344
4196
|
function subsetTTF(ttfInput, usedGids) {
|
|
2345
4197
|
try {
|
|
@@ -2653,7 +4505,7 @@ function buildStructureTree(root, startObjNum, pageObjToStructParents) {
|
|
|
2653
4505
|
};
|
|
2654
4506
|
}
|
|
2655
4507
|
function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
|
|
2656
|
-
const pad2 = (
|
|
4508
|
+
const pad2 = (n2) => String(n2).padStart(2, "0");
|
|
2657
4509
|
const yyyy = now.getFullYear();
|
|
2658
4510
|
const mm = pad2(now.getMonth() + 1);
|
|
2659
4511
|
const dd = pad2(now.getDate());
|
|
@@ -2861,26 +4713,45 @@ function txtShaped(shaped, x, y, font, sz, fontData) {
|
|
|
2861
4713
|
}
|
|
2862
4714
|
return parts.join("\n");
|
|
2863
4715
|
}
|
|
4716
|
+
var fmtScale = (v) => v.toFixed(5).replace(/0+$/, "").replace(/\.$/, "") || "0";
|
|
4717
|
+
function emitColorEmojiRun(parts, run, penX, y, sz, enc) {
|
|
4718
|
+
const fd = run.fontData;
|
|
4719
|
+
const upm = fd.metrics.unitsPerEm;
|
|
4720
|
+
const scale = sz / upm;
|
|
4721
|
+
const s = fmtScale(scale);
|
|
4722
|
+
const hex = (run.hexStr ?? "").replace(/[<>]/g, "");
|
|
4723
|
+
for (let i = 0; i + 4 <= hex.length; i += 4) {
|
|
4724
|
+
const tag = hex.substr(i, 4);
|
|
4725
|
+
const gid = parseInt(tag, 16);
|
|
4726
|
+
const adv = (fd.widths[gid] !== void 0 ? fd.widths[gid] : fd.defaultWidth) * scale;
|
|
4727
|
+
const name = enc.colorEmoji?.useGlyph(fd, gid) ?? null;
|
|
4728
|
+
if (name) {
|
|
4729
|
+
parts.push(`q ${s} 0 0 ${s} ${fmtNum(penX)} ${fmtNum(y)} cm /${name} Do Q`);
|
|
4730
|
+
} else {
|
|
4731
|
+
parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td <${tag}> Tj ET`);
|
|
4732
|
+
}
|
|
4733
|
+
penX += adv;
|
|
4734
|
+
}
|
|
4735
|
+
return penX;
|
|
4736
|
+
}
|
|
2864
4737
|
function txt(str, x, y, font, sz, enc) {
|
|
2865
4738
|
if (!enc.isUnicode) {
|
|
2866
4739
|
return `BT ${font} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${enc.ps(str)} Tj ET`;
|
|
2867
4740
|
}
|
|
2868
4741
|
const runs = enc.textRuns(str, sz);
|
|
2869
4742
|
if (runs.length === 0) return "";
|
|
2870
|
-
if (runs.length === 1) {
|
|
2871
|
-
const run = runs[0];
|
|
2872
|
-
if (run.shaped) return txtShaped(run.shaped, x, y, run.fontRef, sz, run.fontData);
|
|
2873
|
-
return `BT ${run.fontRef} ${sz} Tf ${fmtNum(x)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`;
|
|
2874
|
-
}
|
|
2875
4743
|
const parts = [];
|
|
2876
4744
|
let penX = x;
|
|
2877
4745
|
for (const run of runs) {
|
|
2878
|
-
if (run.
|
|
4746
|
+
if (enc.colorEmoji && run.fontData.colorGlyphs && run.hexStr) {
|
|
4747
|
+
penX = emitColorEmojiRun(parts, run, penX, y, sz, enc);
|
|
4748
|
+
} else if (run.shaped) {
|
|
2879
4749
|
parts.push(txtShaped(run.shaped, penX, y, run.fontRef, sz, run.fontData));
|
|
4750
|
+
penX += run.widthPt;
|
|
2880
4751
|
} else {
|
|
2881
4752
|
parts.push(`BT ${run.fontRef} ${sz} Tf ${fmtNum(penX)} ${fmtNum(y)} Td ${run.hexStr} Tj ET`);
|
|
4753
|
+
penX += run.widthPt;
|
|
2882
4754
|
}
|
|
2883
|
-
penX += run.widthPt;
|
|
2884
4755
|
}
|
|
2885
4756
|
return parts.join("\n");
|
|
2886
4757
|
}
|
|
@@ -2973,12 +4844,12 @@ var DEFAULT_COLUMNS = [
|
|
|
2973
4844
|
{ f: 0.18, a: "c", mx: 20, mxH: 20 }
|
|
2974
4845
|
];
|
|
2975
4846
|
function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
2976
|
-
const
|
|
2977
|
-
const cwi = new Array(
|
|
2978
|
-
const fixed = new Array(
|
|
4847
|
+
const n2 = columns.length;
|
|
4848
|
+
const cwi = new Array(n2).fill(0);
|
|
4849
|
+
const fixed = new Array(n2).fill(false);
|
|
2979
4850
|
let totalFixed = 0;
|
|
2980
4851
|
let freeWeight = 0;
|
|
2981
|
-
for (let i = 0; i <
|
|
4852
|
+
for (let i = 0; i < n2; i++) {
|
|
2982
4853
|
const col = columns[i];
|
|
2983
4854
|
let w = col.f * contentWidth;
|
|
2984
4855
|
let clamped = false;
|
|
@@ -3000,13 +4871,13 @@ function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
|
3000
4871
|
}
|
|
3001
4872
|
const remaining = contentWidth - totalFixed;
|
|
3002
4873
|
if (freeWeight > 0) {
|
|
3003
|
-
for (let i = 0; i <
|
|
4874
|
+
for (let i = 0; i < n2; i++) {
|
|
3004
4875
|
if (!fixed[i]) cwi[i] = columns[i].f / freeWeight * remaining;
|
|
3005
4876
|
}
|
|
3006
4877
|
}
|
|
3007
|
-
const cx = new Array(
|
|
4878
|
+
const cx = new Array(n2);
|
|
3008
4879
|
let x = marginLeft;
|
|
3009
|
-
for (let i = 0; i <
|
|
4880
|
+
for (let i = 0; i < n2; i++) {
|
|
3010
4881
|
cx[i] = x;
|
|
3011
4882
|
x += cwi[i];
|
|
3012
4883
|
}
|
|
@@ -3032,8 +4903,8 @@ function parseColor(input) {
|
|
|
3032
4903
|
`Invalid color format: ${JSON.stringify(input)}. Expected "#RRGGBB", "#RGB", [r, g, b] (0\u2013255), or "R G B" (0.0\u20131.0).`
|
|
3033
4904
|
);
|
|
3034
4905
|
}
|
|
3035
|
-
function fmtChannel(
|
|
3036
|
-
const clamped = Math.max(0, Math.min(1,
|
|
4906
|
+
function fmtChannel(n2) {
|
|
4907
|
+
const clamped = Math.max(0, Math.min(1, n2));
|
|
3037
4908
|
const rounded = Math.round(clamped * 1e3) / 1e3;
|
|
3038
4909
|
return String(rounded);
|
|
3039
4910
|
}
|
|
@@ -3075,8 +4946,8 @@ function parseTupleColor(tuple) {
|
|
|
3075
4946
|
function parsePdfRgbString(str) {
|
|
3076
4947
|
const parts = str.split(" ");
|
|
3077
4948
|
for (const p of parts) {
|
|
3078
|
-
const
|
|
3079
|
-
if (
|
|
4949
|
+
const n2 = Number(p);
|
|
4950
|
+
if (n2 < 0 || n2 > 1) {
|
|
3080
4951
|
throw new Error(
|
|
3081
4952
|
`Invalid PDF RGB value: ${p} in ${JSON.stringify(str)}. Each value must be 0.0\u20131.0.`
|
|
3082
4953
|
);
|
|
@@ -3216,8 +5087,8 @@ var MD5_K = new Uint32Array([
|
|
|
3216
5087
|
718787259,
|
|
3217
5088
|
3951481745
|
|
3218
5089
|
]);
|
|
3219
|
-
function rotl32(x,
|
|
3220
|
-
return (x <<
|
|
5090
|
+
function rotl32(x, n2) {
|
|
5091
|
+
return (x << n2 | x >>> 32 - n2) >>> 0;
|
|
3221
5092
|
}
|
|
3222
5093
|
function md5(input) {
|
|
3223
5094
|
const len = input.length;
|
|
@@ -3455,6 +5326,9 @@ function _buildPageTemplate(template, page, pages, title, date, y, enc, mgL, mgR
|
|
|
3455
5326
|
return { ops, structEls };
|
|
3456
5327
|
}
|
|
3457
5328
|
function buildPDF(params, layoutOptions) {
|
|
5329
|
+
return assembleTableParts(params, layoutOptions).join("");
|
|
5330
|
+
}
|
|
5331
|
+
function assembleTableParts(params, layoutOptions) {
|
|
3458
5332
|
if (!params || typeof params !== "object") {
|
|
3459
5333
|
throw new Error("buildPDF: params is required and must be an object");
|
|
3460
5334
|
}
|
|
@@ -3480,14 +5354,14 @@ function buildPDF(params, layoutOptions) {
|
|
|
3480
5354
|
const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
|
|
3481
5355
|
const pdfaConfig = resolvePdfAConfig();
|
|
3482
5356
|
const tagged = pdfaConfig.enabled;
|
|
3483
|
-
const enc = createEncodingContext(fontEntries, tagged);
|
|
5357
|
+
const enc = createEncodingContext(fontEntries, tagged, false);
|
|
3484
5358
|
const footerTpl = {
|
|
3485
5359
|
left: footerText || void 0,
|
|
3486
5360
|
right: "{page}/{pages}"
|
|
3487
5361
|
};
|
|
3488
5362
|
const headerH = 0;
|
|
3489
5363
|
const dateNow = /* @__PURE__ */ new Date();
|
|
3490
|
-
const pad2d = (
|
|
5364
|
+
const pad2d = (n2) => String(n2).padStart(2, "0");
|
|
3491
5365
|
const dateStr = `${dateNow.getFullYear()}-${pad2d(dateNow.getMonth() + 1)}-${pad2d(dateNow.getDate())}`;
|
|
3492
5366
|
const infoCount = infoItems.length;
|
|
3493
5367
|
const page1Header = TITLE_LN + 16 + infoCount * INFO_LN + 8 + BAL_H + 10;
|
|
@@ -3509,6 +5383,9 @@ function buildPDF(params, layoutOptions) {
|
|
|
3509
5383
|
const pageStreams = [];
|
|
3510
5384
|
let rowIdx = 0;
|
|
3511
5385
|
const prePageObjStart = enc.isUnicode && fontEntries.length > 0 ? 5 + fontEntries.length * 5 + wmExtraObjs : 5 + wmExtraObjs;
|
|
5386
|
+
const preBaseObjCount = enc.isUnicode && fontEntries.length > 0 ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
|
|
5387
|
+
const latinToUniObjNum = tagged ? 0 : preBaseObjCount + 2;
|
|
5388
|
+
const baseFontToUniRef = latinToUniObjNum ? ` /ToUnicode ${latinToUniObjNum} 0 R` : "";
|
|
3512
5389
|
const pageObjToStructParents = /* @__PURE__ */ new Map();
|
|
3513
5390
|
for (let p = 0; p < totalPages; p++) {
|
|
3514
5391
|
const pageObjNum = prePageObjStart + p * 2;
|
|
@@ -3638,8 +5515,8 @@ function buildPDF(params, layoutOptions) {
|
|
|
3638
5515
|
emitObj(3, refDict);
|
|
3639
5516
|
emitObj(4, refDict);
|
|
3640
5517
|
} else {
|
|
3641
|
-
emitObj(3,
|
|
3642
|
-
emitObj(4,
|
|
5518
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
5519
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
3643
5520
|
}
|
|
3644
5521
|
for (let fi = 0; fi < fontEntries.length; fi++) {
|
|
3645
5522
|
const fe = fontEntries[fi];
|
|
@@ -3697,8 +5574,8 @@ function buildPDF(params, layoutOptions) {
|
|
|
3697
5574
|
kids.push(`${pageObjStart + p * 2} 0 R`);
|
|
3698
5575
|
}
|
|
3699
5576
|
emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
|
|
3700
|
-
emitObj(3,
|
|
3701
|
-
emitObj(4,
|
|
5577
|
+
emitObj(3, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
5578
|
+
emitObj(4, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding${baseFontToUniRef} >>`);
|
|
3702
5579
|
let wmGsResLatin = "";
|
|
3703
5580
|
let wmImgResLatin = "";
|
|
3704
5581
|
for (let p = 0; p < totalPages; p++) {
|
|
@@ -3715,13 +5592,18 @@ function buildPDF(params, layoutOptions) {
|
|
|
3715
5592
|
}
|
|
3716
5593
|
const baseObjCount = enc.isUnicode ? 4 + fontEntries.length * 5 + wmExtraObjs + totalPages * 2 : 4 + wmExtraObjs + totalPages * 2;
|
|
3717
5594
|
const infoObjNum = baseObjCount + 1;
|
|
3718
|
-
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata();
|
|
5595
|
+
const { pdfDate, xmpDate: isoDate } = buildPdfMetadata(layoutOptions?.creationDate);
|
|
3719
5596
|
const infoTitle = params.docTitle || title || "";
|
|
3720
5597
|
emitObj(
|
|
3721
5598
|
infoObjNum,
|
|
3722
5599
|
`<< /Title ${encodePdfTextString(infoTitle)} /Producer (pdfnative) /CreationDate (${pdfDate}) >>`
|
|
3723
5600
|
);
|
|
3724
5601
|
let totalObjs = infoObjNum;
|
|
5602
|
+
if (latinToUniObjNum) {
|
|
5603
|
+
const cmap = buildWinAnsiToUnicodeCMap();
|
|
5604
|
+
emitStreamObj(latinToUniObjNum, `<< /Length ${cmap.length}`, cmap);
|
|
5605
|
+
totalObjs = latinToUniObjNum;
|
|
5606
|
+
}
|
|
3725
5607
|
let xmpObjNum = 0;
|
|
3726
5608
|
let outputIntentObjNum = 0;
|
|
3727
5609
|
if (tagged) {
|
|
@@ -3777,7 +5659,7 @@ endobj
|
|
|
3777
5659
|
}
|
|
3778
5660
|
const writer = { emit, emitObj, emitStreamObj, offset: getOffset, adjustOffset, objOffsets, parts };
|
|
3779
5661
|
writeXrefTrailer(writer, totalObjs, infoObjNum, encState, `${infoTitle}|${pdfDate}`);
|
|
3780
|
-
return parts
|
|
5662
|
+
return parts;
|
|
3781
5663
|
}
|
|
3782
5664
|
|
|
3783
5665
|
// src/worker/pdf-worker.ts
|