ag-psd 31.0.3 → 33.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/00d0e0b4 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/0cc76730 +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/20729831 +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/214a593b +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/25f8b84a +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/27518289 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/330ef2d9 +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/37ff16b8 +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/3f72a97c +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  18. package/.v8-cache/v24.7.0-x64-ef5a0af0/75efccd4 +0 -0
  19. package/.v8-cache/v24.7.0-x64-ef5a0af0/762f9305 +0 -0
  20. package/.v8-cache/v24.7.0-x64-ef5a0af0/7bcfc50a +0 -0
  21. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  22. package/.v8-cache/v24.7.0-x64-ef5a0af0/825e9a05 +0 -0
  23. package/.v8-cache/v24.7.0-x64-ef5a0af0/87372817 +0 -0
  24. package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
  25. package/.v8-cache/v24.7.0-x64-ef5a0af0/891a7c73 +0 -0
  26. package/.v8-cache/v24.7.0-x64-ef5a0af0/8b68a3a2 +0 -0
  27. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  28. package/.v8-cache/v24.7.0-x64-ef5a0af0/a1d12a93 +0 -0
  29. package/.v8-cache/v24.7.0-x64-ef5a0af0/a7e80865 +0 -0
  30. package/.v8-cache/v24.7.0-x64-ef5a0af0/ac317c9c +0 -0
  31. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  32. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  33. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8569d6b +0 -0
  34. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8b073c4 +0 -0
  35. package/.v8-cache/v24.7.0-x64-ef5a0af0/bb489c89 +0 -0
  36. package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
  37. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  38. package/.v8-cache/v24.7.0-x64-ef5a0af0/c46725e1 +0 -0
  39. package/.v8-cache/v24.7.0-x64-ef5a0af0/c794cb34 +0 -0
  40. package/.v8-cache/v24.7.0-x64-ef5a0af0/cd4ae980 +0 -0
  41. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  42. package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
  43. package/.v8-cache/v24.7.0-x64-ef5a0af0/d02dd6f7 +0 -0
  44. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  45. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  46. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6735171 +0 -0
  47. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
  48. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
  49. package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
  50. package/.v8-cache/v24.7.0-x64-ef5a0af0/f5417276 +0 -0
  51. package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
  52. package/.v8-cache/v24.7.0-x64-ef5a0af0/f74d3518 +0 -0
  53. package/.v8-cache/v24.7.0-x64-ef5a0af0/f886deac +0 -0
  54. package/.v8-cache/v24.7.0-x64-ef5a0af0/fd0a2987 +0 -0
  55. package/.v8-cache/v24.7.0-x64-ef5a0af0/fe349423 +0 -0
  56. package/CHANGELOG.md +107 -0
  57. package/LICENSE +5 -0
  58. package/README.md +3 -6
  59. package/README_PSD.md +242 -81
  60. package/dist/abr.d.ts +11 -4
  61. package/dist/abr.js +363 -229
  62. package/dist/abr.js.map +1 -1
  63. package/dist/additionalInfo.d.ts +56 -2
  64. package/dist/additionalInfo.js +600 -364
  65. package/dist/additionalInfo.js.map +1 -1
  66. package/dist/ase.d.ts +6 -1
  67. package/dist/ase.js +61 -61
  68. package/dist/ase.js.map +1 -1
  69. package/dist/bundle.js +3096 -1541
  70. package/dist/descriptor.d.ts +25 -6
  71. package/dist/descriptor.js +380 -135
  72. package/dist/descriptor.js.map +1 -1
  73. package/dist/effectsHelpers.js +12 -12
  74. package/dist/effectsHelpers.js.map +1 -1
  75. package/dist/engineData.d.ts +3 -1
  76. package/dist/engineData.js +34 -13
  77. package/dist/engineData.js.map +1 -1
  78. package/dist/engineData2.js +50 -21
  79. package/dist/engineData2.js.map +1 -1
  80. package/dist/helpers.d.ts +6 -3
  81. package/dist/helpers.js +22 -6
  82. package/dist/helpers.js.map +1 -1
  83. package/dist/imageResources.d.ts +15 -3
  84. package/dist/imageResources.js +529 -144
  85. package/dist/imageResources.js.map +1 -1
  86. package/dist/index.js +1 -0
  87. package/dist/index.js.map +1 -1
  88. package/dist/jpeg.js +30 -10
  89. package/dist/jpeg.js.map +1 -1
  90. package/dist/psd.d.ts +154 -18
  91. package/dist/psd.js.map +1 -1
  92. package/dist/psdReader.d.ts +12 -2
  93. package/dist/psdReader.js +720 -421
  94. package/dist/psdReader.js.map +1 -1
  95. package/dist/psdWriter.d.ts +3 -0
  96. package/dist/psdWriter.js +167 -130
  97. package/dist/psdWriter.js.map +1 -1
  98. package/dist/text.js +143 -29
  99. package/dist/text.js.map +1 -1
  100. package/dist/utf8.js +36 -18
  101. package/dist/utf8.js.map +1 -1
  102. package/dist-es/abr.d.ts +11 -4
  103. package/dist-es/abr.js +363 -229
  104. package/dist-es/abr.js.map +1 -1
  105. package/dist-es/additionalInfo.d.ts +56 -2
  106. package/dist-es/additionalInfo.js +600 -364
  107. package/dist-es/additionalInfo.js.map +1 -1
  108. package/dist-es/ase.d.ts +6 -1
  109. package/dist-es/ase.js +61 -61
  110. package/dist-es/ase.js.map +1 -1
  111. package/dist-es/descriptor.d.ts +25 -6
  112. package/dist-es/descriptor.js +380 -135
  113. package/dist-es/descriptor.js.map +1 -1
  114. package/dist-es/effectsHelpers.js +12 -12
  115. package/dist-es/effectsHelpers.js.map +1 -1
  116. package/dist-es/engineData.d.ts +3 -1
  117. package/dist-es/engineData.js +34 -13
  118. package/dist-es/engineData.js.map +1 -1
  119. package/dist-es/engineData2.js +50 -21
  120. package/dist-es/engineData2.js.map +1 -1
  121. package/dist-es/helpers.d.ts +6 -3
  122. package/dist-es/helpers.js +22 -6
  123. package/dist-es/helpers.js.map +1 -1
  124. package/dist-es/imageResources.d.ts +15 -3
  125. package/dist-es/imageResources.js +529 -144
  126. package/dist-es/imageResources.js.map +1 -1
  127. package/dist-es/index.js +1 -0
  128. package/dist-es/index.js.map +1 -1
  129. package/dist-es/jpeg.js +30 -10
  130. package/dist-es/jpeg.js.map +1 -1
  131. package/dist-es/psd.d.ts +154 -18
  132. package/dist-es/psd.js.map +1 -1
  133. package/dist-es/psdReader.d.ts +12 -2
  134. package/dist-es/psdReader.js +720 -421
  135. package/dist-es/psdReader.js.map +1 -1
  136. package/dist-es/psdWriter.d.ts +3 -0
  137. package/dist-es/psdWriter.js +167 -130
  138. package/dist-es/psdWriter.js.map +1 -1
  139. package/dist-es/text.js +143 -29
  140. package/dist-es/text.js.map +1 -1
  141. package/dist-es/utf8.js +36 -18
  142. package/dist-es/utf8.js.map +1 -1
  143. package/package.json +2 -2
  144. package/src/abr.ts +380 -232
  145. package/src/additionalInfo.ts +781 -477
  146. package/src/ase.ts +66 -61
  147. package/src/descriptor.ts +386 -142
  148. package/src/effectsHelpers.ts +9 -9
  149. package/src/engineData.ts +36 -13
  150. package/src/engineData2.ts +468 -432
  151. package/src/helpers.ts +23 -5
  152. package/src/imageResources.ts +658 -167
  153. package/src/index.ts +1 -0
  154. package/src/jpeg.ts +27 -12
  155. package/src/psd.ts +158 -30
  156. package/src/psdReader.ts +732 -414
  157. package/src/psdWriter.ts +186 -132
  158. package/src/text.ts +139 -18
  159. package/src/utf8.ts +35 -21
package/src/text.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { TextStyle, LayerTextData, ParagraphStyle, Font, AntiAlias, TextGridInfo, Justification, Color, TextPath } from './psd';
2
+ import { clamp } from './helpers';
2
3
 
3
4
  interface Adjustments {
4
5
  Axis: number[];
@@ -273,7 +274,16 @@ const styleKeys: (keyof TextStyle)[] = [
273
274
  'kashida', 'diacriticPos',
274
275
  ];
275
276
 
276
- const antialias: AntiAlias[] = ['none', 'crisp', 'strong', 'smooth', 'sharp'];
277
+ const antialias: (AntiAlias | undefined)[] = [
278
+ 'none', // 0
279
+ 'crisp', // 1
280
+ 'strong', // 2
281
+ 'smooth', // 3
282
+ 'sharp', // 4
283
+ undefined, // 5 - unknown
284
+ 'platformLCD', // 6
285
+ 'platform', // 7
286
+ ];
277
287
  const justification: Justification[] = [
278
288
  'left', // 0
279
289
  'right', // 1
@@ -300,13 +310,43 @@ function decodeColor(color: TypeValues): Color {
300
310
  }
301
311
  }
302
312
 
313
+ // h, s, b in 0..1 range, returns r, g, b in 0..1 range
314
+ function hsbToRgb(h: number, s: number, v: number): [number, number, number] {
315
+ h = clamp(h || 0, 0, 1);
316
+ s = clamp(s || 0, 0, 1);
317
+ v = clamp(v || 0, 0, 1);
318
+
319
+ const h6 = (h * 6) % 6;
320
+ const i = Math.floor(h6);
321
+ const f = h6 - i;
322
+ const p = v * (1 - s);
323
+ const q = v * (1 - s * f);
324
+ const t = v * (1 - s * (1 - f));
325
+
326
+ switch (i) {
327
+ case 0: return [v, t, p];
328
+ case 1: return [q, v, p];
329
+ case 2: return [p, v, t];
330
+ case 3: return [p, q, v];
331
+ case 4: return [t, p, v];
332
+ default: return [v, p, q];
333
+ }
334
+ }
335
+
303
336
  function encodeColor(color: Color | undefined): TypeValues {
304
337
  if (!color) {
305
338
  return { Type: 1, Values: [0, 0, 0, 0] };
306
339
  } else if ('r' in color) {
307
340
  return { Type: 1, Values: ['a' in color ? color.a / 255 : 1, color.r / 255, color.g / 255, color.b / 255] };
341
+ } else if ('fr' in color) {
342
+ return { Type: 1, Values: [1, clamp(color.fr || 0, 0, 1), clamp(color.fg || 0, 0, 1), clamp(color.fb || 0, 0, 1)] };
343
+ } else if ('h' in color) {
344
+ const [r, g, b] = hsbToRgb(color.h, color.s, color.b);
345
+ return { Type: 1, Values: [1, r, g, b] };
308
346
  } else if ('c' in color) {
309
347
  return { Type: 2, Values: [1, color.c / 255, color.m / 255, color.y / 255, color.k / 255] };
348
+ } else if ('l' in color) {
349
+ throw new Error('LAB colors are not supported in text layers, use RGB, CMYK or grayscale color instead');
310
350
  } else if ('k' in color) {
311
351
  return { Type: 0, Values: [1, color.k / 255] };
312
352
  } else {
@@ -329,8 +369,9 @@ function objectsEqual(a: any, b: any) {
329
369
  }
330
370
 
331
371
  function findOrAddFont(fonts: Font[], font: Font) {
372
+ // Photoshop writes separate entries for the same font with different synthetic (faux bold/italic) styles
332
373
  for (let i = 0; i < fonts.length; i++) {
333
- if (fonts[i].name === font.name) return i;
374
+ if (fonts[i].name === font.name && (fonts[i].synthetic || 0) === (font.synthetic || 0)) return i;
334
375
  }
335
376
 
336
377
  fonts.push(font);
@@ -346,9 +387,10 @@ function decodeObject(obj: any, keys: string[], fonts: Font[]) {
346
387
  if (obj[Key] === undefined) continue;
347
388
 
348
389
  if (key === 'justification') {
349
- result[key] = justification[obj[Key]];
390
+ result[key] = justification[obj[Key]] ?? 'left';
350
391
  } else if (key === 'font') {
351
- result[key] = fonts[obj[Key]];
392
+ const font = fonts[obj[Key]];
393
+ if (font) result[key] = font; // invalid font index, leave the font unset so it falls back to default
352
394
  } else if (key === 'fillColor' || key === 'strokeColor') {
353
395
  result[key] = decodeColor(obj[Key]);
354
396
  } else {
@@ -368,7 +410,7 @@ function encodeObject(obj: any, keys: string[], fonts: Font[]) {
368
410
  if (obj[key] === undefined) continue;
369
411
 
370
412
  if (key === 'justification') {
371
- result[Key] = justification.indexOf(obj[key] ?? 'left');
413
+ result[Key] = Math.max(0, justification.indexOf(obj[key] ?? 'left')); // default to 'left' for unknown values
372
414
  } else if (key === 'font') {
373
415
  result[Key] = findOrAddFont(fonts, obj[key]);
374
416
  } else if (key === 'fillColor' || key === 'strokeColor') {
@@ -443,6 +485,39 @@ function deduplicateValues<T>(base: T, runs: { style: T; }[], keys: (keyof T)[])
443
485
  }
444
486
  }
445
487
 
488
+ function isDefaultGridColor(color: Color | undefined) {
489
+ // Photoshop writes default grid colors as blue with 0 alpha, ignore alpha when comparing
490
+ return !!color && 'r' in color && color.r === 0 && color.g === 0 && color.b === 255;
491
+ }
492
+
493
+ function decodeGridColor(color: TypeValues | undefined, defaultColor: Color): Color {
494
+ try {
495
+ return color ? decodeColor(color) : defaultColor;
496
+ } catch {
497
+ return defaultColor; // don't fail whole text layer because of invalid grid color
498
+ }
499
+ }
500
+
501
+ function decodeGridInfo(grid: EngineData['EngineDict']['GridInfo'] | undefined): TextGridInfo | undefined {
502
+ if (!grid || typeof grid !== 'object') return undefined;
503
+
504
+ const result: TextGridInfo = {
505
+ isOn: !!grid.GridIsOn,
506
+ show: !!grid.ShowGrid,
507
+ size: grid.GridSize ?? defaultGridInfo.size,
508
+ leading: grid.GridLeading ?? defaultGridInfo.leading,
509
+ color: decodeGridColor(grid.GridColor, defaultGridInfo.color!),
510
+ leadingFillColor: decodeGridColor(grid.GridLeadingFillColor, defaultGridInfo.leadingFillColor!),
511
+ alignLineHeightToGridFlags: !!grid.AlignLineHeightToGridFlags,
512
+ };
513
+
514
+ const isDefault = !result.isOn && !result.show && result.size === defaultGridInfo.size &&
515
+ result.leading === defaultGridInfo.leading && !result.alignLineHeightToGridFlags &&
516
+ isDefaultGridColor(result.color) && isDefaultGridColor(result.leadingFillColor);
517
+
518
+ return isDefault ? undefined : result;
519
+ }
520
+
446
521
  export function decodeEngineData(engineData: EngineData) {
447
522
  // console.log('engineData', require('util').inspect(engineData, false, 99, true));
448
523
  const engineDict = engineData.EngineDict;
@@ -458,9 +533,11 @@ export function decodeEngineData(engineData: EngineData) {
458
533
  let text = engineDict.Editor.Text.replace(/\r/g, '\n');
459
534
  let removedCharacters = 0;
460
535
 
461
- while (/\n$/.test(text)) {
536
+ // Photoshop always appends exactly one trailing \r (and encoder adds one back), any additional
537
+ // trailing new lines are empty paragraphs that are part of the text
538
+ if (/\n$/.test(text)) {
462
539
  text = text.substring(0, text.length - 1);
463
- removedCharacters++;
540
+ removedCharacters = 1;
464
541
  }
465
542
 
466
543
  const result: LayerTextData = {
@@ -484,6 +561,11 @@ export function decodeEngineData(engineData: EngineData) {
484
561
  if (photoshop.BoxBounds) result.boxBounds = photoshop.BoxBounds;
485
562
  }
486
563
 
564
+ // grid info (only included when it differs from defaults, to avoid noise in common case)
565
+
566
+ const gridInfo = decodeGridInfo(engineDict.GridInfo);
567
+ if (gridInfo) result.gridInfo = gridInfo;
568
+
487
569
  // paragraph style
488
570
 
489
571
  // const theNormalParagraphSheet = resourceDict.TheNormalParagraphSheet;
@@ -506,7 +588,8 @@ export function decodeEngineData(engineData: EngineData) {
506
588
  }
507
589
 
508
590
  for (let counter = removedCharacters; result.paragraphStyleRuns.length && counter > 0; counter--) {
509
- if (--result.paragraphStyleRuns[result.paragraphStyleRuns.length - 1].length === 0) {
591
+ // keep 0 length run if it's for empty last paragraph, otherwise we lose its style
592
+ if (--result.paragraphStyleRuns[result.paragraphStyleRuns.length - 1].length === 0 && result.paragraphStyleRuns.length === 1) {
510
593
  result.paragraphStyleRuns.pop();
511
594
  }
512
595
  }
@@ -522,13 +605,18 @@ export function decodeEngineData(engineData: EngineData) {
522
605
  // const styleSheetData = styleSheetSet[theNormalStyleSheet].StyleSheetData;
523
606
  const styleRun = engineDict.StyleRun;
524
607
 
608
+ // style runs without a font use the font of the normal style sheet
609
+ const normalStyleSheet = resourceDict.StyleSheetSet?.[resourceDict.TheNormalStyleSheet ?? 0]?.StyleSheetData;
610
+ const normalFontIndex = normalStyleSheet?.Font;
611
+ const normalFont = (normalFontIndex !== undefined ? fonts[normalFontIndex] : undefined) ?? fonts[0];
612
+
525
613
  result.style = {}; // decodeStyle(styleSheetData, fonts);
526
614
  result.styleRuns = [];
527
615
 
528
616
  for (let i = 0; i < styleRun.RunArray.length; i++) {
529
617
  const length = styleRun.RunLengthArray[i];
530
618
  const style = decodeStyle(styleRun.RunArray[i].StyleSheet.StyleSheetData, fonts);
531
- if (!style.font) style.font = fonts[0];
619
+ if (!style.font && normalFont) style.font = normalFont;
532
620
  result.styleRuns.push({ length, style });
533
621
  }
534
622
 
@@ -545,8 +633,38 @@ export function decodeEngineData(engineData: EngineData) {
545
633
  return result;
546
634
  }
547
635
 
636
+ // Adjusts run lengths (specified in terms of the original text) for characters removed from the text
637
+ // (\r characters of \r\n pairs that are collapsed into single \r)
638
+ function adjustRunLengths<T extends { length: number; }>(runs: T[] | undefined, removed: number[]): T[] | undefined {
639
+ if (!runs || !removed.length) return runs;
640
+
641
+ let start = 0;
642
+ let removedIndex = 0;
643
+
644
+ return runs.map(run => {
645
+ const length = Math.max(0, run.length || 0);
646
+ const end = start + length;
647
+ let count = 0;
648
+
649
+ while (removedIndex < removed.length && removed[removedIndex] < end) {
650
+ if (removed[removedIndex] >= start) count++;
651
+ removedIndex++;
652
+ }
653
+
654
+ start = end;
655
+ return { ...run, length: length - count };
656
+ });
657
+ }
658
+
548
659
  export function encodeEngineData(data: LayerTextData) {
549
- const text = `${(data.text || '').replace(/\r?\n/g, '\r')}\r`;
660
+ const sourceText = data.text || '';
661
+ const removedIndices: number[] = []; // indices of \r characters removed from \r\n pairs
662
+
663
+ for (let i = 0; i < sourceText.length - 1; i++) {
664
+ if (sourceText.charCodeAt(i) === 13 && sourceText.charCodeAt(i + 1) === 10) removedIndices.push(i);
665
+ }
666
+
667
+ const text = `${sourceText.replace(/\r?\n/g, '\r')}\r`;
550
668
 
551
669
  const fonts: Font[] = [
552
670
  { name: 'AdobeInvisFont', script: 0, type: 0, synthetic: 0 },
@@ -555,7 +673,7 @@ export function encodeEngineData(data: LayerTextData) {
555
673
  const defFont = data.style?.font || data.styleRuns?.find(s => s.style.font)?.style.font || defaultFont;
556
674
  const paragraphRunArray: ParagraphRun[] = [];
557
675
  const paragraphRunLengthArray: number[] = [];
558
- const paragraphRuns = data.paragraphStyleRuns;
676
+ const paragraphRuns = adjustRunLengths(data.paragraphStyleRuns, removedIndices);
559
677
 
560
678
  if (paragraphRuns && paragraphRuns.length) {
561
679
  let leftLength = text.length;
@@ -564,14 +682,14 @@ export function encodeEngineData(data: LayerTextData) {
564
682
  let runLength = Math.min(run.length, leftLength);
565
683
  leftLength -= runLength;
566
684
 
567
- if (!runLength) continue; // ignore 0 size runs
568
-
569
- // extend last run if it's only for trailing \r
685
+ // extend last run if it's only for trailing \r (last run has 0 length if it's for empty last paragraph)
570
686
  if (leftLength === 1 && run === paragraphRuns[paragraphRuns.length - 1]) {
571
687
  runLength++;
572
688
  leftLength--;
573
689
  }
574
690
 
691
+ if (!runLength) continue; // ignore 0 size runs
692
+
575
693
  paragraphRunLengthArray.push(runLength);
576
694
  paragraphRunArray.push({
577
695
  ParagraphSheet: {
@@ -609,7 +727,9 @@ export function encodeEngineData(data: LayerTextData) {
609
727
  }
610
728
 
611
729
  const styleSheetData = encodeStyle({ ...defaultStyle, font: defFont }, fonts);
612
- const styleRuns = data.styleRuns || [{ length: text.length, style: data.style || {} }];
730
+ const styleRuns = data.styleRuns?.length ?
731
+ adjustRunLengths(data.styleRuns, removedIndices)! :
732
+ [{ length: text.length, style: data.style || {} }];
613
733
  const styleRunArray: StyleRun[] = [];
614
734
  const styleRunLengthArray: number[] = [];
615
735
 
@@ -642,7 +762,7 @@ export function encodeEngineData(data: LayerTextData) {
642
762
  }
643
763
 
644
764
  // add extra run to the end if existing ones didn't fill it up
645
- if (leftLength && styleRuns.length) {
765
+ if (leftLength) {
646
766
  styleRunLengthArray.push(leftLength);
647
767
  styleRunArray.push({
648
768
  StyleSheet: {
@@ -657,6 +777,7 @@ export function encodeEngineData(data: LayerTextData) {
657
777
  }
658
778
 
659
779
  const gridInfo = { ...defaultGridInfo, ...data.gridInfo };
780
+ const antiAliasIndex = antialias.indexOf(data.antiAlias ?? 'sharp');
660
781
  const WritingDirection = data.orientation === 'vertical' ? 2 : 0;
661
782
  const Procession = data.orientation === 'vertical' ? 1 : 0;
662
783
  const ShapeType = data.shapeType === 'box' ? 1 : 0;
@@ -753,10 +874,10 @@ export function encodeEngineData(data: LayerTextData) {
753
874
  GridSize: gridInfo.size ?? 18,
754
875
  GridLeading: gridInfo.leading ?? 22,
755
876
  GridColor: encodeColor(gridInfo.color),
756
- GridLeadingFillColor: encodeColor(gridInfo.color),
877
+ GridLeadingFillColor: encodeColor(gridInfo.leadingFillColor),
757
878
  AlignLineHeightToGridFlags: !!gridInfo.alignLineHeightToGridFlags,
758
879
  },
759
- AntiAlias: antialias.indexOf(data.antiAlias ?? 'sharp'),
880
+ AntiAlias: antiAliasIndex === -1 ? antialias.indexOf('sharp') : antiAliasIndex,
760
881
  UseFractionalGlyphWidths: data.useFractionalGlyphWidths ?? true,
761
882
  Rendered: {
762
883
  Version: 1,
package/src/utf8.ts CHANGED
@@ -10,36 +10,48 @@ function charLengthInBytes(code: number): number {
10
10
  }
11
11
  }
12
12
 
13
- // Reads a scalar value at index `i`, replacing unpaired surrogates with U+FFFD,
13
+ // Returns scalar value at index `i` (combining surrogate pairs), replacing unpaired surrogates with U+FFFD,
14
14
  // matching how TextEncoder converts a string to a sequence of Unicode scalar values.
15
- function codePointAt(value: string, i: number): { code: number; size: number } {
15
+ // Values above 0xffff always take 2 UTF-16 code units.
16
+ function codePointAt(value: string, i: number): number {
16
17
  const code = value.charCodeAt(i);
17
18
 
19
+ if (code < 0xd800 || code > 0xdfff) return code;
20
+
18
21
  // high surrogate
19
- if (code >= 0xd800 && code <= 0xdbff && (i + 1) < value.length) {
22
+ if (code <= 0xdbff && (i + 1) < value.length) {
20
23
  const extra = value.charCodeAt(i + 1);
21
24
 
22
25
  // low surrogate
23
26
  if (extra >= 0xdc00 && extra <= 0xdfff) {
24
- return { code: ((code & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000, size: 2 };
27
+ return ((code & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
25
28
  }
26
29
  }
27
30
 
28
31
  // unpaired surrogate (lone high or lone low)
29
- if (code >= 0xd800 && code <= 0xdfff) {
30
- return { code: 0xfffd, size: 1 };
31
- }
32
-
33
- return { code, size: 1 };
32
+ return 0xfffd;
34
33
  }
35
34
 
36
35
  export function stringLengthInBytes(value: string): number {
37
36
  let result = 0;
38
37
 
39
- for (let i = 0; i < value.length;) {
40
- const { code, size } = codePointAt(value, i);
41
- result += charLengthInBytes(code);
42
- i += size;
38
+ for (let i = 0; i < value.length; i++) {
39
+ const code = value.charCodeAt(i);
40
+
41
+ if (code < 0x80) {
42
+ result += 1;
43
+ } else if (code < 0x800) {
44
+ result += 2;
45
+ } else {
46
+ const scalar = codePointAt(value, i);
47
+
48
+ if (scalar > 0xffff) {
49
+ result += 4;
50
+ i++;
51
+ } else {
52
+ result += 3;
53
+ }
54
+ }
43
55
  }
44
56
 
45
57
  return result;
@@ -73,10 +85,16 @@ function writeCharacter(buffer: Uint8Array | Buffer, offset: number, code: numbe
73
85
  }
74
86
 
75
87
  export function encodeStringTo(buffer: Uint8Array | Buffer, offset: number, value: string): number {
76
- for (let i = 0; i < value.length;) {
77
- const { code, size } = codePointAt(value, i);
78
- offset += writeCharacter(buffer, offset, code);
79
- i += size;
88
+ for (let i = 0; i < value.length; i++) {
89
+ const code = value.charCodeAt(i);
90
+
91
+ if (code < 0x80) {
92
+ buffer[offset++] = code;
93
+ } else {
94
+ const scalar = codePointAt(value, i);
95
+ if (scalar > 0xffff) i++;
96
+ offset += writeCharacter(buffer, offset, scalar);
97
+ }
80
98
  }
81
99
 
82
100
  return offset;
@@ -95,10 +113,6 @@ export function encodeString(value: string): Uint8Array {
95
113
  // UTF-8 decoder implementing the WHATWG Encoding Standard's non-fatal error handling,
96
114
  // so malformed byte sequences are replaced with U+FFFD instead of throwing, matching TextDecoder.
97
115
  export function decodeString(value: Uint8Array): string {
98
- if (value.byteLength > 1000 && typeof TextDecoder !== 'undefined') {
99
- return (new TextDecoder()).decode(value);
100
- }
101
-
102
116
  const result: string[] = [];
103
117
 
104
118
  function pushCodePoint(code: number) {