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