pdfnative 1.0.5 → 1.2.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 +82 -30
- package/dist/index.cjs +3289 -2064
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +559 -18
- package/dist/index.d.ts +559 -18
- package/dist/index.js +3274 -2065
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +1483 -1155
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +1483 -1155
- package/dist/worker/index.js.map +1 -1
- package/fonts/noto-emoji-data.d.ts +27 -0
- package/fonts/noto-emoji-data.js +64 -0
- package/fonts/noto-sans-data.d.ts +22 -0
- package/fonts/noto-sans-data.js +64 -0
- package/package.json +2 -1
package/dist/worker/index.js
CHANGED
|
@@ -88,12 +88,12 @@ function shapeThaiText(str, fontData) {
|
|
|
88
88
|
function getAdv(gid) {
|
|
89
89
|
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
90
90
|
}
|
|
91
|
-
function
|
|
91
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
92
92
|
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
93
93
|
if (!base) return null;
|
|
94
94
|
return base[markClass] ?? null;
|
|
95
95
|
}
|
|
96
|
-
function
|
|
96
|
+
function getMarkAnchor2(markGid) {
|
|
97
97
|
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
98
98
|
if (!mark) return null;
|
|
99
99
|
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
@@ -130,7 +130,7 @@ function shapeThaiText(str, fontData) {
|
|
|
130
130
|
for (let ai = 0; ai < cluster.aboves.length; ai++) {
|
|
131
131
|
const abvCp = cluster.aboves[ai];
|
|
132
132
|
const markGid = resolveMarkGid(abvCp, isTallBase);
|
|
133
|
-
const markAnchor =
|
|
133
|
+
const markAnchor = getMarkAnchor2(markGid);
|
|
134
134
|
let dx = 0;
|
|
135
135
|
let dy = 0;
|
|
136
136
|
if (ai > 0 && prevAboveMarkGid !== null) {
|
|
@@ -139,7 +139,7 @@ function shapeThaiText(str, fontData) {
|
|
|
139
139
|
dx = prevAboveMarkDx + m2mOffset.dx;
|
|
140
140
|
dy = prevAboveMarkDy + m2mOffset.dy;
|
|
141
141
|
} else if (markAnchor) {
|
|
142
|
-
const baseAnchor =
|
|
142
|
+
const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
143
143
|
if (baseAnchor) {
|
|
144
144
|
dx = baseAnchor[0] - markAnchor.x - baseAdv;
|
|
145
145
|
dy = baseAnchor[1] - markAnchor.y;
|
|
@@ -147,7 +147,7 @@ function shapeThaiText(str, fontData) {
|
|
|
147
147
|
}
|
|
148
148
|
} else {
|
|
149
149
|
if (markAnchor) {
|
|
150
|
-
const baseAnchor =
|
|
150
|
+
const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
151
151
|
if (baseAnchor) {
|
|
152
152
|
dx = baseAnchor[0] - markAnchor.x - baseAdv;
|
|
153
153
|
dy = baseAnchor[1] - markAnchor.y;
|
|
@@ -161,11 +161,11 @@ function shapeThaiText(str, fontData) {
|
|
|
161
161
|
}
|
|
162
162
|
for (const blwCp of cluster.belows) {
|
|
163
163
|
const markGid = cmap[blwCp] || 0;
|
|
164
|
-
const markAnchor =
|
|
164
|
+
const markAnchor = getMarkAnchor2(markGid);
|
|
165
165
|
let dx = 0;
|
|
166
166
|
let dy = 0;
|
|
167
167
|
if (markAnchor) {
|
|
168
|
-
const baseAnchor =
|
|
168
|
+
const baseAnchor = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
169
169
|
if (baseAnchor) {
|
|
170
170
|
dx = baseAnchor[0] - markAnchor.x - baseAdv;
|
|
171
171
|
dy = baseAnchor[1] - markAnchor.y;
|
|
@@ -177,148 +177,6 @@ function shapeThaiText(str, fontData) {
|
|
|
177
177
|
return shaped;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
// src/shaping/script-detect.ts
|
|
181
|
-
function detectCharLang(cp) {
|
|
182
|
-
if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
|
|
183
|
-
if (cp >= 2304 && cp <= 2431 || cp >= 43232 && cp <= 43263) return "hi";
|
|
184
|
-
if (cp >= 3584 && cp <= 3711) return "th";
|
|
185
|
-
if (cp >= 12352 && cp <= 12543) return "ja";
|
|
186
|
-
if (cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687) return "ko";
|
|
187
|
-
if (cp >= 19968 && cp <= 40959 || cp >= 13312 && cp <= 19903 || cp >= 63744 && cp <= 64255) return "zh";
|
|
188
|
-
if (cp >= 7680 && cp <= 7935 || cp === 8363 || cp === 272 || cp === 273 || cp === 259 || cp === 258) return "vi";
|
|
189
|
-
if (cp === 260 || cp === 261 || cp === 262 || cp === 263 || cp === 280 || cp === 281 || cp === 321 || cp === 322 || cp === 323 || cp === 324 || cp === 346 || cp === 347 || cp === 377 || cp === 378 || cp === 379 || cp === 380) return "pl";
|
|
190
|
-
if (cp >= 256 && cp <= 383 || cp === 8378) return "tr";
|
|
191
|
-
if (cp >= 1424 && cp <= 1535) return "he";
|
|
192
|
-
if (cp >= 1536 && cp <= 1791 || cp >= 1872 && cp <= 1919 || cp >= 64336 && cp <= 65023 || cp >= 65136 && cp <= 65279) return "ar";
|
|
193
|
-
if (cp >= 1024 && cp <= 1279 || cp >= 1280 && cp <= 1327) return "ru";
|
|
194
|
-
if (cp >= 4256 && cp <= 4351 || cp >= 11520 && cp <= 11567) return "ka";
|
|
195
|
-
if (cp >= 1328 && cp <= 1423 || cp >= 64275 && cp <= 64279) return "hy";
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// src/shaping/multi-font.ts
|
|
200
|
-
function splitTextByFont(str, fontEntries) {
|
|
201
|
-
if (!str || fontEntries.length === 0) return [];
|
|
202
|
-
if (fontEntries.length === 1) return [{ text: str, entry: fontEntries[0] }];
|
|
203
|
-
const runs = [];
|
|
204
|
-
let currentEntry = null;
|
|
205
|
-
let currentText = "";
|
|
206
|
-
for (let i = 0; i < str.length; ) {
|
|
207
|
-
const cp = str.codePointAt(i) ?? 0;
|
|
208
|
-
const charLen = cp > 65535 ? 2 : 1;
|
|
209
|
-
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
210
|
-
const char = str.substring(i, i + charLen);
|
|
211
|
-
if (currentEntry && currentEntry.fontData.cmap[normCp]) {
|
|
212
|
-
currentText += char;
|
|
213
|
-
i += charLen;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
let newEntry = null;
|
|
217
|
-
const charLang = detectCharLang(normCp);
|
|
218
|
-
if (charLang) {
|
|
219
|
-
for (const fe of fontEntries) {
|
|
220
|
-
if (fe.lang === charLang && fe.fontData.cmap[normCp]) {
|
|
221
|
-
newEntry = fe;
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (!newEntry) {
|
|
227
|
-
for (const fe of fontEntries) {
|
|
228
|
-
if (fe.fontData.cmap[normCp]) {
|
|
229
|
-
newEntry = fe;
|
|
230
|
-
break;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (!newEntry) newEntry = fontEntries[0];
|
|
235
|
-
if (newEntry !== currentEntry) {
|
|
236
|
-
if (currentText && currentEntry) runs.push({ text: currentText, entry: currentEntry });
|
|
237
|
-
currentEntry = newEntry;
|
|
238
|
-
currentText = char;
|
|
239
|
-
} else {
|
|
240
|
-
currentText += char;
|
|
241
|
-
}
|
|
242
|
-
i += charLen;
|
|
243
|
-
}
|
|
244
|
-
if (currentText && currentEntry) runs.push({ text: currentText, entry: currentEntry });
|
|
245
|
-
return runs;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// src/fonts/encoding.ts
|
|
249
|
-
function toWinAnsi(str) {
|
|
250
|
-
if (!str) return "";
|
|
251
|
-
let r = "";
|
|
252
|
-
for (let i = 0; i < str.length; i++) {
|
|
253
|
-
const c = str.charCodeAt(i);
|
|
254
|
-
if (c >= 32 && c <= 126) r += str[i];
|
|
255
|
-
else if (c >= 160 && c <= 255) r += str[i];
|
|
256
|
-
else if (c === 8364) r += "\x80";
|
|
257
|
-
else if (c === 8218) r += "\x82";
|
|
258
|
-
else if (c === 402) r += "\x83";
|
|
259
|
-
else if (c === 8222) r += "\x84";
|
|
260
|
-
else if (c === 8230) r += "\x85";
|
|
261
|
-
else if (c === 8224) r += "\x86";
|
|
262
|
-
else if (c === 8225) r += "\x87";
|
|
263
|
-
else if (c === 710) r += "\x88";
|
|
264
|
-
else if (c === 8240) r += "\x89";
|
|
265
|
-
else if (c === 352) r += "\x8A";
|
|
266
|
-
else if (c === 8249) r += "\x8B";
|
|
267
|
-
else if (c === 338) r += "\x8C";
|
|
268
|
-
else if (c === 381) r += "\x8E";
|
|
269
|
-
else if (c === 8216) r += "\x91";
|
|
270
|
-
else if (c === 8217) r += "\x92";
|
|
271
|
-
else if (c === 8220) r += "\x93";
|
|
272
|
-
else if (c === 8221) r += "\x94";
|
|
273
|
-
else if (c === 8226) r += "\x95";
|
|
274
|
-
else if (c === 8211) r += "\x96";
|
|
275
|
-
else if (c === 8212) r += "\x97";
|
|
276
|
-
else if (c === 732) r += "\x98";
|
|
277
|
-
else if (c === 8482) r += "\x99";
|
|
278
|
-
else if (c === 353) r += "\x9A";
|
|
279
|
-
else if (c === 8250) r += "\x9B";
|
|
280
|
-
else if (c === 339) r += "\x9C";
|
|
281
|
-
else if (c === 382) r += "\x9E";
|
|
282
|
-
else if (c === 376) r += "\x9F";
|
|
283
|
-
else if (c === 160 || c === 8239) r += " ";
|
|
284
|
-
else if (c === 9 || c === 10 || c === 13) r += " ";
|
|
285
|
-
else if (c < 32) ; else r += "?";
|
|
286
|
-
}
|
|
287
|
-
return r;
|
|
288
|
-
}
|
|
289
|
-
function pdfString(str) {
|
|
290
|
-
const s = toWinAnsi(str);
|
|
291
|
-
return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
|
|
292
|
-
}
|
|
293
|
-
function truncate(str, max) {
|
|
294
|
-
if (!str || str.length <= max) return str || "";
|
|
295
|
-
if (max <= 2) return "..";
|
|
296
|
-
return str.slice(0, max - 2) + "..";
|
|
297
|
-
}
|
|
298
|
-
function helveticaWidth(str, sz) {
|
|
299
|
-
let w = 0;
|
|
300
|
-
for (let i = 0; i < str.length; i++) {
|
|
301
|
-
const cp = str.codePointAt(i) ?? 0;
|
|
302
|
-
if (cp > 65535) i++;
|
|
303
|
-
if (cp >= 48 && cp <= 57) w += 556;
|
|
304
|
-
else if (cp >= 65 && cp <= 90) w += 680;
|
|
305
|
-
else if (cp >= 97 && cp <= 122) w += 500;
|
|
306
|
-
else if (cp === 32) w += 278;
|
|
307
|
-
else if (cp === 46 || cp === 44) w += 278;
|
|
308
|
-
else if (cp === 43) w += 584;
|
|
309
|
-
else if (cp === 45) w += 333;
|
|
310
|
-
else if (cp === 47 || cp === 58) w += 278;
|
|
311
|
-
else if (cp === 8212) w += 1e3;
|
|
312
|
-
else if (cp === 8211) w += 556;
|
|
313
|
-
else if (cp === 8230) w += 1e3;
|
|
314
|
-
else if (cp === 8216 || cp === 8217) w += 222;
|
|
315
|
-
else if (cp === 8220 || cp === 8221) w += 333;
|
|
316
|
-
else if (cp === 8364) w += 556;
|
|
317
|
-
else w += 556;
|
|
318
|
-
}
|
|
319
|
-
return w * sz / 1e3;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
180
|
// src/shaping/script-registry.ts
|
|
323
181
|
var ARABIC_START = 1536;
|
|
324
182
|
var ARABIC_END = 1791;
|
|
@@ -340,6 +198,36 @@ var BENGALI_START = 2432;
|
|
|
340
198
|
var BENGALI_END = 2559;
|
|
341
199
|
var TAMIL_START = 2944;
|
|
342
200
|
var TAMIL_END = 3071;
|
|
201
|
+
var EMOJI_RANGES = [
|
|
202
|
+
[127744, 128511],
|
|
203
|
+
// Miscellaneous Symbols and Pictographs
|
|
204
|
+
[128512, 128591],
|
|
205
|
+
// Emoticons
|
|
206
|
+
[128640, 128767],
|
|
207
|
+
// Transport and Map Symbols
|
|
208
|
+
[128768, 128895],
|
|
209
|
+
// Alchemical Symbols (partial)
|
|
210
|
+
[128896, 129023],
|
|
211
|
+
// Geometric Shapes Extended
|
|
212
|
+
[129024, 129279],
|
|
213
|
+
// Supplemental Arrows-C
|
|
214
|
+
[129280, 129535],
|
|
215
|
+
// Supplemental Symbols and Pictographs
|
|
216
|
+
[129536, 129647],
|
|
217
|
+
// Chess Symbols
|
|
218
|
+
[129648, 129791],
|
|
219
|
+
// Symbols and Pictographs Extended-A
|
|
220
|
+
[9728, 9983],
|
|
221
|
+
// Miscellaneous Symbols
|
|
222
|
+
[9984, 10175],
|
|
223
|
+
// Dingbats
|
|
224
|
+
[126976, 127023],
|
|
225
|
+
// Mahjong Tiles
|
|
226
|
+
[127136, 127231]
|
|
227
|
+
// Playing Cards
|
|
228
|
+
];
|
|
229
|
+
var FITZPATRICK_START = 127995;
|
|
230
|
+
var FITZPATRICK_END = 127999;
|
|
343
231
|
function isArabicCodepoint(cp) {
|
|
344
232
|
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;
|
|
345
233
|
}
|
|
@@ -355,6 +243,13 @@ function isTamilCodepoint(cp) {
|
|
|
355
243
|
function isDevanagariCodepoint(cp) {
|
|
356
244
|
return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
|
|
357
245
|
}
|
|
246
|
+
function isEmojiCodepoint(cp) {
|
|
247
|
+
if (cp >= FITZPATRICK_START && cp <= FITZPATRICK_END) return true;
|
|
248
|
+
for (const [lo, hi] of EMOJI_RANGES) {
|
|
249
|
+
if (cp >= lo && cp <= hi) return true;
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
358
253
|
function containsArabic(text) {
|
|
359
254
|
for (let i = 0; i < text.length; ) {
|
|
360
255
|
const cp = text.codePointAt(i) ?? 0;
|
|
@@ -388,442 +283,829 @@ function containsDevanagari(str) {
|
|
|
388
283
|
return false;
|
|
389
284
|
}
|
|
390
285
|
|
|
391
|
-
// src/shaping/
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if (cp
|
|
397
|
-
if (cp
|
|
398
|
-
if (cp >=
|
|
399
|
-
if (cp >=
|
|
400
|
-
if (cp
|
|
401
|
-
if (cp ===
|
|
402
|
-
if (cp
|
|
403
|
-
if (cp
|
|
404
|
-
if (cp
|
|
405
|
-
if (cp
|
|
406
|
-
if (cp
|
|
407
|
-
if (cp
|
|
408
|
-
|
|
409
|
-
if (cp >= 2494 && cp <= 2508) return 2;
|
|
410
|
-
if (cp === 2519) return 5;
|
|
411
|
-
if (cp >= 2534 && cp <= 2543) return 9;
|
|
412
|
-
if (cp === 2527) return 0;
|
|
413
|
-
if (cp === 2493) return 1;
|
|
414
|
-
if (cp === 2510) return 0;
|
|
415
|
-
return -1;
|
|
416
|
-
}
|
|
417
|
-
function isConsonant(cp) {
|
|
418
|
-
return bengaliCharType(cp) === 0;
|
|
286
|
+
// src/shaping/script-detect.ts
|
|
287
|
+
function detectCharLang(cp) {
|
|
288
|
+
if (cp >= 880 && cp <= 1023 || cp >= 7936 && cp <= 8191) return "el";
|
|
289
|
+
if (cp >= 2304 && cp <= 2431 || cp >= 43232 && cp <= 43263) return "hi";
|
|
290
|
+
if (cp >= 3584 && cp <= 3711) return "th";
|
|
291
|
+
if (cp >= 12352 && cp <= 12543) return "ja";
|
|
292
|
+
if (cp >= 44032 && cp <= 55215 || cp >= 4352 && cp <= 4607 || cp >= 12592 && cp <= 12687) return "ko";
|
|
293
|
+
if (cp >= 19968 && cp <= 40959 || cp >= 13312 && cp <= 19903 || cp >= 63744 && cp <= 64255) return "zh";
|
|
294
|
+
if (cp >= 7680 && cp <= 7935 || cp === 8363 || cp === 272 || cp === 273 || cp === 259 || cp === 258) return "vi";
|
|
295
|
+
if (cp === 260 || cp === 261 || cp === 262 || cp === 263 || cp === 280 || cp === 281 || cp === 321 || cp === 322 || cp === 323 || cp === 324 || cp === 346 || cp === 347 || cp === 377 || cp === 378 || cp === 379 || cp === 380) return "pl";
|
|
296
|
+
if (cp >= 256 && cp <= 383 || cp === 8378) return "tr";
|
|
297
|
+
if (cp >= 1424 && cp <= 1535) return "he";
|
|
298
|
+
if (cp >= 1536 && cp <= 1791 || cp >= 1872 && cp <= 1919 || cp >= 64336 && cp <= 65023 || cp >= 65136 && cp <= 65279) return "ar";
|
|
299
|
+
if (cp >= 1024 && cp <= 1279 || cp >= 1280 && cp <= 1327) return "ru";
|
|
300
|
+
if (cp >= 4256 && cp <= 4351 || cp >= 11520 && cp <= 11567) return "ka";
|
|
301
|
+
if (cp >= 1328 && cp <= 1423 || cp >= 64275 && cp <= 64279) return "hy";
|
|
302
|
+
if (isEmojiCodepoint(cp)) return "emoji";
|
|
303
|
+
return null;
|
|
419
304
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
let i = 0;
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
305
|
+
|
|
306
|
+
// src/shaping/multi-font.ts
|
|
307
|
+
function splitTextByFont(str, fontEntries) {
|
|
308
|
+
if (!str || fontEntries.length === 0) return [];
|
|
309
|
+
if (fontEntries.length === 1) return [{ text: str, entry: fontEntries[0] }];
|
|
310
|
+
const runs = [];
|
|
311
|
+
let currentEntry = null;
|
|
312
|
+
let currentText = "";
|
|
313
|
+
for (let i = 0; i < str.length; ) {
|
|
314
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
315
|
+
const charLen = cp > 65535 ? 2 : 1;
|
|
316
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
317
|
+
const char = str.substring(i, i + charLen);
|
|
318
|
+
if (currentEntry && currentEntry.fontData.cmap[normCp]) {
|
|
319
|
+
currentText += char;
|
|
320
|
+
i += charLen;
|
|
435
321
|
continue;
|
|
436
322
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
323
|
+
let newEntry = null;
|
|
324
|
+
const charLang = detectCharLang(normCp);
|
|
325
|
+
if (charLang) {
|
|
326
|
+
for (const fe of fontEntries) {
|
|
327
|
+
if (fe.lang === charLang && fe.fontData.cmap[normCp]) {
|
|
328
|
+
newEntry = fe;
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
445
332
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
lastConsonantIdx = syllable.length;
|
|
452
|
-
syllable.push(cc);
|
|
453
|
-
i++;
|
|
454
|
-
if (i < cps.length && cps[i] === NUKTA) {
|
|
455
|
-
syllable.push(cps[i]);
|
|
456
|
-
i++;
|
|
333
|
+
if (!newEntry) {
|
|
334
|
+
for (const fe of fontEntries) {
|
|
335
|
+
if (fe.fontData.cmap[normCp]) {
|
|
336
|
+
newEntry = fe;
|
|
337
|
+
break;
|
|
457
338
|
}
|
|
458
|
-
if (i < cps.length && cps[i] === HALANT) {
|
|
459
|
-
if (i + 1 < cps.length && isConsonant(cps[i + 1])) {
|
|
460
|
-
syllable.push(cps[i]);
|
|
461
|
-
i++;
|
|
462
|
-
continue;
|
|
463
|
-
} else {
|
|
464
|
-
syllable.push(cps[i]);
|
|
465
|
-
i++;
|
|
466
|
-
break;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
break;
|
|
470
|
-
} else {
|
|
471
|
-
break;
|
|
472
339
|
}
|
|
473
340
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
syllable.push(cps[i]);
|
|
482
|
-
i++;
|
|
483
|
-
} else {
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
341
|
+
if (!newEntry) newEntry = fontEntries[0];
|
|
342
|
+
if (newEntry !== currentEntry) {
|
|
343
|
+
if (currentText && currentEntry) runs.push({ text: currentText, entry: currentEntry });
|
|
344
|
+
currentEntry = newEntry;
|
|
345
|
+
currentText = char;
|
|
346
|
+
} else {
|
|
347
|
+
currentText += char;
|
|
486
348
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
349
|
+
i += charLen;
|
|
350
|
+
}
|
|
351
|
+
if (currentText && currentEntry) runs.push({ text: currentText, entry: currentEntry });
|
|
352
|
+
return runs;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/shaping/bidi.ts
|
|
356
|
+
function classifyBidiType(cp) {
|
|
357
|
+
if (cp >= 768 && cp <= 879 || // Combining Diacritical Marks
|
|
358
|
+
cp >= 1425 && cp <= 1469 || // Hebrew marks
|
|
359
|
+
cp >= 1471 && cp <= 1471 || cp >= 1473 && cp <= 1474 || cp >= 1476 && cp <= 1477 || cp >= 1479 && cp <= 1479 || cp >= 1552 && cp <= 1562 || // Arabic marks
|
|
360
|
+
cp >= 1611 && cp <= 1631 || // Arabic harakat
|
|
361
|
+
cp >= 1648 && cp <= 1648 || // Arabic superscript alef
|
|
362
|
+
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 65056 && cp <= 65071) return "NSM";
|
|
363
|
+
if (cp === 8203 || cp === 8204 || cp === 8205 || cp === 8206 || cp === 8207 || cp === 65279 || cp === 8294 || cp === 8295 || cp === 8296 || cp === 8297) return "BN";
|
|
364
|
+
if (cp >= 1632 && cp <= 1641) return "AN";
|
|
365
|
+
if (cp >= 1776 && cp <= 1785) return "AN";
|
|
366
|
+
if (cp >= 48 && cp <= 57) return "EN";
|
|
367
|
+
if (cp === 43 || cp === 45) return "ES";
|
|
368
|
+
if (cp === 35 || cp === 36 || cp === 37 || cp === 162 || cp === 163 || cp === 164 || cp === 165 || cp === 8364 || cp === 8377 || cp === 8378) return "ET";
|
|
369
|
+
if (cp === 44 || cp === 46 || cp === 47 || cp === 58 || cp === 160) return "CS";
|
|
370
|
+
if (cp === 32 || cp === 9 || cp === 10 || cp === 13 || cp === 12 || cp === 8192 || cp === 8202 || cp === 8232 || cp === 8233 || cp === 8239 || cp === 8287 || cp === 12288) return "WS";
|
|
371
|
+
if (cp >= 1536 && cp <= 1791) return "AL";
|
|
372
|
+
if (cp >= 1872 && cp <= 1919) return "AL";
|
|
373
|
+
if (cp >= 2208 && cp <= 2303) return "AL";
|
|
374
|
+
if (cp >= 64336 && cp <= 65023) return "AL";
|
|
375
|
+
if (cp >= 65136 && cp <= 65278) return "AL";
|
|
376
|
+
if (cp >= 1488 && cp <= 1514) return "R";
|
|
377
|
+
if (cp >= 1520 && cp <= 1524) return "R";
|
|
378
|
+
if (cp >= 1792 && cp <= 1871) return "R";
|
|
379
|
+
if (cp >= 1920 && cp <= 1983) return "R";
|
|
380
|
+
if (cp >= 64285 && cp <= 64335) return "R";
|
|
381
|
+
if (cp >= 65 && cp <= 90) return "L";
|
|
382
|
+
if (cp >= 97 && cp <= 122) return "L";
|
|
383
|
+
if (cp >= 192 && cp <= 591) return "L";
|
|
384
|
+
if (cp >= 880 && cp <= 1023) return "L";
|
|
385
|
+
if (cp >= 1024 && cp <= 1279) return "L";
|
|
386
|
+
if (cp >= 3584 && cp <= 3711) return "L";
|
|
387
|
+
if (cp >= 2304 && cp <= 2431) return "L";
|
|
388
|
+
if (cp >= 12352 && cp <= 12543) return "L";
|
|
389
|
+
if (cp >= 19968 && cp <= 40959) return "L";
|
|
390
|
+
if (cp >= 44032 && cp <= 55215) return "L";
|
|
391
|
+
if (cp >= 33 && cp <= 47) return "ON";
|
|
392
|
+
if (cp >= 58 && cp <= 64) return "ON";
|
|
393
|
+
if (cp >= 91 && cp <= 96) return "ON";
|
|
394
|
+
if (cp >= 123 && cp <= 126) return "ON";
|
|
395
|
+
if (cp >= 161 && cp <= 191) return "ON";
|
|
396
|
+
if (cp >= 8208 && cp <= 8231) return "ON";
|
|
397
|
+
if (cp >= 8240 && cp <= 8286) return "ON";
|
|
398
|
+
return "L";
|
|
399
|
+
}
|
|
400
|
+
function detectParagraphLevel(types) {
|
|
401
|
+
for (const t of types) {
|
|
402
|
+
if (t === "L") return 0;
|
|
403
|
+
if (t === "R" || t === "AL") return 1;
|
|
404
|
+
}
|
|
405
|
+
return 0;
|
|
406
|
+
}
|
|
407
|
+
function resolveWeakTypes(types, paraLevel) {
|
|
408
|
+
const len = types.length;
|
|
409
|
+
let prevType = paraLevel === 0 ? "L" : "R";
|
|
410
|
+
for (let i = 0; i < len; i++) {
|
|
411
|
+
if (types[i] === "BN") continue;
|
|
412
|
+
if (types[i] === "NSM") {
|
|
413
|
+
types[i] = prevType;
|
|
490
414
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
415
|
+
prevType = types[i];
|
|
416
|
+
}
|
|
417
|
+
let lastStrong = paraLevel === 0 ? "L" : "R";
|
|
418
|
+
for (let i = 0; i < len; i++) {
|
|
419
|
+
if (types[i] === "BN") continue;
|
|
420
|
+
if (types[i] === "R" || types[i] === "L" || types[i] === "AL") {
|
|
421
|
+
lastStrong = types[i];
|
|
422
|
+
} else if (types[i] === "EN" && lastStrong === "AL") {
|
|
423
|
+
types[i] = "AN";
|
|
494
424
|
}
|
|
495
|
-
clusters.push({
|
|
496
|
-
codepoints: syllable,
|
|
497
|
-
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
498
|
-
// Adjust for reph prefix
|
|
499
|
-
hasReph,
|
|
500
|
-
preBaseMatras: preMatras
|
|
501
|
-
});
|
|
502
425
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
function shapeBengaliText(str, fontData) {
|
|
506
|
-
const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
507
|
-
const shaped = [];
|
|
508
|
-
function resolveGid(cp) {
|
|
509
|
-
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
510
|
-
return cmap[normCp] || 0;
|
|
426
|
+
for (let i = 0; i < len; i++) {
|
|
427
|
+
if (types[i] === "AL") types[i] = "R";
|
|
511
428
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if (
|
|
515
|
-
|
|
429
|
+
for (let i = 1; i < len - 1; i++) {
|
|
430
|
+
if (types[i] === "BN") continue;
|
|
431
|
+
if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
|
|
432
|
+
types[i] = "EN";
|
|
433
|
+
} else if (types[i] === "CS") {
|
|
434
|
+
if (types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
|
|
435
|
+
else if (types[i - 1] === "AN" && types[i + 1] === "AN") types[i] = "AN";
|
|
436
|
+
}
|
|
516
437
|
}
|
|
517
|
-
|
|
518
|
-
if (
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const compCount = entry.length - 1;
|
|
524
|
-
if (compCount > gids.length - 1) continue;
|
|
525
|
-
let match = true;
|
|
526
|
-
for (let ci = 0; ci < compCount; ci++) {
|
|
527
|
-
if (gids[1 + ci] !== entry[1 + ci]) {
|
|
528
|
-
match = false;
|
|
438
|
+
for (let i = 0; i < len; i++) {
|
|
439
|
+
if (types[i] === "ET") {
|
|
440
|
+
let found = false;
|
|
441
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
442
|
+
if (types[j] === "EN") {
|
|
443
|
+
found = true;
|
|
529
444
|
break;
|
|
530
445
|
}
|
|
446
|
+
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
447
|
+
}
|
|
448
|
+
if (!found) {
|
|
449
|
+
for (let j = i + 1; j < len; j++) {
|
|
450
|
+
if (types[j] === "EN") {
|
|
451
|
+
found = true;
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
455
|
+
}
|
|
531
456
|
}
|
|
532
|
-
if (
|
|
457
|
+
if (found) types[i] = "EN";
|
|
533
458
|
}
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
function getAdv(gid) {
|
|
537
|
-
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
538
|
-
}
|
|
539
|
-
function getBaseAnchor(baseGid, markClass) {
|
|
540
|
-
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
541
|
-
if (!base) return null;
|
|
542
|
-
return base[markClass] ?? null;
|
|
543
459
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
460
|
+
for (let i = 0; i < len; i++) {
|
|
461
|
+
if (types[i] === "ES" || types[i] === "ET" || types[i] === "CS") {
|
|
462
|
+
types[i] = "ON";
|
|
463
|
+
}
|
|
548
464
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
shaped.push({
|
|
557
|
-
gid,
|
|
558
|
-
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
559
|
-
dy: baseAnchorPt[1] - markAnchor.y,
|
|
560
|
-
isZeroAdvance: true
|
|
561
|
-
});
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
566
|
-
} else {
|
|
567
|
-
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
465
|
+
lastStrong = paraLevel === 0 ? "L" : "R";
|
|
466
|
+
for (let i = 0; i < len; i++) {
|
|
467
|
+
if (types[i] === "BN") continue;
|
|
468
|
+
if (types[i] === "L" || types[i] === "R") {
|
|
469
|
+
lastStrong = types[i];
|
|
470
|
+
} else if (types[i] === "EN" && lastStrong === "L") {
|
|
471
|
+
types[i] = "L";
|
|
568
472
|
}
|
|
569
473
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
474
|
+
}
|
|
475
|
+
function resolveNeutralTypes(types, paraLevel) {
|
|
476
|
+
const len = types.length;
|
|
477
|
+
const paraDir = paraLevel === 0 ? "L" : "R";
|
|
478
|
+
for (let i = 0; i < len; i++) {
|
|
479
|
+
if (types[i] !== "ON" && types[i] !== "WS" && types[i] !== "BN") continue;
|
|
480
|
+
const start = i;
|
|
481
|
+
while (i < len && (types[i] === "ON" || types[i] === "WS" || types[i] === "BN")) i++;
|
|
482
|
+
const end = i;
|
|
483
|
+
let prevStrong = paraDir;
|
|
484
|
+
for (let j = start - 1; j >= 0; j--) {
|
|
485
|
+
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
486
|
+
prevStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
580
487
|
break;
|
|
581
488
|
}
|
|
582
489
|
}
|
|
583
|
-
|
|
584
|
-
for (
|
|
585
|
-
if (
|
|
586
|
-
|
|
587
|
-
if (mCp === 2507) {
|
|
588
|
-
emitGlyph(resolveGid(2503), false);
|
|
589
|
-
splitPostComponents.push(2494);
|
|
590
|
-
} else if (mCp === 2508) {
|
|
591
|
-
emitGlyph(resolveGid(2503), false);
|
|
592
|
-
splitPostComponents.push(2519);
|
|
593
|
-
} else {
|
|
594
|
-
emitGlyph(resolveGid(mCp), false);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
const clusterGids = [];
|
|
599
|
-
const clusterEndIdx = [];
|
|
600
|
-
let matraStart = codepoints.length;
|
|
601
|
-
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
602
|
-
const ct = bengaliCharType(codepoints[ci]);
|
|
603
|
-
if (ct === 0 || ct === 7 || ct === 8) {
|
|
604
|
-
clusterGids.push(resolveGid(codepoints[ci]));
|
|
605
|
-
clusterEndIdx.push(ci);
|
|
606
|
-
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
607
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
608
|
-
} else {
|
|
609
|
-
matraStart = ci;
|
|
490
|
+
let nextStrong = paraDir;
|
|
491
|
+
for (let j = end; j < len; j++) {
|
|
492
|
+
if (types[j] === "L" || types[j] === "R" || types[j] === "EN" || types[j] === "AN") {
|
|
493
|
+
nextStrong = types[j] === "EN" || types[j] === "AN" ? "R" : types[j];
|
|
610
494
|
break;
|
|
611
495
|
}
|
|
612
496
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
emitGlyph(subLig.resultGid, false);
|
|
625
|
-
gi += subLig.consumed;
|
|
626
|
-
} else {
|
|
627
|
-
const origCi = clusterEndIdx[gi];
|
|
628
|
-
const ct = bengaliCharType(codepoints[origCi]);
|
|
629
|
-
if (ct === 7) {
|
|
630
|
-
emitGlyph(clusterGids[gi], true, baseGid);
|
|
631
|
-
} else {
|
|
632
|
-
emitGlyph(clusterGids[gi], false);
|
|
633
|
-
}
|
|
634
|
-
gi++;
|
|
635
|
-
}
|
|
636
|
-
}
|
|
497
|
+
const resolved = prevStrong === nextStrong ? prevStrong : paraDir;
|
|
498
|
+
for (let j = start; j < end; j++) {
|
|
499
|
+
types[j] = resolved;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
function assignLevels(types, paraLevel) {
|
|
504
|
+
const levels = [];
|
|
505
|
+
for (const t of types) {
|
|
506
|
+
if (paraLevel === 0) {
|
|
507
|
+
levels.push(t === "R" || t === "AN" ? 1 : 0);
|
|
637
508
|
} else {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
509
|
+
levels.push(t === "L" ? 2 : 1);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return levels;
|
|
513
|
+
}
|
|
514
|
+
var SENTENCE_PUNCT = /* @__PURE__ */ new Set([
|
|
515
|
+
46,
|
|
516
|
+
// .
|
|
517
|
+
44,
|
|
518
|
+
// ,
|
|
519
|
+
59,
|
|
520
|
+
// ;
|
|
521
|
+
58,
|
|
522
|
+
// :
|
|
523
|
+
33,
|
|
524
|
+
// !
|
|
525
|
+
63
|
|
526
|
+
// ?
|
|
527
|
+
]);
|
|
528
|
+
function fixPunctuationAffinity(types, codePoints, len) {
|
|
529
|
+
for (let i = 1; i < len; i++) {
|
|
530
|
+
if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
|
|
531
|
+
let prevIdx = i - 1;
|
|
532
|
+
while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
|
|
533
|
+
if (prevIdx >= 0 && types[prevIdx] === "L") {
|
|
534
|
+
types[i] = "L";
|
|
648
535
|
}
|
|
649
536
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function fixBracketPairing(types, codePoints, len) {
|
|
540
|
+
const OPEN_BRACKETS = {
|
|
541
|
+
40: 41,
|
|
542
|
+
// ( → )
|
|
543
|
+
91: 93,
|
|
544
|
+
// [ → ]
|
|
545
|
+
123: 125
|
|
546
|
+
// { → }
|
|
547
|
+
};
|
|
548
|
+
for (let i = 0; i < len; i++) {
|
|
549
|
+
const closer = OPEN_BRACKETS[codePoints[i]];
|
|
550
|
+
if (closer === void 0) continue;
|
|
551
|
+
let depth = 1;
|
|
552
|
+
let closeIdx = -1;
|
|
553
|
+
for (let j = i + 1; j < len; j++) {
|
|
554
|
+
if (codePoints[j] === codePoints[i]) depth++;
|
|
555
|
+
else if (codePoints[j] === closer) {
|
|
556
|
+
depth--;
|
|
557
|
+
if (depth === 0) {
|
|
558
|
+
closeIdx = j;
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
664
561
|
}
|
|
665
562
|
}
|
|
666
|
-
|
|
667
|
-
|
|
563
|
+
if (closeIdx === -1) continue;
|
|
564
|
+
let hasL = false;
|
|
565
|
+
for (let j = i + 1; j < closeIdx; j++) {
|
|
566
|
+
if (types[j] === "L") {
|
|
567
|
+
hasL = true;
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
668
570
|
}
|
|
669
|
-
if (
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
emitGlyph(raGid, true, baseGid);
|
|
673
|
-
emitGlyph(halantGid, true, baseGid);
|
|
571
|
+
if (hasL) {
|
|
572
|
+
types[i] = "L";
|
|
573
|
+
types[closeIdx] = "L";
|
|
674
574
|
}
|
|
675
575
|
}
|
|
676
|
-
return shaped;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
// src/shaping/tamil-shaper.ts
|
|
680
|
-
var PULLI = 3021;
|
|
681
|
-
function tamilCharType(cp) {
|
|
682
|
-
if (cp === PULLI) return 7;
|
|
683
|
-
if (cp === 2946 || cp === 2947) return 6;
|
|
684
|
-
if (cp >= 2949 && cp <= 2964) return 1;
|
|
685
|
-
if (cp >= 2965 && cp <= 3001) return 0;
|
|
686
|
-
if (cp === 3007) return 4;
|
|
687
|
-
if (cp === 3014) return 4;
|
|
688
|
-
if (cp === 3015) return 4;
|
|
689
|
-
if (cp === 3016) return 4;
|
|
690
|
-
if (cp === 3018) return 4;
|
|
691
|
-
if (cp === 3019) return 4;
|
|
692
|
-
if (cp === 3020) return 4;
|
|
693
|
-
if (cp === 3006) return 5;
|
|
694
|
-
if (cp === 3008) return 5;
|
|
695
|
-
if (cp === 3009 || cp === 3010) return 2;
|
|
696
|
-
if (cp === 3031) return 5;
|
|
697
|
-
if (cp >= 3006 && cp <= 3020) return 2;
|
|
698
|
-
if (cp >= 3046 && cp <= 3055) return 9;
|
|
699
|
-
if (cp >= 3056 && cp <= 3066) return 9;
|
|
700
|
-
if (cp === 3024) return 6;
|
|
701
|
-
return -1;
|
|
702
576
|
}
|
|
703
|
-
function
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
function buildTamilClusters(str) {
|
|
707
|
-
const clusters = [];
|
|
708
|
-
const cps = [];
|
|
709
|
-
for (let i2 = 0; i2 < str.length; ) {
|
|
710
|
-
const cp = str.codePointAt(i2) ?? 0;
|
|
711
|
-
cps.push(cp);
|
|
712
|
-
i2 += cp > 65535 ? 2 : 1;
|
|
713
|
-
}
|
|
577
|
+
function findOutermostIsolatePairs(codePoints) {
|
|
578
|
+
const pairs = [];
|
|
714
579
|
let i = 0;
|
|
715
|
-
while (i <
|
|
716
|
-
const cp =
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
i
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const cc = cps[i];
|
|
728
|
-
const ct = tamilCharType(cc);
|
|
729
|
-
if (ct === 0) {
|
|
730
|
-
lastConsonantIdx = syllable.length;
|
|
731
|
-
syllable.push(cc);
|
|
732
|
-
i++;
|
|
733
|
-
if (i < cps.length && cps[i] === PULLI) {
|
|
734
|
-
if (i + 1 < cps.length && isConsonant2(cps[i + 1])) {
|
|
735
|
-
syllable.push(cps[i]);
|
|
736
|
-
i++;
|
|
737
|
-
continue;
|
|
738
|
-
} else {
|
|
739
|
-
syllable.push(cps[i]);
|
|
740
|
-
i++;
|
|
580
|
+
while (i < codePoints.length) {
|
|
581
|
+
const cp = codePoints[i];
|
|
582
|
+
if (cp === 8294 || cp === 8295 || cp === 8296) {
|
|
583
|
+
let depth = 1;
|
|
584
|
+
let close = -1;
|
|
585
|
+
for (let j = i + 1; j < codePoints.length; j++) {
|
|
586
|
+
const cj = codePoints[j];
|
|
587
|
+
if (cj === 8294 || cj === 8295 || cj === 8296) depth++;
|
|
588
|
+
else if (cj === 8297) {
|
|
589
|
+
depth--;
|
|
590
|
+
if (depth === 0) {
|
|
591
|
+
close = j;
|
|
741
592
|
break;
|
|
742
593
|
}
|
|
743
594
|
}
|
|
744
|
-
break;
|
|
745
|
-
} else {
|
|
746
|
-
break;
|
|
747
595
|
}
|
|
748
|
-
|
|
749
|
-
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
750
|
-
while (i < cps.length) {
|
|
751
|
-
const ct = tamilCharType(cps[i]);
|
|
752
|
-
if (ct >= 2 && ct <= 5) {
|
|
753
|
-
if (ct === 4) {
|
|
754
|
-
preMatras.push(syllable.length);
|
|
755
|
-
}
|
|
756
|
-
syllable.push(cps[i]);
|
|
596
|
+
if (close === -1) {
|
|
757
597
|
i++;
|
|
758
|
-
|
|
759
|
-
break;
|
|
598
|
+
continue;
|
|
760
599
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
}
|
|
766
|
-
if (syllable.length === 0) {
|
|
767
|
-
syllable.push(cps[i] ?? 32);
|
|
600
|
+
const kind = cp === 8294 ? "LRI" : cp === 8295 ? "RLI" : "FSI";
|
|
601
|
+
pairs.push({ open: i, close, kind });
|
|
602
|
+
i = close + 1;
|
|
603
|
+
} else {
|
|
768
604
|
i++;
|
|
769
605
|
}
|
|
770
|
-
clusters.push({
|
|
771
|
-
codepoints: syllable,
|
|
772
|
-
baseIndex: baseIdx,
|
|
773
|
-
preBaseMatras: preMatras
|
|
774
|
-
});
|
|
775
606
|
}
|
|
776
|
-
return
|
|
607
|
+
return pairs;
|
|
777
608
|
}
|
|
778
|
-
function
|
|
779
|
-
const
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
609
|
+
function normalizeBidiEmbeddings(text) {
|
|
610
|
+
const LRE = 8234, RLE = 8235, PDF_CP = 8236, LRO = 8237, RLO = 8238;
|
|
611
|
+
const LRI = 8294, RLI = 8295, PDI = 8297;
|
|
612
|
+
let hasEmbed = false;
|
|
613
|
+
for (let i = 0; i < text.length; i++) {
|
|
614
|
+
const c = text.charCodeAt(i);
|
|
615
|
+
if (c === LRE || c === RLE || c === PDF_CP || c === LRO || c === RLO) {
|
|
616
|
+
hasEmbed = true;
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
784
619
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
620
|
+
if (!hasEmbed) return text;
|
|
621
|
+
const stack = [];
|
|
622
|
+
const out = [];
|
|
623
|
+
const MAX_DEPTH = 125;
|
|
624
|
+
for (let i = 0; i < text.length; ) {
|
|
625
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
626
|
+
const cpLen = cp > 65535 ? 2 : 1;
|
|
627
|
+
if (cp === LRE || cp === RLO || cp === LRO || cp === RLE) {
|
|
628
|
+
if (stack.length >= MAX_DEPTH) {
|
|
629
|
+
i += cpLen;
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
stack.push(1);
|
|
633
|
+
out.push(cp === LRE || cp === LRO ? LRI : RLI);
|
|
634
|
+
i += cpLen;
|
|
635
|
+
} else if (cp === PDF_CP) {
|
|
636
|
+
if (stack.pop()) {
|
|
637
|
+
out.push(PDI);
|
|
638
|
+
}
|
|
639
|
+
i += cpLen;
|
|
640
|
+
} else {
|
|
641
|
+
out.push(cp);
|
|
642
|
+
i += cpLen;
|
|
801
643
|
}
|
|
802
|
-
return null;
|
|
803
644
|
}
|
|
804
|
-
|
|
805
|
-
|
|
645
|
+
let result = "";
|
|
646
|
+
for (let i = 0; i < out.length; i++) result += String.fromCodePoint(out[i]);
|
|
647
|
+
return result;
|
|
648
|
+
}
|
|
649
|
+
function resolveBidiRuns(text) {
|
|
650
|
+
if (!text) return [];
|
|
651
|
+
const normalized = normalizeBidiEmbeddings(text);
|
|
652
|
+
if (normalized !== text) {
|
|
653
|
+
return resolveBidiRuns(normalized);
|
|
806
654
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
655
|
+
const codePoints = [];
|
|
656
|
+
const cpToStr = [];
|
|
657
|
+
for (let i = 0; i < text.length; ) {
|
|
658
|
+
cpToStr.push(i);
|
|
659
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
660
|
+
codePoints.push(cp);
|
|
661
|
+
i += cp > 65535 ? 2 : 1;
|
|
811
662
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
return
|
|
663
|
+
cpToStr.push(text.length);
|
|
664
|
+
const isolates = findOutermostIsolatePairs(codePoints);
|
|
665
|
+
if (isolates.length === 0) {
|
|
666
|
+
return resolveBidiCore(text, codePoints, cpToStr);
|
|
667
|
+
}
|
|
668
|
+
const insideIsolate = new Array(codePoints.length).fill(false);
|
|
669
|
+
for (const p of isolates) {
|
|
670
|
+
for (let k = p.open; k <= p.close; k++) insideIsolate[k] = true;
|
|
671
|
+
}
|
|
672
|
+
const outerTypes = codePoints.map((cp, idx) => insideIsolate[idx] ? "BN" : classifyBidiType(cp));
|
|
673
|
+
const parentLevel = detectParagraphLevel(outerTypes);
|
|
674
|
+
const out = [];
|
|
675
|
+
const emitSegment = (cpStart, cpEnd, forced) => {
|
|
676
|
+
if (cpStart >= cpEnd) return;
|
|
677
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
678
|
+
const segCps = codePoints.slice(cpStart, cpEnd);
|
|
679
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
680
|
+
const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
|
|
681
|
+
const segRuns = forced === void 0 ? resolveBidiRuns(segText) : resolveBidiCore(segText, segCps, segCpToStr, forced);
|
|
682
|
+
for (const r of segRuns) {
|
|
683
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
let cursor = 0;
|
|
687
|
+
for (const pair of isolates) {
|
|
688
|
+
emitSegment(cursor, pair.open, parentLevel);
|
|
689
|
+
const innerStart = pair.open + 1;
|
|
690
|
+
const innerEnd = pair.close;
|
|
691
|
+
let innerLevel;
|
|
692
|
+
if (pair.kind === "LRI") innerLevel = 0;
|
|
693
|
+
else if (pair.kind === "RLI") innerLevel = 1;
|
|
694
|
+
else {
|
|
695
|
+
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
696
|
+
innerLevel = detectParagraphLevel(innerTypes);
|
|
697
|
+
}
|
|
698
|
+
if (innerStart < innerEnd) {
|
|
699
|
+
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
700
|
+
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
701
|
+
const baseStrIdx = cpToStr[innerStart];
|
|
702
|
+
for (const r of innerRuns) {
|
|
703
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
cursor = pair.close + 1;
|
|
707
|
+
}
|
|
708
|
+
emitSegment(cursor, codePoints.length, parentLevel);
|
|
709
|
+
return out;
|
|
710
|
+
}
|
|
711
|
+
function resolveBidiRunsForced(text, forcedLevel) {
|
|
712
|
+
if (!text) return [];
|
|
713
|
+
const codePoints = [];
|
|
714
|
+
const cpToStr = [];
|
|
715
|
+
for (let i = 0; i < text.length; ) {
|
|
716
|
+
cpToStr.push(i);
|
|
717
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
718
|
+
codePoints.push(cp);
|
|
719
|
+
i += cp > 65535 ? 2 : 1;
|
|
816
720
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
721
|
+
cpToStr.push(text.length);
|
|
722
|
+
const isolates = findOutermostIsolatePairs(codePoints);
|
|
723
|
+
if (isolates.length === 0) {
|
|
724
|
+
return resolveBidiCore(text, codePoints, cpToStr, forcedLevel);
|
|
725
|
+
}
|
|
726
|
+
const out = [];
|
|
727
|
+
const emit = (cpStart, cpEnd, forced) => {
|
|
728
|
+
if (cpStart >= cpEnd) return;
|
|
729
|
+
const segText = text.substring(cpToStr[cpStart], cpToStr[cpEnd]);
|
|
730
|
+
const segCps = codePoints.slice(cpStart, cpEnd);
|
|
731
|
+
const baseStrIdx = cpToStr[cpStart];
|
|
732
|
+
const segCpToStr = cpToStr.slice(cpStart, cpEnd + 1).map((x) => x - baseStrIdx);
|
|
733
|
+
const segRuns = resolveBidiCore(segText, segCps, segCpToStr, forced);
|
|
734
|
+
for (const r of segRuns) {
|
|
735
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
let cursor = 0;
|
|
739
|
+
for (const pair of isolates) {
|
|
740
|
+
emit(cursor, pair.open, forcedLevel);
|
|
741
|
+
const innerStart = pair.open + 1;
|
|
742
|
+
const innerEnd = pair.close;
|
|
743
|
+
let innerLevel;
|
|
744
|
+
if (pair.kind === "LRI") innerLevel = 0;
|
|
745
|
+
else if (pair.kind === "RLI") innerLevel = 1;
|
|
746
|
+
else {
|
|
747
|
+
const innerTypes = codePoints.slice(innerStart, innerEnd).map(classifyBidiType);
|
|
748
|
+
innerLevel = detectParagraphLevel(innerTypes);
|
|
749
|
+
}
|
|
750
|
+
if (innerStart < innerEnd) {
|
|
751
|
+
const innerText = text.substring(cpToStr[innerStart], cpToStr[innerEnd]);
|
|
752
|
+
const innerRuns = resolveBidiRunsForced(innerText, innerLevel);
|
|
753
|
+
const baseStrIdx = cpToStr[innerStart];
|
|
754
|
+
for (const r of innerRuns) {
|
|
755
|
+
out.push({ text: r.text, level: r.level, start: r.start + baseStrIdx });
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
cursor = pair.close + 1;
|
|
759
|
+
}
|
|
760
|
+
emit(cursor, codePoints.length, forcedLevel);
|
|
761
|
+
return out;
|
|
762
|
+
}
|
|
763
|
+
function resolveBidiCore(text, codePoints, cpToStr, forcedLevel) {
|
|
764
|
+
const len = codePoints.length;
|
|
765
|
+
if (len === 0) return [];
|
|
766
|
+
const types = codePoints.map(classifyBidiType);
|
|
767
|
+
const paraLevel = forcedLevel !== void 0 ? forcedLevel : detectParagraphLevel(types);
|
|
768
|
+
resolveWeakTypes(types, paraLevel);
|
|
769
|
+
resolveNeutralTypes(types, paraLevel);
|
|
770
|
+
if (paraLevel === 1) {
|
|
771
|
+
fixPunctuationAffinity(types, codePoints, len);
|
|
772
|
+
fixBracketPairing(types, codePoints, len);
|
|
773
|
+
}
|
|
774
|
+
const levels = assignLevels(types, paraLevel);
|
|
775
|
+
const runs = [];
|
|
776
|
+
let runStart = 0;
|
|
777
|
+
let runLevel = levels[0];
|
|
778
|
+
for (let i = 1; i <= len; i++) {
|
|
779
|
+
if (i === len || levels[i] !== runLevel) {
|
|
780
|
+
const start = cpToStr[runStart];
|
|
781
|
+
const end = cpToStr[i];
|
|
782
|
+
let runText = text.substring(start, end);
|
|
783
|
+
if (runLevel % 2 === 1) {
|
|
784
|
+
runText = reverseString(runText);
|
|
785
|
+
}
|
|
786
|
+
runs.push({ text: runText, level: runLevel, start });
|
|
787
|
+
if (i < len) {
|
|
788
|
+
runStart = i;
|
|
789
|
+
runLevel = levels[i];
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
if (paraLevel === 1 && runs.length > 1) {
|
|
794
|
+
runs.reverse();
|
|
795
|
+
}
|
|
796
|
+
return runs;
|
|
797
|
+
}
|
|
798
|
+
function containsRTL(text) {
|
|
799
|
+
for (let i = 0; i < text.length; ) {
|
|
800
|
+
const cp = text.codePointAt(i) ?? 0;
|
|
801
|
+
const t = classifyBidiType(cp);
|
|
802
|
+
if (t === "R" || t === "AL") return true;
|
|
803
|
+
i += cp > 65535 ? 2 : 1;
|
|
804
|
+
}
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
function stripBidiControls(text) {
|
|
808
|
+
if (!text) return text;
|
|
809
|
+
let needs = false;
|
|
810
|
+
for (let i = 0; i < text.length; i++) {
|
|
811
|
+
const c = text.charCodeAt(i);
|
|
812
|
+
if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) {
|
|
813
|
+
needs = true;
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (!needs) return text;
|
|
818
|
+
let out = "";
|
|
819
|
+
for (let i = 0; i < text.length; i++) {
|
|
820
|
+
const c = text.charCodeAt(i);
|
|
821
|
+
if (c === 8206 || c === 8207 || c >= 8234 && c <= 8238 || c >= 8294 && c <= 8297) continue;
|
|
822
|
+
out += text[i];
|
|
823
|
+
}
|
|
824
|
+
return out;
|
|
825
|
+
}
|
|
826
|
+
function reverseString(str) {
|
|
827
|
+
const cps = [];
|
|
828
|
+
for (let i = 0; i < str.length; ) {
|
|
829
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
830
|
+
cps.push(cp);
|
|
831
|
+
i += cp > 65535 ? 2 : 1;
|
|
832
|
+
}
|
|
833
|
+
cps.reverse();
|
|
834
|
+
return String.fromCodePoint(...cps);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/fonts/encoding.ts
|
|
838
|
+
function toWinAnsi(str) {
|
|
839
|
+
if (!str) return "";
|
|
840
|
+
let r = "";
|
|
841
|
+
for (let i = 0; i < str.length; i++) {
|
|
842
|
+
const c = str.charCodeAt(i);
|
|
843
|
+
if (c >= 32 && c <= 126) r += str[i];
|
|
844
|
+
else if (c >= 160 && c <= 255) r += str[i];
|
|
845
|
+
else if (c === 8364) r += "\x80";
|
|
846
|
+
else if (c === 8218) r += "\x82";
|
|
847
|
+
else if (c === 402) r += "\x83";
|
|
848
|
+
else if (c === 8222) r += "\x84";
|
|
849
|
+
else if (c === 8230) r += "\x85";
|
|
850
|
+
else if (c === 8224) r += "\x86";
|
|
851
|
+
else if (c === 8225) r += "\x87";
|
|
852
|
+
else if (c === 710) r += "\x88";
|
|
853
|
+
else if (c === 8240) r += "\x89";
|
|
854
|
+
else if (c === 352) r += "\x8A";
|
|
855
|
+
else if (c === 8249) r += "\x8B";
|
|
856
|
+
else if (c === 338) r += "\x8C";
|
|
857
|
+
else if (c === 381) r += "\x8E";
|
|
858
|
+
else if (c === 8216) r += "\x91";
|
|
859
|
+
else if (c === 8217) r += "\x92";
|
|
860
|
+
else if (c === 8220) r += "\x93";
|
|
861
|
+
else if (c === 8221) r += "\x94";
|
|
862
|
+
else if (c === 8226) r += "\x95";
|
|
863
|
+
else if (c === 8211) r += "\x96";
|
|
864
|
+
else if (c === 8212) r += "\x97";
|
|
865
|
+
else if (c === 732) r += "\x98";
|
|
866
|
+
else if (c === 8482) r += "\x99";
|
|
867
|
+
else if (c === 353) r += "\x9A";
|
|
868
|
+
else if (c === 8250) r += "\x9B";
|
|
869
|
+
else if (c === 339) r += "\x9C";
|
|
870
|
+
else if (c === 382) r += "\x9E";
|
|
871
|
+
else if (c === 376) r += "\x9F";
|
|
872
|
+
else if (c === 160 || c === 8239) r += " ";
|
|
873
|
+
else if (c === 9 || c === 10 || c === 13) r += " ";
|
|
874
|
+
else if (c < 32) ; else r += "?";
|
|
875
|
+
}
|
|
876
|
+
return r;
|
|
877
|
+
}
|
|
878
|
+
function pdfString(str) {
|
|
879
|
+
const s = toWinAnsi(stripBidiControls(str));
|
|
880
|
+
return "(" + s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)") + ")";
|
|
881
|
+
}
|
|
882
|
+
function truncate(str, max) {
|
|
883
|
+
if (!str || str.length <= max) return str || "";
|
|
884
|
+
if (max <= 1) return "\u2026";
|
|
885
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
886
|
+
}
|
|
887
|
+
function helveticaWidth(str, sz) {
|
|
888
|
+
str = stripBidiControls(str);
|
|
889
|
+
let w = 0;
|
|
890
|
+
for (let i = 0; i < str.length; i++) {
|
|
891
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
892
|
+
if (cp > 65535) i++;
|
|
893
|
+
if (cp >= 48 && cp <= 57) w += 556;
|
|
894
|
+
else if (cp >= 65 && cp <= 90) w += 680;
|
|
895
|
+
else if (cp >= 97 && cp <= 122) w += 500;
|
|
896
|
+
else if (cp === 32) w += 278;
|
|
897
|
+
else if (cp === 46 || cp === 44) w += 278;
|
|
898
|
+
else if (cp === 43) w += 584;
|
|
899
|
+
else if (cp === 45) w += 333;
|
|
900
|
+
else if (cp === 47 || cp === 58) w += 278;
|
|
901
|
+
else if (cp === 8212) w += 1e3;
|
|
902
|
+
else if (cp === 8211) w += 556;
|
|
903
|
+
else if (cp === 8230) w += 1e3;
|
|
904
|
+
else if (cp === 8216 || cp === 8217) w += 222;
|
|
905
|
+
else if (cp === 8220 || cp === 8221) w += 333;
|
|
906
|
+
else if (cp === 8364) w += 556;
|
|
907
|
+
else w += 556;
|
|
908
|
+
}
|
|
909
|
+
return w * sz / 1e3;
|
|
910
|
+
}
|
|
911
|
+
function helveticaBoldWidth(str, sz) {
|
|
912
|
+
str = stripBidiControls(str);
|
|
913
|
+
let w = 0;
|
|
914
|
+
for (let i = 0; i < str.length; i++) {
|
|
915
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
916
|
+
if (cp > 65535) i++;
|
|
917
|
+
if (cp >= 48 && cp <= 57) w += 556;
|
|
918
|
+
else if (cp >= 65 && cp <= 90) w += 722;
|
|
919
|
+
else if (cp >= 97 && cp <= 122) w += 611;
|
|
920
|
+
else if (cp === 32) w += 278;
|
|
921
|
+
else if (cp === 46 || cp === 44) w += 278;
|
|
922
|
+
else if (cp === 43) w += 584;
|
|
923
|
+
else if (cp === 45) w += 333;
|
|
924
|
+
else if (cp === 47 || cp === 58) w += 278;
|
|
925
|
+
else if (cp === 8212) w += 1e3;
|
|
926
|
+
else if (cp === 8211) w += 556;
|
|
927
|
+
else if (cp === 8230) w += 1e3;
|
|
928
|
+
else if (cp === 8216 || cp === 8217) w += 278;
|
|
929
|
+
else if (cp === 8220 || cp === 8221) w += 500;
|
|
930
|
+
else if (cp === 8364) w += 556;
|
|
931
|
+
else w += 611;
|
|
932
|
+
}
|
|
933
|
+
return w * sz / 1e3;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// src/shaping/gsub-driver.ts
|
|
937
|
+
function tryLigature(gids, ligatures) {
|
|
938
|
+
if (!ligatures || gids.length < 2) return null;
|
|
939
|
+
const firstGid = gids[0];
|
|
940
|
+
const entries = ligatures[firstGid];
|
|
941
|
+
if (!entries) return null;
|
|
942
|
+
for (const entry of entries) {
|
|
943
|
+
const compCount = entry.length - 1;
|
|
944
|
+
if (compCount > gids.length - 1) continue;
|
|
945
|
+
let match = true;
|
|
946
|
+
for (let ci = 0; ci < compCount; ci++) {
|
|
947
|
+
if (gids[1 + ci] !== entry[1 + ci]) {
|
|
948
|
+
match = false;
|
|
949
|
+
break;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (match) return { resultGid: entry[0], consumed: compCount + 1 };
|
|
953
|
+
}
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// src/shaping/bengali-shaper.ts
|
|
958
|
+
var HALANT = 2509;
|
|
959
|
+
var NUKTA = 2492;
|
|
960
|
+
var RA = 2480;
|
|
961
|
+
function bengaliCharType(cp) {
|
|
962
|
+
if (cp === HALANT) return 7;
|
|
963
|
+
if (cp === NUKTA) return 8;
|
|
964
|
+
if (cp >= 2433 && cp <= 2435) return 6;
|
|
965
|
+
if (cp >= 2437 && cp <= 2452) return 1;
|
|
966
|
+
if (cp >= 2453 && cp <= 2489) return 0;
|
|
967
|
+
if (cp === 2495) return 4;
|
|
968
|
+
if (cp === 2503) return 4;
|
|
969
|
+
if (cp === 2504) return 4;
|
|
970
|
+
if (cp === 2497 || cp === 2498 || cp === 2499 || cp === 2500) return 3;
|
|
971
|
+
if (cp === 2494) return 5;
|
|
972
|
+
if (cp === 2496) return 5;
|
|
973
|
+
if (cp === 2507) return 4;
|
|
974
|
+
if (cp === 2508) return 4;
|
|
975
|
+
if (cp >= 2494 && cp <= 2508) return 2;
|
|
976
|
+
if (cp === 2519) return 5;
|
|
977
|
+
if (cp >= 2534 && cp <= 2543) return 9;
|
|
978
|
+
if (cp === 2527) return 0;
|
|
979
|
+
if (cp === 2493) return 1;
|
|
980
|
+
if (cp === 2510) return 0;
|
|
981
|
+
return -1;
|
|
982
|
+
}
|
|
983
|
+
function isConsonant(cp) {
|
|
984
|
+
return bengaliCharType(cp) === 0;
|
|
985
|
+
}
|
|
986
|
+
function buildBengaliClusters(str) {
|
|
987
|
+
const clusters = [];
|
|
988
|
+
const cps = [];
|
|
989
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
990
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
991
|
+
cps.push(cp);
|
|
992
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
993
|
+
}
|
|
994
|
+
let i = 0;
|
|
995
|
+
while (i < cps.length) {
|
|
996
|
+
const cp = cps[i];
|
|
997
|
+
const type = bengaliCharType(cp);
|
|
998
|
+
if (type < 0 || cp < BENGALI_START || cp > BENGALI_END) {
|
|
999
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
1000
|
+
i++;
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
const syllable = [];
|
|
1004
|
+
let baseIdx = 0;
|
|
1005
|
+
let hasReph = false;
|
|
1006
|
+
const preMatras = [];
|
|
1007
|
+
if (isConsonant(cp) && cp === RA && i + 2 < cps.length && cps[i + 1] === HALANT && isConsonant(cps[i + 2])) {
|
|
1008
|
+
hasReph = true;
|
|
1009
|
+
syllable.push(cp, cps[i + 1]);
|
|
1010
|
+
i += 2;
|
|
1011
|
+
}
|
|
1012
|
+
let lastConsonantIdx = -1;
|
|
1013
|
+
while (i < cps.length) {
|
|
1014
|
+
const cc = cps[i];
|
|
1015
|
+
const ct = bengaliCharType(cc);
|
|
1016
|
+
if (ct === 0) {
|
|
1017
|
+
lastConsonantIdx = syllable.length;
|
|
1018
|
+
syllable.push(cc);
|
|
1019
|
+
i++;
|
|
1020
|
+
if (i < cps.length && cps[i] === NUKTA) {
|
|
1021
|
+
syllable.push(cps[i]);
|
|
1022
|
+
i++;
|
|
1023
|
+
}
|
|
1024
|
+
if (i < cps.length && cps[i] === HALANT) {
|
|
1025
|
+
if (i + 1 < cps.length && isConsonant(cps[i + 1])) {
|
|
1026
|
+
syllable.push(cps[i]);
|
|
1027
|
+
i++;
|
|
1028
|
+
continue;
|
|
1029
|
+
} else {
|
|
1030
|
+
syllable.push(cps[i]);
|
|
1031
|
+
i++;
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
break;
|
|
1036
|
+
} else {
|
|
1037
|
+
break;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1041
|
+
while (i < cps.length) {
|
|
1042
|
+
const ct = bengaliCharType(cps[i]);
|
|
1043
|
+
if (ct >= 2 && ct <= 5) {
|
|
1044
|
+
if (ct === 4) {
|
|
1045
|
+
preMatras.push(syllable.length);
|
|
1046
|
+
}
|
|
1047
|
+
syllable.push(cps[i]);
|
|
1048
|
+
i++;
|
|
1049
|
+
} else {
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
while (i < cps.length && bengaliCharType(cps[i]) === 6) {
|
|
1054
|
+
syllable.push(cps[i]);
|
|
1055
|
+
i++;
|
|
1056
|
+
}
|
|
1057
|
+
if (syllable.length === 0) {
|
|
1058
|
+
syllable.push(cps[i] ?? 32);
|
|
1059
|
+
i++;
|
|
1060
|
+
}
|
|
1061
|
+
clusters.push({
|
|
1062
|
+
codepoints: syllable,
|
|
1063
|
+
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
1064
|
+
// Adjust for reph prefix
|
|
1065
|
+
hasReph,
|
|
1066
|
+
preBaseMatras: preMatras
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
return clusters;
|
|
1070
|
+
}
|
|
1071
|
+
function shapeBengaliText(str, fontData) {
|
|
1072
|
+
const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1073
|
+
const shaped = [];
|
|
1074
|
+
function resolveGid(cp) {
|
|
1075
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1076
|
+
return cmap[normCp] || 0;
|
|
1077
|
+
}
|
|
1078
|
+
function resolveGidGsub(cp) {
|
|
1079
|
+
const gid = resolveGid(cp);
|
|
1080
|
+
if (gsub[gid] !== void 0) return gsub[gid];
|
|
1081
|
+
return gid;
|
|
1082
|
+
}
|
|
1083
|
+
function tryLig(gids) {
|
|
1084
|
+
return tryLigature(gids, ligatures);
|
|
1085
|
+
}
|
|
1086
|
+
function getAdv(gid) {
|
|
1087
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1088
|
+
}
|
|
1089
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
1090
|
+
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
1091
|
+
if (!base) return null;
|
|
1092
|
+
return base[markClass] ?? null;
|
|
1093
|
+
}
|
|
1094
|
+
function getMarkAnchor2(markGid) {
|
|
1095
|
+
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
1096
|
+
if (!mark) return null;
|
|
1097
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1098
|
+
}
|
|
1099
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
1100
|
+
if (isZero && baseGid !== void 0) {
|
|
1101
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
1102
|
+
if (markAnchor) {
|
|
1103
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
1104
|
+
if (baseAnchorPt) {
|
|
1105
|
+
const baseAdv = getAdv(baseGid);
|
|
1106
|
+
shaped.push({
|
|
1107
|
+
gid,
|
|
1108
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
827
1109
|
dy: baseAnchorPt[1] - markAnchor.y,
|
|
828
1110
|
isZeroAdvance: true
|
|
829
1111
|
});
|
|
@@ -835,12 +1117,13 @@ function shapeTamilText(str, fontData) {
|
|
|
835
1117
|
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
836
1118
|
}
|
|
837
1119
|
}
|
|
838
|
-
const clusters =
|
|
1120
|
+
const clusters = buildBengaliClusters(str);
|
|
839
1121
|
for (const cluster of clusters) {
|
|
840
|
-
const { codepoints, preBaseMatras } = cluster;
|
|
1122
|
+
const { codepoints, hasReph, preBaseMatras } = cluster;
|
|
1123
|
+
const baseStart = hasReph ? 2 : 0;
|
|
841
1124
|
let baseGid = 0;
|
|
842
|
-
for (let ci =
|
|
843
|
-
const ct =
|
|
1125
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1126
|
+
const ct = bengaliCharType(codepoints[ci]);
|
|
844
1127
|
if (ct === 0) {
|
|
845
1128
|
baseGid = resolveGid(codepoints[ci]);
|
|
846
1129
|
} else if (ct >= 2) {
|
|
@@ -851,15 +1134,12 @@ function shapeTamilText(str, fontData) {
|
|
|
851
1134
|
for (const mIdx of preBaseMatras) {
|
|
852
1135
|
if (mIdx < codepoints.length) {
|
|
853
1136
|
const mCp = codepoints[mIdx];
|
|
854
|
-
if (mCp ===
|
|
855
|
-
emitGlyph(resolveGid(
|
|
856
|
-
splitPostComponents.push(
|
|
857
|
-
} else if (mCp ===
|
|
858
|
-
emitGlyph(resolveGid(
|
|
859
|
-
splitPostComponents.push(
|
|
860
|
-
} else if (mCp === 3020) {
|
|
861
|
-
emitGlyph(resolveGid(3014), false);
|
|
862
|
-
splitPostComponents.push(3031);
|
|
1137
|
+
if (mCp === 2507) {
|
|
1138
|
+
emitGlyph(resolveGid(2503), false);
|
|
1139
|
+
splitPostComponents.push(2494);
|
|
1140
|
+
} else if (mCp === 2508) {
|
|
1141
|
+
emitGlyph(resolveGid(2503), false);
|
|
1142
|
+
splitPostComponents.push(2519);
|
|
863
1143
|
} else {
|
|
864
1144
|
emitGlyph(resolveGid(mCp), false);
|
|
865
1145
|
}
|
|
@@ -868,22 +1148,20 @@ function shapeTamilText(str, fontData) {
|
|
|
868
1148
|
const clusterGids = [];
|
|
869
1149
|
const clusterEndIdx = [];
|
|
870
1150
|
let matraStart = codepoints.length;
|
|
871
|
-
for (let ci =
|
|
872
|
-
const ct =
|
|
873
|
-
if (ct === 0 || ct === 7) {
|
|
1151
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1152
|
+
const ct = bengaliCharType(codepoints[ci]);
|
|
1153
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
874
1154
|
clusterGids.push(resolveGid(codepoints[ci]));
|
|
875
1155
|
clusterEndIdx.push(ci);
|
|
876
|
-
} else if (ct
|
|
1156
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1157
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1158
|
+
} else {
|
|
877
1159
|
matraStart = ci;
|
|
878
1160
|
break;
|
|
879
|
-
} else if (ct < 0) {
|
|
880
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
881
|
-
} else if (ct === 1) {
|
|
882
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
883
1161
|
}
|
|
884
1162
|
}
|
|
885
1163
|
let ligConsumed = 0;
|
|
886
|
-
const ligResult =
|
|
1164
|
+
const ligResult = tryLig(clusterGids);
|
|
887
1165
|
if (ligResult) {
|
|
888
1166
|
emitGlyph(ligResult.resultGid, false);
|
|
889
1167
|
baseGid = ligResult.resultGid;
|
|
@@ -891,13 +1169,13 @@ function shapeTamilText(str, fontData) {
|
|
|
891
1169
|
let gi = ligConsumed;
|
|
892
1170
|
while (gi < clusterGids.length) {
|
|
893
1171
|
const subSeq = clusterGids.slice(gi);
|
|
894
|
-
const subLig =
|
|
1172
|
+
const subLig = tryLig(subSeq);
|
|
895
1173
|
if (subLig) {
|
|
896
1174
|
emitGlyph(subLig.resultGid, false);
|
|
897
1175
|
gi += subLig.consumed;
|
|
898
1176
|
} else {
|
|
899
1177
|
const origCi = clusterEndIdx[gi];
|
|
900
|
-
const ct =
|
|
1178
|
+
const ct = bengaliCharType(codepoints[origCi]);
|
|
901
1179
|
if (ct === 7) {
|
|
902
1180
|
emitGlyph(clusterGids[gi], true, baseGid);
|
|
903
1181
|
} else {
|
|
@@ -907,19 +1185,21 @@ function shapeTamilText(str, fontData) {
|
|
|
907
1185
|
}
|
|
908
1186
|
}
|
|
909
1187
|
} else {
|
|
910
|
-
for (let ci =
|
|
1188
|
+
for (let ci = baseStart; ci < matraStart; ci++) {
|
|
911
1189
|
const cp = codepoints[ci];
|
|
912
|
-
const ct =
|
|
1190
|
+
const ct = bengaliCharType(cp);
|
|
913
1191
|
if (ct === 0) {
|
|
914
1192
|
emitGlyph(resolveGid(cp), false);
|
|
915
1193
|
} else if (ct === 7) {
|
|
916
1194
|
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1195
|
+
} else if (ct === 8) {
|
|
1196
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
917
1197
|
}
|
|
918
1198
|
}
|
|
919
1199
|
}
|
|
920
1200
|
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
921
1201
|
const cp = codepoints[ci];
|
|
922
|
-
const ct =
|
|
1202
|
+
const ct = bengaliCharType(cp);
|
|
923
1203
|
if (ct === 4) continue;
|
|
924
1204
|
if (ct === 2 || ct === 3) {
|
|
925
1205
|
emitGlyph(resolveGid(cp), true, baseGid);
|
|
@@ -936,36 +1216,44 @@ function shapeTamilText(str, fontData) {
|
|
|
936
1216
|
for (const postCp of splitPostComponents) {
|
|
937
1217
|
emitGlyph(resolveGid(postCp), false);
|
|
938
1218
|
}
|
|
1219
|
+
if (hasReph) {
|
|
1220
|
+
const raGid = resolveGidGsub(RA);
|
|
1221
|
+
const halantGid = resolveGid(HALANT);
|
|
1222
|
+
emitGlyph(raGid, true, baseGid);
|
|
1223
|
+
emitGlyph(halantGid, true, baseGid);
|
|
1224
|
+
}
|
|
939
1225
|
}
|
|
940
1226
|
return shaped;
|
|
941
1227
|
}
|
|
942
1228
|
|
|
943
|
-
// src/shaping/
|
|
944
|
-
var
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
if (cp
|
|
949
|
-
if (cp
|
|
950
|
-
if (cp
|
|
951
|
-
if (cp
|
|
952
|
-
if (cp
|
|
953
|
-
if (cp ===
|
|
954
|
-
if (cp
|
|
955
|
-
if (cp ===
|
|
956
|
-
if (cp ===
|
|
957
|
-
if (cp ===
|
|
958
|
-
if (cp
|
|
959
|
-
if (cp
|
|
960
|
-
if (cp
|
|
961
|
-
if (cp
|
|
962
|
-
if (cp
|
|
1229
|
+
// src/shaping/tamil-shaper.ts
|
|
1230
|
+
var PULLI = 3021;
|
|
1231
|
+
function tamilCharType(cp) {
|
|
1232
|
+
if (cp === PULLI) return 7;
|
|
1233
|
+
if (cp === 2946 || cp === 2947) return 6;
|
|
1234
|
+
if (cp >= 2949 && cp <= 2964) return 1;
|
|
1235
|
+
if (cp >= 2965 && cp <= 3001) return 0;
|
|
1236
|
+
if (cp === 3007) return 4;
|
|
1237
|
+
if (cp === 3014) return 4;
|
|
1238
|
+
if (cp === 3015) return 4;
|
|
1239
|
+
if (cp === 3016) return 4;
|
|
1240
|
+
if (cp === 3018) return 4;
|
|
1241
|
+
if (cp === 3019) return 4;
|
|
1242
|
+
if (cp === 3020) return 4;
|
|
1243
|
+
if (cp === 3006) return 5;
|
|
1244
|
+
if (cp === 3008) return 5;
|
|
1245
|
+
if (cp === 3009 || cp === 3010) return 2;
|
|
1246
|
+
if (cp === 3031) return 5;
|
|
1247
|
+
if (cp >= 3006 && cp <= 3020) return 2;
|
|
1248
|
+
if (cp >= 3046 && cp <= 3055) return 9;
|
|
1249
|
+
if (cp >= 3056 && cp <= 3066) return 9;
|
|
1250
|
+
if (cp === 3024) return 6;
|
|
963
1251
|
return -1;
|
|
964
1252
|
}
|
|
965
|
-
function
|
|
966
|
-
return
|
|
1253
|
+
function isConsonant2(cp) {
|
|
1254
|
+
return tamilCharType(cp) === 0;
|
|
967
1255
|
}
|
|
968
|
-
function
|
|
1256
|
+
function buildTamilClusters(str) {
|
|
969
1257
|
const clusters = [];
|
|
970
1258
|
const cps = [];
|
|
971
1259
|
for (let i2 = 0; i2 < str.length; ) {
|
|
@@ -976,34 +1264,24 @@ function buildDevanagariClusters(str) {
|
|
|
976
1264
|
let i = 0;
|
|
977
1265
|
while (i < cps.length) {
|
|
978
1266
|
const cp = cps[i];
|
|
979
|
-
const type =
|
|
980
|
-
if (type < 0 || cp <
|
|
981
|
-
clusters.push({ codepoints: [cp], baseIndex: 0,
|
|
1267
|
+
const type = tamilCharType(cp);
|
|
1268
|
+
if (type < 0 || cp < TAMIL_START || cp > TAMIL_END) {
|
|
1269
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, preBaseMatras: [] });
|
|
982
1270
|
i++;
|
|
983
1271
|
continue;
|
|
984
1272
|
}
|
|
985
1273
|
const syllable = [];
|
|
986
|
-
let hasReph = false;
|
|
987
|
-
const preMatras = [];
|
|
988
|
-
if (isConsonant3(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant3(cps[i + 2])) {
|
|
989
|
-
hasReph = true;
|
|
990
|
-
syllable.push(cp, cps[i + 1]);
|
|
991
|
-
i += 2;
|
|
992
|
-
}
|
|
993
1274
|
let lastConsonantIdx = -1;
|
|
1275
|
+
const preMatras = [];
|
|
994
1276
|
while (i < cps.length) {
|
|
995
1277
|
const cc = cps[i];
|
|
996
|
-
const ct =
|
|
1278
|
+
const ct = tamilCharType(cc);
|
|
997
1279
|
if (ct === 0) {
|
|
998
1280
|
lastConsonantIdx = syllable.length;
|
|
999
1281
|
syllable.push(cc);
|
|
1000
1282
|
i++;
|
|
1001
|
-
if (i < cps.length && cps[i] ===
|
|
1002
|
-
|
|
1003
|
-
i++;
|
|
1004
|
-
}
|
|
1005
|
-
if (i < cps.length && cps[i] === HALANT2) {
|
|
1006
|
-
if (i + 1 < cps.length && isConsonant3(cps[i + 1])) {
|
|
1283
|
+
if (i < cps.length && cps[i] === PULLI) {
|
|
1284
|
+
if (i + 1 < cps.length && isConsonant2(cps[i + 1])) {
|
|
1007
1285
|
syllable.push(cps[i]);
|
|
1008
1286
|
i++;
|
|
1009
1287
|
continue;
|
|
@@ -1020,7 +1298,7 @@ function buildDevanagariClusters(str) {
|
|
|
1020
1298
|
}
|
|
1021
1299
|
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1022
1300
|
while (i < cps.length) {
|
|
1023
|
-
const ct =
|
|
1301
|
+
const ct = tamilCharType(cps[i]);
|
|
1024
1302
|
if (ct >= 2 && ct <= 5) {
|
|
1025
1303
|
if (ct === 4) {
|
|
1026
1304
|
preMatras.push(syllable.length);
|
|
@@ -1031,7 +1309,7 @@ function buildDevanagariClusters(str) {
|
|
|
1031
1309
|
break;
|
|
1032
1310
|
}
|
|
1033
1311
|
}
|
|
1034
|
-
while (i < cps.length &&
|
|
1312
|
+
while (i < cps.length && tamilCharType(cps[i]) === 6) {
|
|
1035
1313
|
syllable.push(cps[i]);
|
|
1036
1314
|
i++;
|
|
1037
1315
|
}
|
|
@@ -1041,57 +1319,40 @@ function buildDevanagariClusters(str) {
|
|
|
1041
1319
|
}
|
|
1042
1320
|
clusters.push({
|
|
1043
1321
|
codepoints: syllable,
|
|
1044
|
-
baseIndex:
|
|
1045
|
-
hasReph,
|
|
1322
|
+
baseIndex: baseIdx,
|
|
1046
1323
|
preBaseMatras: preMatras
|
|
1047
1324
|
});
|
|
1048
1325
|
}
|
|
1049
1326
|
return clusters;
|
|
1050
1327
|
}
|
|
1051
|
-
function
|
|
1052
|
-
const { cmap,
|
|
1328
|
+
function shapeTamilText(str, fontData) {
|
|
1329
|
+
const { cmap, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1053
1330
|
const shaped = [];
|
|
1054
1331
|
function resolveGid(cp) {
|
|
1055
1332
|
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1056
1333
|
return cmap[normCp] || 0;
|
|
1057
1334
|
}
|
|
1058
|
-
function
|
|
1059
|
-
|
|
1060
|
-
const firstGid = gids[0];
|
|
1061
|
-
const entries = ligatures[firstGid];
|
|
1062
|
-
if (!entries) return null;
|
|
1063
|
-
for (const entry of entries) {
|
|
1064
|
-
const compCount = entry.length - 1;
|
|
1065
|
-
if (compCount > gids.length - 1) continue;
|
|
1066
|
-
let match = true;
|
|
1067
|
-
for (let ci = 0; ci < compCount; ci++) {
|
|
1068
|
-
if (gids[1 + ci] !== entry[1 + ci]) {
|
|
1069
|
-
match = false;
|
|
1070
|
-
break;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
if (match) return { resultGid: entry[0], consumed: compCount + 1 };
|
|
1074
|
-
}
|
|
1075
|
-
return null;
|
|
1335
|
+
function tryLig(gids) {
|
|
1336
|
+
return tryLigature(gids, ligatures);
|
|
1076
1337
|
}
|
|
1077
1338
|
function getAdv(gid) {
|
|
1078
1339
|
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1079
1340
|
}
|
|
1080
|
-
function
|
|
1341
|
+
function getBaseAnchor2(baseGid, markClass) {
|
|
1081
1342
|
const base = markAnchors && markAnchors.bases && markAnchors.bases[baseGid];
|
|
1082
1343
|
if (!base) return null;
|
|
1083
1344
|
return base[markClass] ?? null;
|
|
1084
1345
|
}
|
|
1085
|
-
function
|
|
1346
|
+
function getMarkAnchor2(markGid) {
|
|
1086
1347
|
const mark = markAnchors && markAnchors.marks && markAnchors.marks[markGid];
|
|
1087
1348
|
if (!mark) return null;
|
|
1088
1349
|
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1089
1350
|
}
|
|
1090
1351
|
function emitGlyph(gid, isZero, baseGid) {
|
|
1091
1352
|
if (isZero && baseGid !== void 0) {
|
|
1092
|
-
const markAnchor =
|
|
1353
|
+
const markAnchor = getMarkAnchor2(gid);
|
|
1093
1354
|
if (markAnchor) {
|
|
1094
|
-
const baseAnchorPt =
|
|
1355
|
+
const baseAnchorPt = getBaseAnchor2(baseGid, markAnchor.classIdx);
|
|
1095
1356
|
if (baseAnchorPt) {
|
|
1096
1357
|
const baseAdv = getAdv(baseGid);
|
|
1097
1358
|
shaped.push({
|
|
@@ -1108,566 +1369,560 @@ function shapeDevanagariText(str, fontData) {
|
|
|
1108
1369
|
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1109
1370
|
}
|
|
1110
1371
|
}
|
|
1111
|
-
const clusters =
|
|
1372
|
+
const clusters = buildTamilClusters(str);
|
|
1112
1373
|
for (const cluster of clusters) {
|
|
1113
|
-
const { codepoints,
|
|
1114
|
-
const baseStart = hasReph ? 2 : 0;
|
|
1374
|
+
const { codepoints, preBaseMatras } = cluster;
|
|
1115
1375
|
let baseGid = 0;
|
|
1116
|
-
for (let ci =
|
|
1117
|
-
const ct =
|
|
1376
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1377
|
+
const ct = tamilCharType(codepoints[ci]);
|
|
1118
1378
|
if (ct === 0) {
|
|
1119
1379
|
baseGid = resolveGid(codepoints[ci]);
|
|
1120
1380
|
} else if (ct >= 2) {
|
|
1121
1381
|
break;
|
|
1122
1382
|
}
|
|
1123
1383
|
}
|
|
1124
|
-
const splitPostComponents = [];
|
|
1125
|
-
for (const mIdx of preBaseMatras) {
|
|
1126
|
-
if (mIdx < codepoints.length) {
|
|
1127
|
-
const mCp = codepoints[mIdx];
|
|
1128
|
-
if (mCp ===
|
|
1129
|
-
emitGlyph(resolveGid(
|
|
1130
|
-
splitPostComponents.push(
|
|
1131
|
-
} else if (mCp ===
|
|
1132
|
-
emitGlyph(resolveGid(
|
|
1133
|
-
splitPostComponents.push(
|
|
1134
|
-
} else {
|
|
1135
|
-
emitGlyph(resolveGid(
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
const raGid = resolveGid(RA2);
|
|
1141
|
-
const halantGid = resolveGid(HALANT2);
|
|
1142
|
-
const rephLig = tryLigature([raGid, halantGid]);
|
|
1143
|
-
if (rephLig) {
|
|
1144
|
-
emitGlyph(rephLig.resultGid, true, baseGid);
|
|
1145
|
-
} else {
|
|
1146
|
-
const raGsubbed = gsub[raGid] !== void 0 ? gsub[raGid] : raGid;
|
|
1147
|
-
emitGlyph(raGsubbed, true, baseGid);
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
const clusterGids = [];
|
|
1151
|
-
const clusterEndIdx = [];
|
|
1152
|
-
let matraStart = codepoints.length;
|
|
1153
|
-
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1154
|
-
const ct = devanagariCharType(codepoints[ci]);
|
|
1155
|
-
if (ct === 0 || ct === 7 || ct === 8) {
|
|
1156
|
-
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1157
|
-
clusterEndIdx.push(ci);
|
|
1158
|
-
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1159
|
-
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1160
|
-
} else {
|
|
1161
|
-
matraStart = ci;
|
|
1162
|
-
break;
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
const ligResult = tryLigature(clusterGids);
|
|
1166
|
-
if (ligResult) {
|
|
1167
|
-
emitGlyph(ligResult.resultGid, false);
|
|
1168
|
-
baseGid = ligResult.resultGid;
|
|
1169
|
-
let gi = ligResult.consumed;
|
|
1170
|
-
while (gi < clusterGids.length) {
|
|
1171
|
-
const subSeq = clusterGids.slice(gi);
|
|
1172
|
-
const subLig = tryLigature(subSeq);
|
|
1173
|
-
if (subLig) {
|
|
1174
|
-
emitGlyph(subLig.resultGid, false);
|
|
1175
|
-
gi += subLig.consumed;
|
|
1176
|
-
} else {
|
|
1177
|
-
const origCi = clusterEndIdx[gi];
|
|
1178
|
-
const ct = devanagariCharType(codepoints[origCi]);
|
|
1179
|
-
if (ct === 7) {
|
|
1180
|
-
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1181
|
-
} else {
|
|
1182
|
-
emitGlyph(clusterGids[gi], false);
|
|
1183
|
-
}
|
|
1184
|
-
gi++;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
} else {
|
|
1188
|
-
for (let ci = baseStart; ci < matraStart; ci++) {
|
|
1189
|
-
const cp = codepoints[ci];
|
|
1190
|
-
const ct = devanagariCharType(cp);
|
|
1191
|
-
if (ct === 0) {
|
|
1192
|
-
emitGlyph(resolveGid(cp), false);
|
|
1193
|
-
} else if (ct === 7) {
|
|
1194
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1195
|
-
} else if (ct === 8) {
|
|
1196
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1201
|
-
const cp = codepoints[ci];
|
|
1202
|
-
const ct = devanagariCharType(cp);
|
|
1203
|
-
if (ct === 4) continue;
|
|
1204
|
-
if (ct === 2 || ct === 3) {
|
|
1205
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1206
|
-
} else if (ct === 5) {
|
|
1207
|
-
emitGlyph(resolveGid(cp), false);
|
|
1208
|
-
} else if (ct === 6) {
|
|
1209
|
-
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1210
|
-
} else if (ct === 9) {
|
|
1211
|
-
emitGlyph(resolveGid(cp), false);
|
|
1212
|
-
} else {
|
|
1213
|
-
emitGlyph(resolveGid(cp), false);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
for (const postCp of splitPostComponents) {
|
|
1217
|
-
emitGlyph(resolveGid(postCp), false);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
return shaped;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
// src/shaping/arabic-shaper.ts
|
|
1224
|
-
function getJoiningType(cp) {
|
|
1225
|
-
if (cp >= 1611 && cp <= 1631 || // Harakat (vowel marks)
|
|
1226
|
-
cp === 1648 || // Superscript alef
|
|
1227
|
-
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 1552 && cp <= 1562) return "T";
|
|
1228
|
-
if (cp === 1600) return "C";
|
|
1229
|
-
if (cp >= 1574 && cp <= 1576 || // YEH HAMZA, BA series
|
|
1230
|
-
cp >= 1578 && cp <= 1582 || // TA through KHA
|
|
1231
|
-
cp >= 1587 && cp <= 1594 || // SEEN through GHAIN
|
|
1232
|
-
cp >= 1601 && cp <= 1607 || // FA through HA
|
|
1233
|
-
cp === 1609 || // ALEF MAKSURA
|
|
1234
|
-
cp === 1610 || // YA
|
|
1235
|
-
cp === 1656 || // HIGH HAMZA YEH
|
|
1236
|
-
cp >= 1690 && cp <= 1727 || // Extended Arabic
|
|
1237
|
-
cp >= 1729 && cp <= 1731 || cp >= 1740 && cp <= 1742 || cp >= 1744 && cp <= 1747 || cp === 1749 || cp === 1786 || cp === 1787 || cp === 1788) return "D";
|
|
1238
|
-
if (cp === 1570 || cp === 1571 || cp === 1572 || cp === 1573 || cp === 1575 || // ALEF
|
|
1239
|
-
cp === 1577 || // TEH MARBUTA
|
|
1240
|
-
cp === 1583 || cp === 1584 || // DAL, THAL
|
|
1241
|
-
cp === 1585 || cp === 1586 || // RA, ZAIN
|
|
1242
|
-
cp === 1608 || // WAW
|
|
1243
|
-
cp >= 1649 && cp <= 1651 || cp === 1653 || cp === 1654 || cp === 1655 || cp >= 1672 && cp <= 1689 || // Extended DAL/RA series
|
|
1244
|
-
cp === 1728 || cp >= 1732 && cp <= 1739 || cp === 1743 || cp === 1774 || cp === 1775) return "R";
|
|
1245
|
-
if (cp >= ARABIC_START && cp <= ARABIC_END) return "U";
|
|
1246
|
-
return "U";
|
|
1247
|
-
}
|
|
1248
|
-
var ARABIC_PRES_FORMS = /* @__PURE__ */ new Map([
|
|
1249
|
-
[1569, { isol: 65152 }],
|
|
1250
|
-
[1570, { isol: 65153, fina: 65154 }],
|
|
1251
|
-
[1571, { isol: 65155, fina: 65156 }],
|
|
1252
|
-
[1572, { isol: 65157, fina: 65158 }],
|
|
1253
|
-
[1573, { isol: 65159, fina: 65160 }],
|
|
1254
|
-
[1574, { isol: 65161, fina: 65162, init: 65163, medi: 65164 }],
|
|
1255
|
-
[1575, { isol: 65165, fina: 65166 }],
|
|
1256
|
-
[1576, { isol: 65167, fina: 65168, init: 65169, medi: 65170 }],
|
|
1257
|
-
[1577, { isol: 65171, fina: 65172 }],
|
|
1258
|
-
[1578, { isol: 65173, fina: 65174, init: 65175, medi: 65176 }],
|
|
1259
|
-
[1579, { isol: 65177, fina: 65178, init: 65179, medi: 65180 }],
|
|
1260
|
-
[1580, { isol: 65181, fina: 65182, init: 65183, medi: 65184 }],
|
|
1261
|
-
[1581, { isol: 65185, fina: 65186, init: 65187, medi: 65188 }],
|
|
1262
|
-
[1582, { isol: 65189, fina: 65190, init: 65191, medi: 65192 }],
|
|
1263
|
-
[1583, { isol: 65193, fina: 65194 }],
|
|
1264
|
-
[1584, { isol: 65195, fina: 65196 }],
|
|
1265
|
-
[1585, { isol: 65197, fina: 65198 }],
|
|
1266
|
-
[1586, { isol: 65199, fina: 65200 }],
|
|
1267
|
-
[1587, { isol: 65201, fina: 65202, init: 65203, medi: 65204 }],
|
|
1268
|
-
[1588, { isol: 65205, fina: 65206, init: 65207, medi: 65208 }],
|
|
1269
|
-
[1589, { isol: 65209, fina: 65210, init: 65211, medi: 65212 }],
|
|
1270
|
-
[1590, { isol: 65213, fina: 65214, init: 65215, medi: 65216 }],
|
|
1271
|
-
[1591, { isol: 65217, fina: 65218, init: 65219, medi: 65220 }],
|
|
1272
|
-
[1592, { isol: 65221, fina: 65222, init: 65223, medi: 65224 }],
|
|
1273
|
-
[1593, { isol: 65225, fina: 65226, init: 65227, medi: 65228 }],
|
|
1274
|
-
[1594, { isol: 65229, fina: 65230, init: 65231, medi: 65232 }],
|
|
1275
|
-
[1601, { isol: 65233, fina: 65234, init: 65235, medi: 65236 }],
|
|
1276
|
-
[1602, { isol: 65237, fina: 65238, init: 65239, medi: 65240 }],
|
|
1277
|
-
[1603, { isol: 65241, fina: 65242, init: 65243, medi: 65244 }],
|
|
1278
|
-
[1604, { isol: 65245, fina: 65246, init: 65247, medi: 65248 }],
|
|
1279
|
-
[1605, { isol: 65249, fina: 65250, init: 65251, medi: 65252 }],
|
|
1280
|
-
[1606, { isol: 65253, fina: 65254, init: 65255, medi: 65256 }],
|
|
1281
|
-
[1607, { isol: 65257, fina: 65258, init: 65259, medi: 65260 }],
|
|
1282
|
-
[1608, { isol: 65261, fina: 65262 }],
|
|
1283
|
-
[1609, { isol: 65263, fina: 65264 }],
|
|
1284
|
-
[1610, { isol: 65265, fina: 65266, init: 65267, medi: 65268 }]
|
|
1285
|
-
]);
|
|
1286
|
-
var LAM_ALEF_PRES = /* @__PURE__ */ new Map([
|
|
1287
|
-
[1570, [65269, 65270]],
|
|
1288
|
-
// LAM + ALEF WITH MADDA ABOVE
|
|
1289
|
-
[1571, [65271, 65272]],
|
|
1290
|
-
// LAM + ALEF WITH HAMZA ABOVE
|
|
1291
|
-
[1573, [65273, 65274]],
|
|
1292
|
-
// LAM + ALEF WITH HAMZA BELOW
|
|
1293
|
-
[1575, [65275, 65276]]
|
|
1294
|
-
// LAM + ALEF
|
|
1295
|
-
]);
|
|
1296
|
-
function resolvePositionalForms(codePoints) {
|
|
1297
|
-
const len = codePoints.length;
|
|
1298
|
-
const forms = new Array(len).fill("isol");
|
|
1299
|
-
const joining = codePoints.map(getJoiningType);
|
|
1300
|
-
for (let i = 0; i < len; i++) {
|
|
1301
|
-
if (joining[i] === "T" || joining[i] === "U") continue;
|
|
1302
|
-
let prevJoin = "U";
|
|
1303
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
1304
|
-
if (joining[j] !== "T") {
|
|
1305
|
-
prevJoin = joining[j];
|
|
1306
|
-
break;
|
|
1384
|
+
const splitPostComponents = [];
|
|
1385
|
+
for (const mIdx of preBaseMatras) {
|
|
1386
|
+
if (mIdx < codepoints.length) {
|
|
1387
|
+
const mCp = codepoints[mIdx];
|
|
1388
|
+
if (mCp === 3018) {
|
|
1389
|
+
emitGlyph(resolveGid(3014), false);
|
|
1390
|
+
splitPostComponents.push(3006);
|
|
1391
|
+
} else if (mCp === 3019) {
|
|
1392
|
+
emitGlyph(resolveGid(3015), false);
|
|
1393
|
+
splitPostComponents.push(3006);
|
|
1394
|
+
} else if (mCp === 3020) {
|
|
1395
|
+
emitGlyph(resolveGid(3014), false);
|
|
1396
|
+
splitPostComponents.push(3031);
|
|
1397
|
+
} else {
|
|
1398
|
+
emitGlyph(resolveGid(mCp), false);
|
|
1399
|
+
}
|
|
1307
1400
|
}
|
|
1308
1401
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1402
|
+
const clusterGids = [];
|
|
1403
|
+
const clusterEndIdx = [];
|
|
1404
|
+
let matraStart = codepoints.length;
|
|
1405
|
+
for (let ci = 0; ci < codepoints.length; ci++) {
|
|
1406
|
+
const ct = tamilCharType(codepoints[ci]);
|
|
1407
|
+
if (ct === 0 || ct === 7) {
|
|
1408
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1409
|
+
clusterEndIdx.push(ci);
|
|
1410
|
+
} else if (ct >= 2) {
|
|
1411
|
+
matraStart = ci;
|
|
1313
1412
|
break;
|
|
1413
|
+
} else if (ct < 0) {
|
|
1414
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1415
|
+
} else if (ct === 1) {
|
|
1416
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1314
1417
|
}
|
|
1315
1418
|
}
|
|
1316
|
-
|
|
1317
|
-
const
|
|
1318
|
-
if (
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1419
|
+
let ligConsumed = 0;
|
|
1420
|
+
const ligResult = tryLig(clusterGids);
|
|
1421
|
+
if (ligResult) {
|
|
1422
|
+
emitGlyph(ligResult.resultGid, false);
|
|
1423
|
+
baseGid = ligResult.resultGid;
|
|
1424
|
+
ligConsumed = ligResult.consumed;
|
|
1425
|
+
let gi = ligConsumed;
|
|
1426
|
+
while (gi < clusterGids.length) {
|
|
1427
|
+
const subSeq = clusterGids.slice(gi);
|
|
1428
|
+
const subLig = tryLig(subSeq);
|
|
1429
|
+
if (subLig) {
|
|
1430
|
+
emitGlyph(subLig.resultGid, false);
|
|
1431
|
+
gi += subLig.consumed;
|
|
1432
|
+
} else {
|
|
1433
|
+
const origCi = clusterEndIdx[gi];
|
|
1434
|
+
const ct = tamilCharType(codepoints[origCi]);
|
|
1435
|
+
if (ct === 7) {
|
|
1436
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1437
|
+
} else {
|
|
1438
|
+
emitGlyph(clusterGids[gi], false);
|
|
1439
|
+
}
|
|
1440
|
+
gi++;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1324
1443
|
} else {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
function isLamAlef(cp1, cp2) {
|
|
1333
|
-
return cp1 === LAM && ALEF_VARIANTS.has(cp2);
|
|
1334
|
-
}
|
|
1335
|
-
function shapeArabicText(str, fontData) {
|
|
1336
|
-
if (!str) return [];
|
|
1337
|
-
const codePoints = [];
|
|
1338
|
-
for (let i = 0; i < str.length; ) {
|
|
1339
|
-
const cp = str.codePointAt(i) ?? 0;
|
|
1340
|
-
codePoints.push(cp);
|
|
1341
|
-
i += cp > 65535 ? 2 : 1;
|
|
1342
|
-
}
|
|
1343
|
-
const forms = resolvePositionalForms(codePoints);
|
|
1344
|
-
const glyphs = [];
|
|
1345
|
-
const cmap = fontData.cmap;
|
|
1346
|
-
for (let i = 0; i < codePoints.length; i++) {
|
|
1347
|
-
const cp = codePoints[i];
|
|
1348
|
-
if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
|
|
1349
|
-
const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
|
|
1350
|
-
if (ligForms) {
|
|
1351
|
-
const isFinal = forms[i] === "medi" || forms[i] === "fina";
|
|
1352
|
-
const ligCP = isFinal ? ligForms[1] : ligForms[0];
|
|
1353
|
-
const ligGid = cmap[ligCP];
|
|
1354
|
-
if (ligGid) {
|
|
1355
|
-
glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1356
|
-
i++;
|
|
1357
|
-
continue;
|
|
1444
|
+
for (let ci = 0; ci < matraStart; ci++) {
|
|
1445
|
+
const cp = codepoints[ci];
|
|
1446
|
+
const ct = tamilCharType(cp);
|
|
1447
|
+
if (ct === 0) {
|
|
1448
|
+
emitGlyph(resolveGid(cp), false);
|
|
1449
|
+
} else if (ct === 7) {
|
|
1450
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1358
1451
|
}
|
|
1359
1452
|
}
|
|
1360
1453
|
}
|
|
1361
|
-
let
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
else if (
|
|
1368
|
-
|
|
1369
|
-
else
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1454
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1455
|
+
const cp = codepoints[ci];
|
|
1456
|
+
const ct = tamilCharType(cp);
|
|
1457
|
+
if (ct === 4) continue;
|
|
1458
|
+
if (ct === 2 || ct === 3) {
|
|
1459
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1460
|
+
} else if (ct === 5) {
|
|
1461
|
+
emitGlyph(resolveGid(cp), false);
|
|
1462
|
+
} else if (ct === 6) {
|
|
1463
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1464
|
+
} else if (ct === 9) {
|
|
1465
|
+
emitGlyph(resolveGid(cp), false);
|
|
1466
|
+
} else {
|
|
1467
|
+
emitGlyph(resolveGid(cp), false);
|
|
1373
1468
|
}
|
|
1374
1469
|
}
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1470
|
+
for (const postCp of splitPostComponents) {
|
|
1471
|
+
emitGlyph(resolveGid(postCp), false);
|
|
1472
|
+
}
|
|
1378
1473
|
}
|
|
1379
|
-
return
|
|
1474
|
+
return shaped;
|
|
1380
1475
|
}
|
|
1381
1476
|
|
|
1382
|
-
// src/shaping/
|
|
1383
|
-
function
|
|
1384
|
-
if (
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
if (
|
|
1391
|
-
|
|
1392
|
-
if (
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
if (
|
|
1398
|
-
|
|
1399
|
-
if (
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
if (cp >= 1520 && cp <= 1524) return "R";
|
|
1405
|
-
if (cp >= 1792 && cp <= 1871) return "R";
|
|
1406
|
-
if (cp >= 1920 && cp <= 1983) return "R";
|
|
1407
|
-
if (cp >= 64285 && cp <= 64335) return "R";
|
|
1408
|
-
if (cp >= 65 && cp <= 90) return "L";
|
|
1409
|
-
if (cp >= 97 && cp <= 122) return "L";
|
|
1410
|
-
if (cp >= 192 && cp <= 591) return "L";
|
|
1411
|
-
if (cp >= 880 && cp <= 1023) return "L";
|
|
1412
|
-
if (cp >= 1024 && cp <= 1279) return "L";
|
|
1413
|
-
if (cp >= 3584 && cp <= 3711) return "L";
|
|
1414
|
-
if (cp >= 2304 && cp <= 2431) return "L";
|
|
1415
|
-
if (cp >= 12352 && cp <= 12543) return "L";
|
|
1416
|
-
if (cp >= 19968 && cp <= 40959) return "L";
|
|
1417
|
-
if (cp >= 44032 && cp <= 55215) return "L";
|
|
1418
|
-
if (cp >= 33 && cp <= 47) return "ON";
|
|
1419
|
-
if (cp >= 58 && cp <= 64) return "ON";
|
|
1420
|
-
if (cp >= 91 && cp <= 96) return "ON";
|
|
1421
|
-
if (cp >= 123 && cp <= 126) return "ON";
|
|
1422
|
-
if (cp >= 161 && cp <= 191) return "ON";
|
|
1423
|
-
if (cp >= 8208 && cp <= 8231) return "ON";
|
|
1424
|
-
if (cp >= 8240 && cp <= 8286) return "ON";
|
|
1425
|
-
return "L";
|
|
1477
|
+
// src/shaping/gpos-positioner.ts
|
|
1478
|
+
function getBaseAnchor(markAnchors, baseGid, markClass) {
|
|
1479
|
+
if (!markAnchors) return null;
|
|
1480
|
+
const base = markAnchors.bases[baseGid];
|
|
1481
|
+
if (!base) return null;
|
|
1482
|
+
return base[markClass] ?? null;
|
|
1483
|
+
}
|
|
1484
|
+
function getMarkAnchor(markAnchors, markGid) {
|
|
1485
|
+
if (!markAnchors) return null;
|
|
1486
|
+
const mark = markAnchors.marks[markGid];
|
|
1487
|
+
if (!mark) return null;
|
|
1488
|
+
return { classIdx: mark[0], x: mark[1], y: mark[2] };
|
|
1489
|
+
}
|
|
1490
|
+
function positionMarkOnBase(markAnchors, markGid, baseGid, baseAdv) {
|
|
1491
|
+
const mark = getMarkAnchor(markAnchors, markGid);
|
|
1492
|
+
if (!mark) return null;
|
|
1493
|
+
const base = getBaseAnchor(markAnchors, baseGid, mark.classIdx);
|
|
1494
|
+
if (!base) return null;
|
|
1495
|
+
return {
|
|
1496
|
+
dx: base[0] - mark.x - baseAdv,
|
|
1497
|
+
dy: base[1] - mark.y
|
|
1498
|
+
};
|
|
1426
1499
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1500
|
+
|
|
1501
|
+
// src/shaping/devanagari-shaper.ts
|
|
1502
|
+
var HALANT2 = 2381;
|
|
1503
|
+
var NUKTA2 = 2364;
|
|
1504
|
+
var RA2 = 2352;
|
|
1505
|
+
function devanagariCharType(cp) {
|
|
1506
|
+
if (cp === HALANT2) return 7;
|
|
1507
|
+
if (cp === NUKTA2) return 8;
|
|
1508
|
+
if (cp >= 2305 && cp <= 2307) return 6;
|
|
1509
|
+
if (cp >= 2308 && cp <= 2324) return 1;
|
|
1510
|
+
if (cp >= 2325 && cp <= 2361) return 0;
|
|
1511
|
+
if (cp === 2367) return 4;
|
|
1512
|
+
if (cp >= 2369 && cp <= 2372) return 3;
|
|
1513
|
+
if (cp === 2366 || cp === 2368) return 5;
|
|
1514
|
+
if (cp === 2375 || cp === 2376) return 2;
|
|
1515
|
+
if (cp === 2379 || cp === 2380) return 4;
|
|
1516
|
+
if (cp >= 2373 && cp <= 2380) return 2;
|
|
1517
|
+
if (cp >= 2406 && cp <= 2415) return 9;
|
|
1518
|
+
if (cp >= 2392 && cp <= 2399) return 0;
|
|
1519
|
+
if (cp === 2365) return 1;
|
|
1520
|
+
if (cp === 2384) return 1;
|
|
1521
|
+
return -1;
|
|
1433
1522
|
}
|
|
1434
|
-
function
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1523
|
+
function isConsonant3(cp) {
|
|
1524
|
+
return devanagariCharType(cp) === 0;
|
|
1525
|
+
}
|
|
1526
|
+
function buildDevanagariClusters(str) {
|
|
1527
|
+
const clusters = [];
|
|
1528
|
+
const cps = [];
|
|
1529
|
+
for (let i2 = 0; i2 < str.length; ) {
|
|
1530
|
+
const cp = str.codePointAt(i2) ?? 0;
|
|
1531
|
+
cps.push(cp);
|
|
1532
|
+
i2 += cp > 65535 ? 2 : 1;
|
|
1443
1533
|
}
|
|
1444
|
-
let
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1534
|
+
let i = 0;
|
|
1535
|
+
while (i < cps.length) {
|
|
1536
|
+
const cp = cps[i];
|
|
1537
|
+
const type = devanagariCharType(cp);
|
|
1538
|
+
if (type < 0 || cp < DEVANAGARI_START || cp > DEVANAGARI_END) {
|
|
1539
|
+
clusters.push({ codepoints: [cp], baseIndex: 0, hasReph: false, preBaseMatras: [] });
|
|
1540
|
+
i++;
|
|
1541
|
+
continue;
|
|
1451
1542
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
types[i] = "EN";
|
|
1460
|
-
} else if (types[i] === "CS") {
|
|
1461
|
-
if (types[i - 1] === "EN" && types[i + 1] === "EN") types[i] = "EN";
|
|
1462
|
-
else if (types[i - 1] === "AN" && types[i + 1] === "AN") types[i] = "AN";
|
|
1543
|
+
const syllable = [];
|
|
1544
|
+
let hasReph = false;
|
|
1545
|
+
const preMatras = [];
|
|
1546
|
+
if (isConsonant3(cp) && cp === RA2 && i + 2 < cps.length && cps[i + 1] === HALANT2 && isConsonant3(cps[i + 2])) {
|
|
1547
|
+
hasReph = true;
|
|
1548
|
+
syllable.push(cp, cps[i + 1]);
|
|
1549
|
+
i += 2;
|
|
1463
1550
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1551
|
+
let lastConsonantIdx = -1;
|
|
1552
|
+
while (i < cps.length) {
|
|
1553
|
+
const cc = cps[i];
|
|
1554
|
+
const ct = devanagariCharType(cc);
|
|
1555
|
+
if (ct === 0) {
|
|
1556
|
+
lastConsonantIdx = syllable.length;
|
|
1557
|
+
syllable.push(cc);
|
|
1558
|
+
i++;
|
|
1559
|
+
if (i < cps.length && cps[i] === NUKTA2) {
|
|
1560
|
+
syllable.push(cps[i]);
|
|
1561
|
+
i++;
|
|
1472
1562
|
}
|
|
1473
|
-
if (
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1563
|
+
if (i < cps.length && cps[i] === HALANT2) {
|
|
1564
|
+
if (i + 1 < cps.length && isConsonant3(cps[i + 1])) {
|
|
1565
|
+
syllable.push(cps[i]);
|
|
1566
|
+
i++;
|
|
1567
|
+
continue;
|
|
1568
|
+
} else {
|
|
1569
|
+
syllable.push(cps[i]);
|
|
1570
|
+
i++;
|
|
1479
1571
|
break;
|
|
1480
1572
|
}
|
|
1481
|
-
if (types[j] !== "ET" && types[j] !== "BN") break;
|
|
1482
1573
|
}
|
|
1574
|
+
break;
|
|
1575
|
+
} else {
|
|
1576
|
+
break;
|
|
1483
1577
|
}
|
|
1484
|
-
if (found) types[i] = "EN";
|
|
1485
1578
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1579
|
+
const baseIdx = lastConsonantIdx >= 0 ? lastConsonantIdx : 0;
|
|
1580
|
+
while (i < cps.length) {
|
|
1581
|
+
const ct = devanagariCharType(cps[i]);
|
|
1582
|
+
if (ct >= 2 && ct <= 5) {
|
|
1583
|
+
if (ct === 4) {
|
|
1584
|
+
preMatras.push(syllable.length);
|
|
1585
|
+
}
|
|
1586
|
+
syllable.push(cps[i]);
|
|
1587
|
+
i++;
|
|
1588
|
+
} else {
|
|
1589
|
+
break;
|
|
1590
|
+
}
|
|
1490
1591
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
if (types[i] === "BN") continue;
|
|
1495
|
-
if (types[i] === "L" || types[i] === "R") {
|
|
1496
|
-
lastStrong = types[i];
|
|
1497
|
-
} else if (types[i] === "EN" && lastStrong === "L") {
|
|
1498
|
-
types[i] = "L";
|
|
1592
|
+
while (i < cps.length && devanagariCharType(cps[i]) === 6) {
|
|
1593
|
+
syllable.push(cps[i]);
|
|
1594
|
+
i++;
|
|
1499
1595
|
}
|
|
1596
|
+
if (syllable.length === 0) {
|
|
1597
|
+
syllable.push(cps[i] ?? 32);
|
|
1598
|
+
i++;
|
|
1599
|
+
}
|
|
1600
|
+
clusters.push({
|
|
1601
|
+
codepoints: syllable,
|
|
1602
|
+
baseIndex: hasReph ? baseIdx + 2 : baseIdx,
|
|
1603
|
+
hasReph,
|
|
1604
|
+
preBaseMatras: preMatras
|
|
1605
|
+
});
|
|
1500
1606
|
}
|
|
1607
|
+
return clusters;
|
|
1501
1608
|
}
|
|
1502
|
-
function
|
|
1503
|
-
const
|
|
1504
|
-
const
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1609
|
+
function shapeDevanagariText(str, fontData) {
|
|
1610
|
+
const { cmap, gsub, ligatures, markAnchors, widths, defaultWidth } = fontData;
|
|
1611
|
+
const shaped = [];
|
|
1612
|
+
function resolveGid(cp) {
|
|
1613
|
+
const normCp = cp === 8239 || cp === 160 ? 32 : cp;
|
|
1614
|
+
return cmap[normCp] || 0;
|
|
1615
|
+
}
|
|
1616
|
+
function tryLig(gids) {
|
|
1617
|
+
return tryLigature(gids, ligatures);
|
|
1618
|
+
}
|
|
1619
|
+
function getAdv(gid) {
|
|
1620
|
+
return widths[gid] !== void 0 ? widths[gid] : defaultWidth;
|
|
1621
|
+
}
|
|
1622
|
+
function emitGlyph(gid, isZero, baseGid) {
|
|
1623
|
+
if (isZero && baseGid !== void 0) {
|
|
1624
|
+
const markAnchor = getMarkAnchor(markAnchors, gid);
|
|
1625
|
+
if (markAnchor) {
|
|
1626
|
+
const baseAnchorPt = getBaseAnchor(markAnchors, baseGid, markAnchor.classIdx);
|
|
1627
|
+
if (baseAnchorPt) {
|
|
1628
|
+
const baseAdv = getAdv(baseGid);
|
|
1629
|
+
shaped.push({
|
|
1630
|
+
gid,
|
|
1631
|
+
dx: baseAnchorPt[0] - markAnchor.x - baseAdv,
|
|
1632
|
+
dy: baseAnchorPt[1] - markAnchor.y,
|
|
1633
|
+
isZeroAdvance: true
|
|
1634
|
+
});
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1515
1637
|
}
|
|
1638
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: true });
|
|
1639
|
+
} else {
|
|
1640
|
+
shaped.push({ gid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1516
1641
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1642
|
+
}
|
|
1643
|
+
const clusters = buildDevanagariClusters(str);
|
|
1644
|
+
for (const cluster of clusters) {
|
|
1645
|
+
const { codepoints, hasReph, preBaseMatras } = cluster;
|
|
1646
|
+
const baseStart = hasReph ? 2 : 0;
|
|
1647
|
+
let baseGid = 0;
|
|
1648
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1649
|
+
const ct = devanagariCharType(codepoints[ci]);
|
|
1650
|
+
if (ct === 0) {
|
|
1651
|
+
baseGid = resolveGid(codepoints[ci]);
|
|
1652
|
+
} else if (ct >= 2) {
|
|
1521
1653
|
break;
|
|
1522
1654
|
}
|
|
1523
1655
|
}
|
|
1524
|
-
const
|
|
1525
|
-
for (
|
|
1526
|
-
|
|
1656
|
+
const splitPostComponents = [];
|
|
1657
|
+
for (const mIdx of preBaseMatras) {
|
|
1658
|
+
if (mIdx < codepoints.length) {
|
|
1659
|
+
const mCp = codepoints[mIdx];
|
|
1660
|
+
if (mCp === 2379) {
|
|
1661
|
+
emitGlyph(resolveGid(2375), false);
|
|
1662
|
+
splitPostComponents.push(2366);
|
|
1663
|
+
} else if (mCp === 2380) {
|
|
1664
|
+
emitGlyph(resolveGid(2375), false);
|
|
1665
|
+
splitPostComponents.push(2380);
|
|
1666
|
+
} else {
|
|
1667
|
+
emitGlyph(resolveGid(mCp), false);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1527
1670
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1671
|
+
if (hasReph) {
|
|
1672
|
+
const raGid = resolveGid(RA2);
|
|
1673
|
+
const halantGid = resolveGid(HALANT2);
|
|
1674
|
+
const rephLig = tryLig([raGid, halantGid]);
|
|
1675
|
+
if (rephLig) {
|
|
1676
|
+
emitGlyph(rephLig.resultGid, true, baseGid);
|
|
1677
|
+
} else {
|
|
1678
|
+
const raGsubbed = gsub[raGid] !== void 0 ? gsub[raGid] : raGid;
|
|
1679
|
+
emitGlyph(raGsubbed, true, baseGid);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
const clusterGids = [];
|
|
1683
|
+
const clusterEndIdx = [];
|
|
1684
|
+
let matraStart = codepoints.length;
|
|
1685
|
+
for (let ci = baseStart; ci < codepoints.length; ci++) {
|
|
1686
|
+
const ct = devanagariCharType(codepoints[ci]);
|
|
1687
|
+
if (ct === 0 || ct === 7 || ct === 8) {
|
|
1688
|
+
clusterGids.push(resolveGid(codepoints[ci]));
|
|
1689
|
+
clusterEndIdx.push(ci);
|
|
1690
|
+
} else if (ct < 0 || ct === 1 || ct === 9) {
|
|
1691
|
+
emitGlyph(resolveGid(codepoints[ci]), false);
|
|
1692
|
+
} else {
|
|
1693
|
+
matraStart = ci;
|
|
1694
|
+
break;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
const ligResult = tryLig(clusterGids);
|
|
1698
|
+
if (ligResult) {
|
|
1699
|
+
emitGlyph(ligResult.resultGid, false);
|
|
1700
|
+
baseGid = ligResult.resultGid;
|
|
1701
|
+
let gi = ligResult.consumed;
|
|
1702
|
+
while (gi < clusterGids.length) {
|
|
1703
|
+
const subSeq = clusterGids.slice(gi);
|
|
1704
|
+
const subLig = tryLig(subSeq);
|
|
1705
|
+
if (subLig) {
|
|
1706
|
+
emitGlyph(subLig.resultGid, false);
|
|
1707
|
+
gi += subLig.consumed;
|
|
1708
|
+
} else {
|
|
1709
|
+
const origCi = clusterEndIdx[gi];
|
|
1710
|
+
const ct = devanagariCharType(codepoints[origCi]);
|
|
1711
|
+
if (ct === 7) {
|
|
1712
|
+
emitGlyph(clusterGids[gi], true, baseGid);
|
|
1713
|
+
} else {
|
|
1714
|
+
emitGlyph(clusterGids[gi], false);
|
|
1715
|
+
}
|
|
1716
|
+
gi++;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1535
1719
|
} else {
|
|
1536
|
-
|
|
1720
|
+
for (let ci = baseStart; ci < matraStart; ci++) {
|
|
1721
|
+
const cp = codepoints[ci];
|
|
1722
|
+
const ct = devanagariCharType(cp);
|
|
1723
|
+
if (ct === 0) {
|
|
1724
|
+
emitGlyph(resolveGid(cp), false);
|
|
1725
|
+
} else if (ct === 7) {
|
|
1726
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1727
|
+
} else if (ct === 8) {
|
|
1728
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1537
1731
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
63
|
|
1553
|
-
// ?
|
|
1554
|
-
]);
|
|
1555
|
-
function fixPunctuationAffinity(types, codePoints, len) {
|
|
1556
|
-
for (let i = 1; i < len; i++) {
|
|
1557
|
-
if (types[i] === "R" && SENTENCE_PUNCT.has(codePoints[i])) {
|
|
1558
|
-
let prevIdx = i - 1;
|
|
1559
|
-
while (prevIdx >= 0 && (types[prevIdx] === "WS" || types[prevIdx] === "BN")) prevIdx--;
|
|
1560
|
-
if (prevIdx >= 0 && types[prevIdx] === "L") {
|
|
1561
|
-
types[i] = "L";
|
|
1732
|
+
for (let ci = matraStart; ci < codepoints.length; ci++) {
|
|
1733
|
+
const cp = codepoints[ci];
|
|
1734
|
+
const ct = devanagariCharType(cp);
|
|
1735
|
+
if (ct === 4) continue;
|
|
1736
|
+
if (ct === 2 || ct === 3) {
|
|
1737
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1738
|
+
} else if (ct === 5) {
|
|
1739
|
+
emitGlyph(resolveGid(cp), false);
|
|
1740
|
+
} else if (ct === 6) {
|
|
1741
|
+
emitGlyph(resolveGid(cp), true, baseGid);
|
|
1742
|
+
} else if (ct === 9) {
|
|
1743
|
+
emitGlyph(resolveGid(cp), false);
|
|
1744
|
+
} else {
|
|
1745
|
+
emitGlyph(resolveGid(cp), false);
|
|
1562
1746
|
}
|
|
1563
1747
|
}
|
|
1748
|
+
for (const postCp of splitPostComponents) {
|
|
1749
|
+
emitGlyph(resolveGid(postCp), false);
|
|
1750
|
+
}
|
|
1564
1751
|
}
|
|
1752
|
+
return shaped;
|
|
1565
1753
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1754
|
+
|
|
1755
|
+
// src/shaping/arabic-shaper.ts
|
|
1756
|
+
function getJoiningType(cp) {
|
|
1757
|
+
if (cp >= 1611 && cp <= 1631 || // Harakat (vowel marks)
|
|
1758
|
+
cp === 1648 || // Superscript alef
|
|
1759
|
+
cp >= 1750 && cp <= 1756 || cp >= 1759 && cp <= 1764 || cp >= 1767 && cp <= 1768 || cp >= 1770 && cp <= 1773 || cp >= 1552 && cp <= 1562) return "T";
|
|
1760
|
+
if (cp === 1600) return "C";
|
|
1761
|
+
if (cp >= 1574 && cp <= 1576 || // YEH HAMZA, BA series
|
|
1762
|
+
cp >= 1578 && cp <= 1582 || // TA through KHA
|
|
1763
|
+
cp >= 1587 && cp <= 1594 || // SEEN through GHAIN
|
|
1764
|
+
cp >= 1601 && cp <= 1607 || // FA through HA
|
|
1765
|
+
cp === 1609 || // ALEF MAKSURA
|
|
1766
|
+
cp === 1610 || // YA
|
|
1767
|
+
cp === 1656 || // HIGH HAMZA YEH
|
|
1768
|
+
cp >= 1690 && cp <= 1727 || // Extended Arabic
|
|
1769
|
+
cp >= 1729 && cp <= 1731 || cp >= 1740 && cp <= 1742 || cp >= 1744 && cp <= 1747 || cp === 1749 || cp === 1786 || cp === 1787 || cp === 1788) return "D";
|
|
1770
|
+
if (cp === 1570 || cp === 1571 || cp === 1572 || cp === 1573 || cp === 1575 || // ALEF
|
|
1771
|
+
cp === 1577 || // TEH MARBUTA
|
|
1772
|
+
cp === 1583 || cp === 1584 || // DAL, THAL
|
|
1773
|
+
cp === 1585 || cp === 1586 || // RA, ZAIN
|
|
1774
|
+
cp === 1608 || // WAW
|
|
1775
|
+
cp >= 1649 && cp <= 1651 || cp === 1653 || cp === 1654 || cp === 1655 || cp >= 1672 && cp <= 1689 || // Extended DAL/RA series
|
|
1776
|
+
cp === 1728 || cp >= 1732 && cp <= 1739 || cp === 1743 || cp === 1774 || cp === 1775) return "R";
|
|
1777
|
+
if (cp >= ARABIC_START && cp <= ARABIC_END) return "U";
|
|
1778
|
+
return "U";
|
|
1779
|
+
}
|
|
1780
|
+
var ARABIC_PRES_FORMS = /* @__PURE__ */ new Map([
|
|
1781
|
+
[1569, { isol: 65152 }],
|
|
1782
|
+
[1570, { isol: 65153, fina: 65154 }],
|
|
1783
|
+
[1571, { isol: 65155, fina: 65156 }],
|
|
1784
|
+
[1572, { isol: 65157, fina: 65158 }],
|
|
1785
|
+
[1573, { isol: 65159, fina: 65160 }],
|
|
1786
|
+
[1574, { isol: 65161, fina: 65162, init: 65163, medi: 65164 }],
|
|
1787
|
+
[1575, { isol: 65165, fina: 65166 }],
|
|
1788
|
+
[1576, { isol: 65167, fina: 65168, init: 65169, medi: 65170 }],
|
|
1789
|
+
[1577, { isol: 65171, fina: 65172 }],
|
|
1790
|
+
[1578, { isol: 65173, fina: 65174, init: 65175, medi: 65176 }],
|
|
1791
|
+
[1579, { isol: 65177, fina: 65178, init: 65179, medi: 65180 }],
|
|
1792
|
+
[1580, { isol: 65181, fina: 65182, init: 65183, medi: 65184 }],
|
|
1793
|
+
[1581, { isol: 65185, fina: 65186, init: 65187, medi: 65188 }],
|
|
1794
|
+
[1582, { isol: 65189, fina: 65190, init: 65191, medi: 65192 }],
|
|
1795
|
+
[1583, { isol: 65193, fina: 65194 }],
|
|
1796
|
+
[1584, { isol: 65195, fina: 65196 }],
|
|
1797
|
+
[1585, { isol: 65197, fina: 65198 }],
|
|
1798
|
+
[1586, { isol: 65199, fina: 65200 }],
|
|
1799
|
+
[1587, { isol: 65201, fina: 65202, init: 65203, medi: 65204 }],
|
|
1800
|
+
[1588, { isol: 65205, fina: 65206, init: 65207, medi: 65208 }],
|
|
1801
|
+
[1589, { isol: 65209, fina: 65210, init: 65211, medi: 65212 }],
|
|
1802
|
+
[1590, { isol: 65213, fina: 65214, init: 65215, medi: 65216 }],
|
|
1803
|
+
[1591, { isol: 65217, fina: 65218, init: 65219, medi: 65220 }],
|
|
1804
|
+
[1592, { isol: 65221, fina: 65222, init: 65223, medi: 65224 }],
|
|
1805
|
+
[1593, { isol: 65225, fina: 65226, init: 65227, medi: 65228 }],
|
|
1806
|
+
[1594, { isol: 65229, fina: 65230, init: 65231, medi: 65232 }],
|
|
1807
|
+
[1601, { isol: 65233, fina: 65234, init: 65235, medi: 65236 }],
|
|
1808
|
+
[1602, { isol: 65237, fina: 65238, init: 65239, medi: 65240 }],
|
|
1809
|
+
[1603, { isol: 65241, fina: 65242, init: 65243, medi: 65244 }],
|
|
1810
|
+
[1604, { isol: 65245, fina: 65246, init: 65247, medi: 65248 }],
|
|
1811
|
+
[1605, { isol: 65249, fina: 65250, init: 65251, medi: 65252 }],
|
|
1812
|
+
[1606, { isol: 65253, fina: 65254, init: 65255, medi: 65256 }],
|
|
1813
|
+
[1607, { isol: 65257, fina: 65258, init: 65259, medi: 65260 }],
|
|
1814
|
+
[1608, { isol: 65261, fina: 65262 }],
|
|
1815
|
+
[1609, { isol: 65263, fina: 65264 }],
|
|
1816
|
+
[1610, { isol: 65265, fina: 65266, init: 65267, medi: 65268 }]
|
|
1817
|
+
]);
|
|
1818
|
+
var LAM_ALEF_PRES = /* @__PURE__ */ new Map([
|
|
1819
|
+
[1570, [65269, 65270]],
|
|
1820
|
+
// LAM + ALEF WITH MADDA ABOVE
|
|
1821
|
+
[1571, [65271, 65272]],
|
|
1822
|
+
// LAM + ALEF WITH HAMZA ABOVE
|
|
1823
|
+
[1573, [65273, 65274]],
|
|
1824
|
+
// LAM + ALEF WITH HAMZA BELOW
|
|
1825
|
+
[1575, [65275, 65276]]
|
|
1826
|
+
// LAM + ALEF
|
|
1827
|
+
]);
|
|
1828
|
+
function resolvePositionalForms(codePoints) {
|
|
1829
|
+
const len = codePoints.length;
|
|
1830
|
+
const forms = new Array(len).fill("isol");
|
|
1831
|
+
const joining = codePoints.map(getJoiningType);
|
|
1575
1832
|
for (let i = 0; i < len; i++) {
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
let
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
else if (codePoints[j] === closer) {
|
|
1583
|
-
depth--;
|
|
1584
|
-
if (depth === 0) {
|
|
1585
|
-
closeIdx = j;
|
|
1586
|
-
break;
|
|
1587
|
-
}
|
|
1833
|
+
if (joining[i] === "T" || joining[i] === "U") continue;
|
|
1834
|
+
let prevJoin = "U";
|
|
1835
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
1836
|
+
if (joining[j] !== "T") {
|
|
1837
|
+
prevJoin = joining[j];
|
|
1838
|
+
break;
|
|
1588
1839
|
}
|
|
1589
1840
|
}
|
|
1590
|
-
|
|
1591
|
-
let
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
hasL = true;
|
|
1841
|
+
let nextJoin = "U";
|
|
1842
|
+
for (let j = i + 1; j < len; j++) {
|
|
1843
|
+
if (joining[j] !== "T") {
|
|
1844
|
+
nextJoin = joining[j];
|
|
1595
1845
|
break;
|
|
1596
1846
|
}
|
|
1597
1847
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1848
|
+
const joinsToPrev = prevJoin === "D" || prevJoin === "C";
|
|
1849
|
+
const joinsToNext = (nextJoin === "D" || nextJoin === "R" || nextJoin === "C") && (joining[i] === "D" || joining[i] === "C");
|
|
1850
|
+
if (joinsToPrev && joinsToNext) {
|
|
1851
|
+
forms[i] = "medi";
|
|
1852
|
+
} else if (joinsToPrev) {
|
|
1853
|
+
forms[i] = "fina";
|
|
1854
|
+
} else if (joinsToNext) {
|
|
1855
|
+
forms[i] = "init";
|
|
1856
|
+
} else {
|
|
1857
|
+
forms[i] = "isol";
|
|
1601
1858
|
}
|
|
1602
1859
|
}
|
|
1860
|
+
return forms;
|
|
1603
1861
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1862
|
+
var LAM = 1604;
|
|
1863
|
+
var ALEF_VARIANTS = /* @__PURE__ */ new Set([1570, 1571, 1573, 1575]);
|
|
1864
|
+
function isLamAlef(cp1, cp2) {
|
|
1865
|
+
return cp1 === LAM && ALEF_VARIANTS.has(cp2);
|
|
1866
|
+
}
|
|
1867
|
+
function shapeArabicText(str, fontData) {
|
|
1868
|
+
if (!str) return [];
|
|
1606
1869
|
const codePoints = [];
|
|
1607
|
-
for (let i = 0; i <
|
|
1608
|
-
const cp =
|
|
1870
|
+
for (let i = 0; i < str.length; ) {
|
|
1871
|
+
const cp = str.codePointAt(i) ?? 0;
|
|
1609
1872
|
codePoints.push(cp);
|
|
1610
1873
|
i += cp > 65535 ? 2 : 1;
|
|
1611
1874
|
}
|
|
1612
|
-
const
|
|
1613
|
-
|
|
1614
|
-
const
|
|
1615
|
-
const
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
for (let i = 1; i <= len; i++) {
|
|
1634
|
-
if (i === len || levels[i] !== runLevel) {
|
|
1635
|
-
const start = cpToStr[runStart];
|
|
1636
|
-
const end = cpToStr[i];
|
|
1637
|
-
let runText = text.substring(start, end);
|
|
1638
|
-
if (runLevel % 2 === 1) {
|
|
1639
|
-
runText = reverseString(runText);
|
|
1875
|
+
const forms = resolvePositionalForms(codePoints);
|
|
1876
|
+
const glyphs = [];
|
|
1877
|
+
const cmap = fontData.cmap;
|
|
1878
|
+
const widths = fontData.widths;
|
|
1879
|
+
const defaultWidth = fontData.defaultWidth;
|
|
1880
|
+
const markAnchors = fontData.markAnchors;
|
|
1881
|
+
let lastBaseGid = 0;
|
|
1882
|
+
for (let i = 0; i < codePoints.length; i++) {
|
|
1883
|
+
const cp = codePoints[i];
|
|
1884
|
+
if (i < codePoints.length - 1 && isLamAlef(cp, codePoints[i + 1])) {
|
|
1885
|
+
const ligForms = LAM_ALEF_PRES.get(codePoints[i + 1]);
|
|
1886
|
+
if (ligForms) {
|
|
1887
|
+
const isFinal = forms[i] === "medi" || forms[i] === "fina";
|
|
1888
|
+
const ligCP = isFinal ? ligForms[1] : ligForms[0];
|
|
1889
|
+
const ligGid = cmap[ligCP];
|
|
1890
|
+
if (ligGid) {
|
|
1891
|
+
glyphs.push({ gid: ligGid, dx: 0, dy: 0, isZeroAdvance: false });
|
|
1892
|
+
lastBaseGid = ligGid;
|
|
1893
|
+
i++;
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1640
1896
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1897
|
+
}
|
|
1898
|
+
let gid = cmap[cp] ?? 0;
|
|
1899
|
+
const presForm = ARABIC_PRES_FORMS.get(cp);
|
|
1900
|
+
if (presForm) {
|
|
1901
|
+
const form = forms[i];
|
|
1902
|
+
let presCP;
|
|
1903
|
+
if (form === "init") presCP = presForm.init;
|
|
1904
|
+
else if (form === "medi") presCP = presForm.medi;
|
|
1905
|
+
else if (form === "fina") presCP = presForm.fina;
|
|
1906
|
+
else presCP = presForm.isol;
|
|
1907
|
+
if (presCP) {
|
|
1908
|
+
const presGid = cmap[presCP];
|
|
1909
|
+
if (presGid) gid = presGid;
|
|
1645
1910
|
}
|
|
1646
1911
|
}
|
|
1912
|
+
const joining = getJoiningType(cp);
|
|
1913
|
+
const isZeroAdvance = joining === "T";
|
|
1914
|
+
if (isZeroAdvance && lastBaseGid !== 0) {
|
|
1915
|
+
const baseAdv = widths[lastBaseGid] !== void 0 ? widths[lastBaseGid] : defaultWidth;
|
|
1916
|
+
const offset = positionMarkOnBase(markAnchors, gid, lastBaseGid, baseAdv);
|
|
1917
|
+
if (offset) {
|
|
1918
|
+
glyphs.push({ gid, dx: offset.dx, dy: offset.dy, isZeroAdvance: true });
|
|
1919
|
+
continue;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
glyphs.push({ gid, dx: 0, dy: 0, isZeroAdvance });
|
|
1923
|
+
if (!isZeroAdvance) lastBaseGid = gid;
|
|
1647
1924
|
}
|
|
1648
|
-
|
|
1649
|
-
runs.reverse();
|
|
1650
|
-
}
|
|
1651
|
-
return runs;
|
|
1652
|
-
}
|
|
1653
|
-
function containsRTL(text) {
|
|
1654
|
-
for (let i = 0; i < text.length; ) {
|
|
1655
|
-
const cp = text.codePointAt(i) ?? 0;
|
|
1656
|
-
const t = classifyBidiType(cp);
|
|
1657
|
-
if (t === "R" || t === "AL") return true;
|
|
1658
|
-
i += cp > 65535 ? 2 : 1;
|
|
1659
|
-
}
|
|
1660
|
-
return false;
|
|
1661
|
-
}
|
|
1662
|
-
function reverseString(str) {
|
|
1663
|
-
const cps = [];
|
|
1664
|
-
for (let i = 0; i < str.length; ) {
|
|
1665
|
-
const cp = str.codePointAt(i) ?? 0;
|
|
1666
|
-
cps.push(cp);
|
|
1667
|
-
i += cp > 65535 ? 2 : 1;
|
|
1668
|
-
}
|
|
1669
|
-
cps.reverse();
|
|
1670
|
-
return String.fromCodePoint(...cps);
|
|
1925
|
+
return glyphs;
|
|
1671
1926
|
}
|
|
1672
1927
|
|
|
1673
1928
|
// src/core/encoding-context.ts
|
|
@@ -1713,7 +1968,7 @@ function splitArabicNonArabic(text, fd) {
|
|
|
1713
1968
|
if (cur) segments.push({ text: cur, arabic: curArabic });
|
|
1714
1969
|
return segments;
|
|
1715
1970
|
}
|
|
1716
|
-
function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
|
|
1971
|
+
function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false) {
|
|
1717
1972
|
const upm = fd.metrics.unitsPerEm;
|
|
1718
1973
|
const result = [];
|
|
1719
1974
|
let mode = null;
|
|
@@ -1753,11 +2008,11 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
|
|
|
1753
2008
|
const cp = rawCp === 8239 || rawCp === 160 ? 32 : rawCp;
|
|
1754
2009
|
const char = text.substring(i, i + charLen);
|
|
1755
2010
|
const gid = fd.cmap[cp] ?? 0;
|
|
1756
|
-
if (gid === 0 && isWinAnsi(cp)) {
|
|
2011
|
+
if (gid === 0 && isWinAnsi(cp) && !pdfA) {
|
|
1757
2012
|
if (mode === "cid") flushCid();
|
|
1758
2013
|
mode = "hel";
|
|
1759
2014
|
helChars += char;
|
|
1760
|
-
} else if (mode === "hel" && isWinAnsi(cp)) {
|
|
2015
|
+
} else if (mode === "hel" && isWinAnsi(cp) && !pdfA) {
|
|
1761
2016
|
helChars += char;
|
|
1762
2017
|
} else {
|
|
1763
2018
|
if (mode === "hel") flushHel();
|
|
@@ -1774,7 +2029,7 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid) {
|
|
|
1774
2029
|
if (mode === "hel") flushHel();
|
|
1775
2030
|
return result;
|
|
1776
2031
|
}
|
|
1777
|
-
function createEncodingContext(fontEntries) {
|
|
2032
|
+
function createEncodingContext(fontEntries, pdfA = false) {
|
|
1778
2033
|
if (!fontEntries || fontEntries.length === 0) {
|
|
1779
2034
|
return {
|
|
1780
2035
|
isUnicode: false,
|
|
@@ -1803,6 +2058,8 @@ function createEncodingContext(fontEntries) {
|
|
|
1803
2058
|
return _usedGids;
|
|
1804
2059
|
},
|
|
1805
2060
|
textRuns(str, sz) {
|
|
2061
|
+
if (!str) return [];
|
|
2062
|
+
str = stripBidiControls(str);
|
|
1806
2063
|
if (!str) return [];
|
|
1807
2064
|
if (containsRTL(str)) {
|
|
1808
2065
|
const bidiRuns = resolveBidiRuns(str);
|
|
@@ -1830,12 +2087,12 @@ function createEncodingContext(fontEntries) {
|
|
|
1830
2087
|
}
|
|
1831
2088
|
result.push({ text: seg.text, fontRef, fontData: fd, shaped: visual, hexStr: null, widthPt: designW * sz / upm });
|
|
1832
2089
|
} else {
|
|
1833
|
-
const subRuns = buildTextRunsWithFallback(seg.text, fontRef, fd, sz, _trackGid);
|
|
2090
|
+
const subRuns = buildTextRunsWithFallback(seg.text, fontRef, fd, sz, _trackGid, pdfA);
|
|
1834
2091
|
result.push(...subRuns);
|
|
1835
2092
|
}
|
|
1836
2093
|
}
|
|
1837
2094
|
} else if (isRTL) {
|
|
1838
|
-
const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid);
|
|
2095
|
+
const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid, pdfA);
|
|
1839
2096
|
result.push(...subRuns);
|
|
1840
2097
|
} else {
|
|
1841
2098
|
if (containsThai(fRun.text)) {
|
|
@@ -1879,7 +2136,7 @@ function createEncodingContext(fontEntries) {
|
|
|
1879
2136
|
}
|
|
1880
2137
|
result.push({ text: fRun.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm });
|
|
1881
2138
|
} else {
|
|
1882
|
-
const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid);
|
|
2139
|
+
const subRuns = buildTextRunsWithFallback(fRun.text, fontRef, fd, sz, _trackGid, pdfA);
|
|
1883
2140
|
result.push(...subRuns);
|
|
1884
2141
|
}
|
|
1885
2142
|
}
|
|
@@ -1936,10 +2193,12 @@ function createEncodingContext(fontEntries) {
|
|
|
1936
2193
|
}
|
|
1937
2194
|
return [{ text: run.text, fontRef, fontData: fd, shaped, hexStr: null, widthPt: designW * sz / upm }];
|
|
1938
2195
|
}
|
|
1939
|
-
return buildTextRunsWithFallback(run.text, fontRef, fd, sz, _trackGid);
|
|
2196
|
+
return buildTextRunsWithFallback(run.text, fontRef, fd, sz, _trackGid, pdfA);
|
|
1940
2197
|
});
|
|
1941
2198
|
},
|
|
1942
2199
|
ps(str) {
|
|
2200
|
+
if (!str) return "<>";
|
|
2201
|
+
str = stripBidiControls(str);
|
|
1943
2202
|
if (!str) return "<>";
|
|
1944
2203
|
const { cmap } = primary.fontData;
|
|
1945
2204
|
if (containsRTL(str)) {
|
|
@@ -2408,7 +2667,7 @@ function buildPdfMetadata(now = /* @__PURE__ */ new Date()) {
|
|
|
2408
2667
|
const xmpDate = `${yyyy}-${mm}-${dd}T${hh}:${mi}:${ss}${tzSign}${tzH}:${tzM}`;
|
|
2409
2668
|
return { pdfDate, xmpDate };
|
|
2410
2669
|
}
|
|
2411
|
-
function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B", author) {
|
|
2670
|
+
function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B", author, subject, keywords) {
|
|
2412
2671
|
const escapedTitle = escapeXml(title);
|
|
2413
2672
|
const lines = [
|
|
2414
2673
|
'<?xpacket begin="\xEF\xBB\xBF" id="W5M0MpCehiHzreSzNTczkc9d"?>',
|
|
@@ -2427,7 +2686,9 @@ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B"
|
|
|
2427
2686
|
` <xmp:ModifyDate>${createDate}</xmp:ModifyDate>`,
|
|
2428
2687
|
` <xmp:MetadataDate>${createDate}</xmp:MetadataDate>`,
|
|
2429
2688
|
` <pdfaid:part>${pdfaPart}</pdfaid:part>`,
|
|
2430
|
-
` <pdfaid:conformance>${pdfaConformance}</pdfaid:conformance
|
|
2689
|
+
` <pdfaid:conformance>${pdfaConformance}</pdfaid:conformance>`
|
|
2690
|
+
);
|
|
2691
|
+
lines.push(
|
|
2431
2692
|
" </rdf:Description>",
|
|
2432
2693
|
" </rdf:RDF>",
|
|
2433
2694
|
"</x:xmpmeta>",
|
|
@@ -2435,6 +2696,35 @@ function buildXMPMetadata(title, createDate, pdfaPart = 2, pdfaConformance = "B"
|
|
|
2435
2696
|
);
|
|
2436
2697
|
return lines.join("\n");
|
|
2437
2698
|
}
|
|
2699
|
+
function utf8EncodeBinaryString(str) {
|
|
2700
|
+
let out = "";
|
|
2701
|
+
for (let i = 0; i < str.length; i++) {
|
|
2702
|
+
let cp = str.charCodeAt(i);
|
|
2703
|
+
if (cp >= 55296 && cp <= 56319 && i + 1 < str.length) {
|
|
2704
|
+
const lo = str.charCodeAt(i + 1);
|
|
2705
|
+
if (lo >= 56320 && lo <= 57343) {
|
|
2706
|
+
cp = (cp - 55296 << 10) + (lo - 56320) + 65536;
|
|
2707
|
+
i++;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (cp < 128) {
|
|
2711
|
+
out += String.fromCharCode(cp);
|
|
2712
|
+
} else if (cp < 2048) {
|
|
2713
|
+
out += String.fromCharCode(192 | cp >> 6);
|
|
2714
|
+
out += String.fromCharCode(128 | cp & 63);
|
|
2715
|
+
} else if (cp < 65536) {
|
|
2716
|
+
out += String.fromCharCode(224 | cp >> 12);
|
|
2717
|
+
out += String.fromCharCode(128 | cp >> 6 & 63);
|
|
2718
|
+
out += String.fromCharCode(128 | cp & 63);
|
|
2719
|
+
} else {
|
|
2720
|
+
out += String.fromCharCode(240 | cp >> 18);
|
|
2721
|
+
out += String.fromCharCode(128 | cp >> 12 & 63);
|
|
2722
|
+
out += String.fromCharCode(128 | cp >> 6 & 63);
|
|
2723
|
+
out += String.fromCharCode(128 | cp & 63);
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
return out;
|
|
2727
|
+
}
|
|
2438
2728
|
function buildOutputIntentDict(iccStreamObjNum, subtype = "GTS_PDFA1") {
|
|
2439
2729
|
return `<< /Type /OutputIntent /S /${subtype} /OutputConditionIdentifier (sRGB IEC61966-2.1) /RegistryName (http://www.color.org) /DestOutputProfile ${iccStreamObjNum} 0 R >>`;
|
|
2440
2730
|
}
|
|
@@ -2592,22 +2882,22 @@ function txt(str, x, y, font, sz, enc) {
|
|
|
2592
2882
|
}
|
|
2593
2883
|
return parts.join("\n");
|
|
2594
2884
|
}
|
|
2595
|
-
function txtR(str, rightX, y, font, sz, enc) {
|
|
2596
|
-
const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2885
|
+
function txtR(str, rightX, y, font, sz, enc, bold = false) {
|
|
2886
|
+
const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2597
2887
|
return txt(str, rightX - width, y, font, sz, enc);
|
|
2598
2888
|
}
|
|
2599
|
-
function txtC(str, leftX, y, font, sz, colW, enc) {
|
|
2600
|
-
const width = enc.isUnicode ? enc.tw(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2889
|
+
function txtC(str, leftX, y, font, sz, colW, enc, bold = false) {
|
|
2890
|
+
const width = enc.isUnicode ? enc.tw(str, sz) : bold ? helveticaBoldWidth(str, sz) : helveticaWidth(toWinAnsi(str), sz);
|
|
2601
2891
|
return txt(str, leftX + (colW - width) / 2, y, font, sz, enc);
|
|
2602
2892
|
}
|
|
2603
2893
|
function txtTagged(str, x, y, font, sz, enc, mcid) {
|
|
2604
2894
|
return wrapSpan(txt(str, x, y, font, sz, enc), str, mcid);
|
|
2605
2895
|
}
|
|
2606
|
-
function txtRTagged(str, rightX, y, font, sz, enc, mcid) {
|
|
2607
|
-
return wrapSpan(txtR(str, rightX, y, font, sz, enc), str, mcid);
|
|
2896
|
+
function txtRTagged(str, rightX, y, font, sz, enc, mcid, bold = false) {
|
|
2897
|
+
return wrapSpan(txtR(str, rightX, y, font, sz, enc, bold), str, mcid);
|
|
2608
2898
|
}
|
|
2609
|
-
function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid) {
|
|
2610
|
-
return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc), str, mcid);
|
|
2899
|
+
function txtCTagged(str, leftX, y, font, sz, colW, enc, mcid, bold = false) {
|
|
2900
|
+
return wrapSpan(txtC(str, leftX, y, font, sz, colW, enc, bold), str, mcid);
|
|
2611
2901
|
}
|
|
2612
2902
|
function encodePdfTextString(str) {
|
|
2613
2903
|
let ascii = true;
|
|
@@ -2681,13 +2971,42 @@ var DEFAULT_COLUMNS = [
|
|
|
2681
2971
|
{ f: 0.18, a: "c", mx: 20, mxH: 20 }
|
|
2682
2972
|
];
|
|
2683
2973
|
function computeColumnPositions(columns, marginLeft, contentWidth) {
|
|
2684
|
-
const
|
|
2685
|
-
const cwi =
|
|
2974
|
+
const n = columns.length;
|
|
2975
|
+
const cwi = new Array(n).fill(0);
|
|
2976
|
+
const fixed = new Array(n).fill(false);
|
|
2977
|
+
let totalFixed = 0;
|
|
2978
|
+
let freeWeight = 0;
|
|
2979
|
+
for (let i = 0; i < n; i++) {
|
|
2980
|
+
const col = columns[i];
|
|
2981
|
+
let w = col.f * contentWidth;
|
|
2982
|
+
let clamped = false;
|
|
2983
|
+
if (col.minWidth !== void 0 && w < col.minWidth) {
|
|
2984
|
+
w = col.minWidth;
|
|
2985
|
+
clamped = true;
|
|
2986
|
+
}
|
|
2987
|
+
if (col.maxWidth !== void 0 && w > col.maxWidth) {
|
|
2988
|
+
w = col.maxWidth;
|
|
2989
|
+
clamped = true;
|
|
2990
|
+
}
|
|
2991
|
+
if (clamped) {
|
|
2992
|
+
cwi[i] = w;
|
|
2993
|
+
fixed[i] = true;
|
|
2994
|
+
totalFixed += w;
|
|
2995
|
+
} else {
|
|
2996
|
+
freeWeight += col.f;
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
const remaining = contentWidth - totalFixed;
|
|
3000
|
+
if (freeWeight > 0) {
|
|
3001
|
+
for (let i = 0; i < n; i++) {
|
|
3002
|
+
if (!fixed[i]) cwi[i] = columns[i].f / freeWeight * remaining;
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
const cx = new Array(n);
|
|
2686
3006
|
let x = marginLeft;
|
|
2687
|
-
for (
|
|
2688
|
-
cx
|
|
2689
|
-
|
|
2690
|
-
x += col.f * contentWidth;
|
|
3007
|
+
for (let i = 0; i < n; i++) {
|
|
3008
|
+
cx[i] = x;
|
|
3009
|
+
x += cwi[i];
|
|
2691
3010
|
}
|
|
2692
3011
|
return { cx, cwi };
|
|
2693
3012
|
}
|
|
@@ -3036,17 +3355,17 @@ function _buildTableHeader(y, headers, enc, cx, cwi, columns, cw, mgL, mgR, pgW,
|
|
|
3036
3355
|
const thEl = { type: "TH", children: [mcref] };
|
|
3037
3356
|
thChildren.push(thEl);
|
|
3038
3357
|
if (columns[i].a === "r") {
|
|
3039
|
-
ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
|
|
3358
|
+
ops.push(txtRTagged(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid, true));
|
|
3040
3359
|
} else if (columns[i].a === "c") {
|
|
3041
|
-
ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid));
|
|
3360
|
+
ops.push(txtCTagged(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, mcid, true));
|
|
3042
3361
|
} else {
|
|
3043
3362
|
ops.push(txtTagged(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc, mcid));
|
|
3044
3363
|
}
|
|
3045
3364
|
} else {
|
|
3046
3365
|
if (columns[i].a === "r") {
|
|
3047
|
-
ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc));
|
|
3366
|
+
ops.push(txtR(t, cx[i] + cwi[i] - 3, y - TH_H + 4, enc.f2, fs.th, enc, true));
|
|
3048
3367
|
} else if (columns[i].a === "c") {
|
|
3049
|
-
ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc));
|
|
3368
|
+
ops.push(txtC(t, cx[i], y - TH_H + 4, enc.f2, fs.th, cwi[i], enc, true));
|
|
3050
3369
|
} else {
|
|
3051
3370
|
ops.push(txt(t, cx[i] + 3, y - TH_H + 4, enc.f2, fs.th, enc));
|
|
3052
3371
|
}
|
|
@@ -3157,7 +3476,9 @@ function buildPDF(params, layoutOptions) {
|
|
|
3157
3476
|
const fs = DEFAULT_FONT_SIZES;
|
|
3158
3477
|
const { cx, cwi } = computeColumnPositions(columns, mg.l, cw);
|
|
3159
3478
|
const fontEntries = params.fontEntries || (fontData ? [{ fontData, fontRef: "/F3", lang: "unknown" }] : []);
|
|
3160
|
-
const
|
|
3479
|
+
const pdfaConfig = resolvePdfAConfig();
|
|
3480
|
+
const tagged = pdfaConfig.enabled;
|
|
3481
|
+
const enc = createEncodingContext(fontEntries, tagged);
|
|
3161
3482
|
const footerTpl = {
|
|
3162
3483
|
left: footerText || void 0,
|
|
3163
3484
|
right: "{page}/{pages}"
|
|
@@ -3178,8 +3499,6 @@ function buildPDF(params, layoutOptions) {
|
|
|
3178
3499
|
totalPages = 1 + Math.ceil((totalRows - rowsPage1) / rowsPerPage);
|
|
3179
3500
|
}
|
|
3180
3501
|
if (totalPages < 1) totalPages = 1;
|
|
3181
|
-
const pdfaConfig = resolvePdfAConfig();
|
|
3182
|
-
const tagged = pdfaConfig.enabled;
|
|
3183
3502
|
const encState = null;
|
|
3184
3503
|
const wmExtraObjs = 0;
|
|
3185
3504
|
const mcidAlloc = tagged ? createMCIDAllocator() : void 0;
|
|
@@ -3309,8 +3628,17 @@ function buildPDF(params, layoutOptions) {
|
|
|
3309
3628
|
kids.push(`${pageObjStart + p * 2} 0 R`);
|
|
3310
3629
|
}
|
|
3311
3630
|
emitObj(2, `<< /Type /Pages /Kids [${kids.join(" ")}] /Count ${totalPages} >>`);
|
|
3312
|
-
|
|
3313
|
-
|
|
3631
|
+
if (tagged) {
|
|
3632
|
+
const pf = fontEntries[0];
|
|
3633
|
+
const bfName = `/${pf.fontData.fontName.replace(/[^A-Za-z0-9-]/g, "")}`;
|
|
3634
|
+
const primaryBase = 5;
|
|
3635
|
+
const refDict = `<< /Type /Font /Subtype /Type0 /BaseFont ${bfName} /Encoding /Identity-H /DescendantFonts [${primaryBase + 1} 0 R] /ToUnicode ${primaryBase + 4} 0 R >>`;
|
|
3636
|
+
emitObj(3, refDict);
|
|
3637
|
+
emitObj(4, refDict);
|
|
3638
|
+
} else {
|
|
3639
|
+
emitObj(3, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>");
|
|
3640
|
+
emitObj(4, "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >>");
|
|
3641
|
+
}
|
|
3314
3642
|
for (let fi = 0; fi < fontEntries.length; fi++) {
|
|
3315
3643
|
const fe = fontEntries[fi];
|
|
3316
3644
|
const fd = fe.fontData;
|
|
@@ -3404,7 +3732,7 @@ function buildPDF(params, layoutOptions) {
|
|
|
3404
3732
|
structTreeRootObjNum = tree.structTreeRootObjNum;
|
|
3405
3733
|
totalObjs = treeStart + tree.totalObjects - 1;
|
|
3406
3734
|
xmpObjNum = totalObjs + 1;
|
|
3407
|
-
const xmpContent = buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance);
|
|
3735
|
+
const xmpContent = utf8EncodeBinaryString(buildXMPMetadata(infoTitle, isoDate, pdfaConfig.pdfaPart, pdfaConfig.pdfaConformance));
|
|
3408
3736
|
emitStreamObj(
|
|
3409
3737
|
xmpObjNum,
|
|
3410
3738
|
`<< /Type /Metadata /Subtype /XML /Length ${xmpContent.length}`,
|