pdfnative 1.3.0 → 1.5.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.
@@ -225,6 +225,16 @@ var MYANMAR_EXTENDED_A_END = 43647;
225
225
  var MYANMAR_EXTENDED_B_START = 43488;
226
226
  var MYANMAR_EXTENDED_B_END = 43519;
227
227
  var MYANMAR_VIRAMA = 4153;
228
+ var MATH_OPERATORS_START = 8704;
229
+ var MATH_OPERATORS_END = 8959;
230
+ var SUPPLEMENTAL_MATH_OPERATORS_START = 10752;
231
+ var SUPPLEMENTAL_MATH_OPERATORS_END = 11007;
232
+ var GEOMETRIC_SHAPES_START = 9632;
233
+ var GEOMETRIC_SHAPES_END = 9727;
234
+ var MISC_MATH_SYMBOLS_A_START = 10176;
235
+ var MISC_MATH_SYMBOLS_A_END = 10223;
236
+ var MISC_MATH_SYMBOLS_B_START = 10624;
237
+ var MISC_MATH_SYMBOLS_B_END = 10751;
228
238
  var EMOJI_RANGES = [
229
239
  [127744, 128511],
230
240
  // Miscellaneous Symbols and Pictographs
@@ -291,6 +301,9 @@ function isMyanmarCodepoint(cp) {
291
301
  function isDevanagariCodepoint(cp) {
292
302
  return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
293
303
  }
304
+ function isMathCodepoint(cp) {
305
+ return cp >= MATH_OPERATORS_START && cp <= MATH_OPERATORS_END || cp >= SUPPLEMENTAL_MATH_OPERATORS_START && cp <= SUPPLEMENTAL_MATH_OPERATORS_END || cp >= GEOMETRIC_SHAPES_START && cp <= GEOMETRIC_SHAPES_END || cp >= MISC_MATH_SYMBOLS_A_START && cp <= MISC_MATH_SYMBOLS_A_END || cp >= MISC_MATH_SYMBOLS_B_START && cp <= MISC_MATH_SYMBOLS_B_END;
306
+ }
294
307
  function isEmojiCodepoint(cp) {
295
308
  if (cp >= FITZPATRICK_START && cp <= FITZPATRICK_END) return true;
296
309
  for (const [lo, hi] of EMOJI_RANGES) {
@@ -386,6 +399,7 @@ function detectCharLang(cp) {
386
399
  if (cp >= 1024 && cp <= 1279 || cp >= 1280 && cp <= 1327) return "ru";
387
400
  if (cp >= 4256 && cp <= 4351 || cp >= 11520 && cp <= 11567) return "ka";
388
401
  if (cp >= 1328 && cp <= 1423 || cp >= 64275 && cp <= 64279) return "hy";
402
+ if (isMathCodepoint(cp)) return "math";
389
403
  if (isEmojiCodepoint(cp)) return "emoji";
390
404
  return null;
391
405
  }
@@ -3581,15 +3595,86 @@ function radialShadingDict(p, m) {
3581
3595
  const s = (sx + sy) / 2 || 1;
3582
3596
  return `<< /ShadingType 3 /ColorSpace /DeviceRGB /Coords [${n(x0)} ${n(y0)} ${n(p.r0 * s)} ${n(x1)} ${n(y1)} ${n(p.r1 * s)}] /Function ${buildGradientFunction(p.stops)} /Extend ${extendFlags(p.extend)} >>`;
3583
3597
  }
3598
+ function colorAtOffset(stops, t) {
3599
+ if (stops.length === 0) return [0, 0, 0, 255];
3600
+ const sorted = stops.slice().sort((a, b) => a.offset - b.offset);
3601
+ if (t <= sorted[0].offset) return sorted[0].color;
3602
+ const last = sorted[sorted.length - 1];
3603
+ if (t >= last.offset) return last.color;
3604
+ for (let i = 0; i < sorted.length - 1; i++) {
3605
+ const a = sorted[i], b = sorted[i + 1];
3606
+ if (t >= a.offset && t <= b.offset) {
3607
+ const span = b.offset - a.offset || 1;
3608
+ const f = (t - a.offset) / span;
3609
+ return [
3610
+ Math.round(a.color[0] + (b.color[0] - a.color[0]) * f),
3611
+ Math.round(a.color[1] + (b.color[1] - a.color[1]) * f),
3612
+ Math.round(a.color[2] + (b.color[2] - a.color[2]) * f),
3613
+ Math.round(a.color[3] + (b.color[3] - a.color[3]) * f)
3614
+ ];
3615
+ }
3616
+ }
3617
+ return last.color;
3618
+ }
3619
+ function emitSweep(p, cx, cy, maxR, body, gsFor, blendMode) {
3620
+ const start = p.startAngle;
3621
+ const end = p.endAngle;
3622
+ const span = end - start;
3623
+ if (Math.abs(span) < 0.01) {
3624
+ const c = colorAtOffset(p.stops, 0);
3625
+ const gs = gsFor(c[3] / 255, blendMode);
3626
+ if (gs) body.push(`/${gs} gs`);
3627
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
3628
+ body.push(`${n(cx - maxR)} ${n(cy - maxR)} ${n(2 * maxR)} ${n(2 * maxR)} re`);
3629
+ body.push("f");
3630
+ return;
3631
+ }
3632
+ const steps = Math.max(12, Math.min(180, Math.ceil(Math.abs(span) / 3)));
3633
+ const r = maxR * 1.5;
3634
+ const rad = Math.PI / 180;
3635
+ for (let i = 0; i < steps; i++) {
3636
+ const a0 = start + span * i / steps;
3637
+ const a1 = start + span * (i + 1) / steps;
3638
+ const tMid = (i + 0.5) / steps;
3639
+ const c = colorAtOffset(p.stops, tMid);
3640
+ const gs = gsFor(c[3] / 255, blendMode);
3641
+ body.push("q");
3642
+ if (gs) body.push(`/${gs} gs`);
3643
+ body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
3644
+ const x0 = cx + r * Math.cos(a0 * rad), y0 = cy + r * Math.sin(a0 * rad);
3645
+ const x1 = cx + r * Math.cos(a1 * rad), y1 = cy + r * Math.sin(a1 * rad);
3646
+ body.push(`${n(cx)} ${n(cy)} m ${n(x0)} ${n(y0)} l ${n(x1)} ${n(y1)} l h`);
3647
+ body.push("f");
3648
+ body.push("Q");
3649
+ }
3650
+ }
3584
3651
  function renderColorGlyph(glyph, outlines, unitsPerEm) {
3585
3652
  const body = [];
3586
3653
  const shadings = [];
3587
3654
  const extGStates = [];
3588
- const alphaMap = /* @__PURE__ */ new Map();
3655
+ const gsMap = /* @__PURE__ */ new Map();
3589
3656
  let shadingIdx = 0;
3657
+ const gsFor = (alpha, bm) => {
3658
+ const a = Math.max(0, Math.min(1, alpha));
3659
+ const needAlpha = a < 0.999;
3660
+ const needBm = bm !== void 0 && bm !== "Normal";
3661
+ if (!needAlpha && !needBm) return "";
3662
+ const key = `${needAlpha ? a.toFixed(3) : "1"}|${needBm ? bm : ""}`;
3663
+ let name = gsMap.get(key);
3664
+ if (!name) {
3665
+ name = `Gs${gsMap.size}`;
3666
+ gsMap.set(key, name);
3667
+ const parts = [];
3668
+ if (needAlpha) parts.push(`/ca ${n(a)}`, `/CA ${n(a)}`);
3669
+ if (needBm) parts.push(`/BM /${bm}`);
3670
+ extGStates.push({ name, dict: `<< ${parts.join(" ")} >>` });
3671
+ }
3672
+ return name;
3673
+ };
3590
3674
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
3591
3675
  for (const layer of glyph.layers) {
3592
3676
  const m = layer.transform ?? ID;
3677
+ const bm = layer.blendMode;
3593
3678
  const contours = outlines(layer.glyphId);
3594
3679
  if (contours.length === 0) continue;
3595
3680
  const path = contoursToPath(contours, m);
@@ -3604,26 +3689,36 @@ function renderColorGlyph(glyph, outlines, unitsPerEm) {
3604
3689
  }
3605
3690
  if (layer.paint.kind === "solid") {
3606
3691
  const c = layer.paint.color;
3607
- const alpha = c[3] / 255;
3608
3692
  body.push("q");
3609
- if (alpha < 0.999) {
3610
- let gs = alphaMap.get(c[3]);
3611
- if (!gs) {
3612
- gs = `GsA${alphaMap.size}`;
3613
- alphaMap.set(c[3], gs);
3614
- extGStates.push({ name: gs, dict: `<< /ca ${n(alpha)} /CA ${n(alpha)} >>` });
3615
- }
3616
- body.push(`/${gs} gs`);
3617
- }
3693
+ const gs = gsFor(c[3] / 255, bm);
3694
+ if (gs) body.push(`/${gs} gs`);
3618
3695
  body.push(`${ch(c[0])} ${ch(c[1])} ${ch(c[2])} rg`);
3619
3696
  body.push(path);
3620
3697
  body.push("f");
3621
3698
  body.push("Q");
3699
+ } else if (layer.paint.kind === "sweep") {
3700
+ const [cx, cy] = tx(m, layer.paint.center[0], layer.paint.center[1]);
3701
+ let r2 = 0;
3702
+ for (const contour of contours) {
3703
+ for (const pt of contour) {
3704
+ const [px, py] = tx(m, pt.x, pt.y);
3705
+ const d = (px - cx) * (px - cx) + (py - cy) * (py - cy);
3706
+ if (d > r2) r2 = d;
3707
+ }
3708
+ }
3709
+ const maxR = Math.sqrt(r2) || 1;
3710
+ body.push("q");
3711
+ body.push(path);
3712
+ body.push("W n");
3713
+ emitSweep(layer.paint, cx, cy, maxR, body, gsFor, bm);
3714
+ body.push("Q");
3622
3715
  } else {
3623
3716
  const name = `Sh${shadingIdx++}`;
3624
3717
  const dict = layer.paint.kind === "linear" ? linearShadingDict(layer.paint, m) : radialShadingDict(layer.paint, m);
3625
3718
  shadings.push({ name, dict });
3626
3719
  body.push("q");
3720
+ const gs = gsFor(1, bm);
3721
+ if (gs) body.push(`/${gs} gs`);
3627
3722
  body.push(path);
3628
3723
  body.push("W n");
3629
3724
  body.push(`/${name} sh`);
@@ -3765,6 +3860,10 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
3765
3860
  for (let i = 0; i < text.length; ) {
3766
3861
  const rawCp = text.codePointAt(i) ?? 0;
3767
3862
  const charLen = rawCp > 65535 ? 2 : 1;
3863
+ if (rawCp < 32 || rawCp === 127) {
3864
+ i += charLen;
3865
+ continue;
3866
+ }
3768
3867
  const cp = rawCp === 8239 || rawCp === 160 ? 32 : rawCp;
3769
3868
  const char = text.substring(i, i + charLen);
3770
3869
  const gid = fd.cmap[cp] ?? 0;