reamkit 1.25.1 → 1.26.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.
@@ -25,3 +25,16 @@ export declare function buildShadingMap(file: PdfFile, page: PdfPage): Map<strin
25
25
  * @returns Name → fill alpha, for the states that state one below 1.
26
26
  */
27
27
  export declare function buildAlphaMap(file: PdfFile, page: PdfPage): Map<string, number>;
28
+ /**
29
+ * §8.6 — how many components a colour space takes, and what they mean.
30
+ *
31
+ * `sc` / `scn` give bare numbers; only the space in force says whether `1 1 1`
32
+ * is white or something else, and whether a lone `1` is white (DeviceGray) or
33
+ * the colorant at full strength (Separation), which is usually black.
34
+ */
35
+ export interface ColorSpaceInfo {
36
+ readonly kind: 'gray' | 'rgb' | 'cmyk' | 'tint';
37
+ readonly components: number;
38
+ }
39
+ /** The spaces a page's `/ColorSpace` resources name, by name. */
40
+ export declare function buildColorSpaceMap(file: PdfFile, page: PdfPage): Map<string, ColorSpaceInfo>;
@@ -1,4 +1,4 @@
1
- import { PDF_NULL, PdfStream } from "../pdf/objects.js";
1
+ import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
2
2
  //#region src/pdf-reader/shading.ts
3
3
  /**
4
4
  * Resolve a page's `/Pattern` resources into gradient fills (E-PDF EP16c, ISO
@@ -179,5 +179,71 @@ function buildAlphaMap(file, page) {
179
179
  }
180
180
  return out;
181
181
  }
182
+ /** The spaces a page's `/ColorSpace` resources name, by name. */
183
+ function buildColorSpaceMap(file, page) {
184
+ const out = /* @__PURE__ */ new Map();
185
+ if (!page.resources) return out;
186
+ const spaces = file.get(page.resources, "ColorSpace");
187
+ if (!(spaces instanceof Map)) return out;
188
+ for (const [name, value] of spaces) {
189
+ const info = colorSpaceInfo(file, file.resolve(value));
190
+ if (info) out.set(name, info);
191
+ }
192
+ return out;
193
+ }
194
+ /** What a colour space object comes to, or `undefined` for one not read. */
195
+ function colorSpaceInfo(file, cs) {
196
+ if (cs instanceof PdfName) return byFamily(cs.value, 0);
197
+ if (!Array.isArray(cs) || cs.length === 0) return void 0;
198
+ const head = file.resolve(cs[0]);
199
+ if (!(head instanceof PdfName)) return void 0;
200
+ if (head.value === "ICCBased") {
201
+ const stream = file.resolve(cs[1] ?? PDF_NULL);
202
+ const n = stream instanceof PdfStream ? file.get(stream.dict, "N") : void 0;
203
+ return byFamily("ICCBased", typeof n === "number" ? n : 3);
204
+ }
205
+ if (head.value === "Indexed") return;
206
+ if (head.value === "Separation" || head.value === "DeviceN") {
207
+ const names = file.resolve(cs[1] ?? PDF_NULL);
208
+ return {
209
+ kind: "tint",
210
+ components: head.value === "Separation" ? 1 : Array.isArray(names) ? names.length : 1
211
+ };
212
+ }
213
+ return byFamily(head.value, 0);
214
+ }
215
+ function byFamily(family, n) {
216
+ switch (family) {
217
+ case "DeviceGray":
218
+ case "CalGray":
219
+ case "G": return {
220
+ kind: "gray",
221
+ components: 1
222
+ };
223
+ case "DeviceRGB":
224
+ case "CalRGB":
225
+ case "Lab":
226
+ case "RGB": return {
227
+ kind: "rgb",
228
+ components: 3
229
+ };
230
+ case "DeviceCMYK":
231
+ case "CMYK": return {
232
+ kind: "cmyk",
233
+ components: 4
234
+ };
235
+ case "ICCBased": return n === 1 ? {
236
+ kind: "gray",
237
+ components: 1
238
+ } : n === 4 ? {
239
+ kind: "cmyk",
240
+ components: 4
241
+ } : {
242
+ kind: "rgb",
243
+ components: 3
244
+ };
245
+ default: return;
246
+ }
247
+ }
182
248
  //#endregion
183
- export { buildAlphaMap, buildShadingMap };
249
+ export { buildAlphaMap, buildColorSpaceMap, buildShadingMap };
@@ -3,7 +3,7 @@ import { FEATURES } from "../core/ir/features.js";
3
3
  import { IDENTITY, interpretContent, multiply } from "./content.js";
4
4
  import { collectPageAppearances } from "./annots.js";
5
5
  import { buildFonts } from "./text.js";
6
- import { buildAlphaMap, buildShadingMap } from "./shading.js";
6
+ import { buildAlphaMap, buildColorSpaceMap, buildShadingMap } from "./shading.js";
7
7
  //#region src/pdf-reader/vector.ts
8
8
  /**
9
9
  * Every vector the page paints, its FORM XOBJECTS included (§8.8).
@@ -17,14 +17,14 @@ import { buildAlphaMap, buildShadingMap } from "./shading.js";
17
17
  * The same walk `collectPageImages` already makes, with the same depth and
18
18
  * cycle guards, collecting paths instead of pictures.
19
19
  */
20
- function paintedVectors(file, page, shadings, alphas) {
20
+ function paintedVectors(file, page, shadings, alphas, spaces) {
21
21
  const out = [];
22
22
  const visiting = /* @__PURE__ */ new Set();
23
23
  const walk = (resources, content, baseCtm, depth, prefix) => {
24
24
  if (out.length >= MAX_VECTORS) return;
25
25
  const xobjects = resources ? file.get(resources, "XObject") : PDF_NULL;
26
26
  const xobjDict = xobjects instanceof Map ? xobjects : void 0;
27
- const result = interpretContent(content, buildFonts(file, resources), baseCtm, shadings, alphas);
27
+ const result = interpretContent(content, buildFonts(file, resources), baseCtm, shadings, alphas, spaces);
28
28
  const events = [
29
29
  ...result.vectors.map((vector) => ({
30
30
  order: vector.order,
@@ -120,9 +120,10 @@ function collectPageVectors(file, page, occupied = []) {
120
120
  const pageArea = Math.max(1, Math.abs((px1 - px0) * (py1 - py0)));
121
121
  const shadings = buildShadingMap(file, page);
122
122
  const alphas = buildAlphaMap(file, page);
123
+ const spaces = buildColorSpaceMap(file, page);
123
124
  const out = [];
124
125
  const painted = [...occupied];
125
- const raws = paintedVectors(file, page, shadings, alphas);
126
+ const raws = paintedVectors(file, page, shadings, alphas, spaces);
126
127
  for (const raw of raws) {
127
128
  if (out.length >= MAX_VECTORS) break;
128
129
  const v = clipped(raw);
@@ -43,10 +43,6 @@ var RASTER_MEDIA = {
43
43
  ext: "jpeg",
44
44
  contentType: "image/jpeg"
45
45
  },
46
- jpeg2000: {
47
- ext: "jp2",
48
- contentType: "image/jp2"
49
- },
50
46
  gif: {
51
47
  ext: "gif",
52
48
  contentType: "image/gif"
@@ -56,6 +52,18 @@ var RASTER_MEDIA = {
56
52
  contentType: "image/bmp"
57
53
  }
58
54
  };
55
+ /**
56
+ * Why an image was not written, in terms of the image — for the loss report,
57
+ * which used to say "image bytes missing" whatever the reason, including for
58
+ * bytes that were all there and in a format the format does not admit.
59
+ */
60
+ function imageRefusal(resource, state) {
61
+ const bytes = resource === void 0 ? void 0 : state.resources.get(resource);
62
+ if (!bytes) return "image bytes missing";
63
+ const format = detectImageFormat(bytes);
64
+ if (format === "jpeg2000") return "JPEG 2000 picture dropped — §15.2.14 admits no such image part, and no reader of a .docx shows one";
65
+ return `picture dropped — ${format ?? "an unrecognised"} is not an image part a .docx may carry`;
66
+ }
59
67
  function mediaInfo(bytes) {
60
68
  const raster = detectImageFormat(bytes);
61
69
  if (raster) return RASTER_MEDIA[raster];
@@ -137,9 +145,7 @@ function writeDocx(flow) {
137
145
  if (i === sections.length - 1) finalSectPr = sp;
138
146
  else sectPrByClosingIndex.set(sec.endIndex - 1, sp);
139
147
  });
140
- flow.body.forEach((el, idx) => {
141
- emitBlock(body, el, losses, state, docScope, sectPrByClosingIndex.get(idx));
142
- });
148
+ emitBody(body, flow.body, losses, state, docScope, sectPrByClosingIndex);
143
149
  if (finalSectPr) body.push(finalSectPr);
144
150
  const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><w:body>${body.join("")}</w:body></w:document>`;
145
151
  const numberingPart = flow.body.some((el) => el.kind === "paragraph" && el.paragraph.properties.numbering !== void 0) && flow.numbering ? {
@@ -365,6 +371,59 @@ var docxWriter = {
365
371
  supports: new Set([FEATURES.text]),
366
372
  write: (doc) => writeDocx(doc)
367
373
  };
374
+ /**
375
+ * The document body, with consecutive floating drawings gathered into one
376
+ * carrier paragraph.
377
+ *
378
+ * §20.4.2.3 — a floating drawing is placed by its anchor and not by where its
379
+ * paragraph lands, so the paragraph is only somewhere for the anchor to hang
380
+ * from. Given one paragraph EACH, a page of placed artwork is that many
381
+ * paragraphs of flow: 160F-2019.pdf's one-page form is 355 drawings, and Word
382
+ * ran it to sixteen pages of empty lines with the artwork anchored to them.
383
+ * Gathered, the page costs one paragraph however much is placed on it.
384
+ *
385
+ * A block that closes a section keeps its own paragraph — the section break
386
+ * rides that paragraph's properties, and it must be the last one in the
387
+ * section.
388
+ */
389
+ function emitBody(out, blocks, losses, state, scope, sectPrByClosingIndex) {
390
+ let carried = [];
391
+ const flush = () => {
392
+ if (carried.length > 0) out.push(`<w:p>${carried.join("")}</w:p>`);
393
+ carried = [];
394
+ };
395
+ blocks.forEach((el, idx) => {
396
+ const closing = sectPrByClosingIndex.get(idx);
397
+ const anchored = closing === void 0 ? floatingDrawingRun(el, losses, state, scope) : void 0;
398
+ if (anchored !== void 0) {
399
+ carried.push(anchored);
400
+ return;
401
+ }
402
+ flush();
403
+ emitBlock(out, el, losses, state, scope, closing);
404
+ });
405
+ flush();
406
+ }
407
+ /**
408
+ * The run holding one block's drawing, when that block states where on the page
409
+ * it goes — `undefined` for everything that flows, which the ordinary block
410
+ * emitter handles.
411
+ */
412
+ function floatingDrawingRun(el, losses, state, scope) {
413
+ if (el.kind === "image" && el.image.float) {
414
+ const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float);
415
+ if (drawing === "") {
416
+ losses.push({
417
+ severity: "dropped",
418
+ feature: FEATURES.images,
419
+ detail: imageRefusal(el.image.resource, state)
420
+ });
421
+ return "";
422
+ }
423
+ return `<w:r>${drawing}</w:r>`;
424
+ }
425
+ if (el.kind === "shape" && el.shape.float) return `<w:r>${shapeDrawingXml(el.shape, losses, state, scope)}</w:r>`;
426
+ }
368
427
  function emitBlock(out, el, losses, state, scope, closingSectPr) {
369
428
  if (el.kind === "paragraph") {
370
429
  out.push(paragraphXml(el.paragraph, state, scope, closingSectPr));
@@ -376,13 +435,13 @@ function emitBlock(out, el, losses, state, scope, closingSectPr) {
376
435
  return;
377
436
  }
378
437
  if (el.kind === "image") {
379
- const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope);
438
+ const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float);
380
439
  if (drawing) out.push(`<w:p>${pPrWithSect(el.image.paragraphProperties, closingSectPr)}<w:r>${drawing}</w:r></w:p>`);
381
440
  else {
382
441
  losses.push({
383
442
  severity: "dropped",
384
443
  feature: FEATURES.images,
385
- detail: "image bytes missing"
444
+ detail: imageRefusal(el.image.resource, state)
386
445
  });
387
446
  if (closingSectPr) out.push(`<w:p><w:pPr>${closingSectPr}</w:pPr></w:p>`);
388
447
  }
@@ -425,14 +484,82 @@ function chartBlockXml(chart, state, scope, losses) {
425
484
  const id = ++state.drawingSeq;
426
485
  return `<w:drawing><wp:inline xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${id}" name="Chart ${id}"${chart.altText ? ` descr="${escapeAttr(chart.altText)}"` : ""}/><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart"><c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="${relId}"/></a:graphicData></a:graphic></wp:inline></w:drawing>`;
427
486
  }
428
- function drawingXml(resource, widthPt, heightPt, altText, state, scope) {
487
+ /**
488
+ * §20.4.2.3 — the frame a drawing is placed by: `wp:anchor` where the block
489
+ * states where on the page it belongs, `wp:inline` where it belongs in the run
490
+ * of text.
491
+ *
492
+ * Everything used to be inline. The document model has carried the anchor since
493
+ * the reader learned to read one, and the PDF reconstruction fills it in for
494
+ * every line and rule it lifts off a page — and all of it was thrown away here,
495
+ * so a converted page came out as a column of drawings stacked in document
496
+ * order. S2.pdf's two colour wheels marched down the left margin as eighteen
497
+ * separate wedges; 160F-2019.pdf's one-page form ran to sixteen pages, because
498
+ * each of its 355 drawings took a paragraph of its own in the flow.
499
+ *
500
+ * The attribute list is not decoration: CT_Anchor requires `simplePos`,
501
+ * `relativeHeight`, `behindDoc`, `locked`, `layoutInCell` and `allowOverlap`,
502
+ * and the children are a SEQUENCE — position, extent, wrap, docPr, graphic —
503
+ * that Word refuses out of order.
504
+ *
505
+ * @param float The placement, or `undefined` for a drawing that flows.
506
+ * @param cx Width in EMU.
507
+ * @param cy Height in EMU.
508
+ * @param head The `wp:docPr` (and anything else) that precedes the graphic.
509
+ * @param graphic The `a:graphic` element the frame carries.
510
+ */
511
+ function drawingFrame(float, cx, cy, head, graphic, state, rotation60k) {
512
+ const WP_NS = " xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\"";
513
+ if (!float) return `<w:drawing><wp:inline${WP_NS}><wp:extent cx="${cx}" cy="${cy}"/>${head}${graphic}</wp:inline></w:drawing>`;
514
+ const emu = (pt) => Number.isFinite(pt) ? Math.round((pt ?? 0) * EMU_PER_PT) : 0;
515
+ const d = float.wrapDist;
516
+ const dist = ` distT="${String(emu(d?.topPt))}" distB="${String(emu(d?.bottomPt))}" distL="${String(emu(d?.leftPt))}" distR="${String(emu(d?.rightPt))}"`;
517
+ const z = float.zOrder ?? ++state.drawingSeq;
518
+ const attrs = `${dist} simplePos="0" relativeHeight="${String(z)}" behindDoc="${float.behind ? "1" : "0"}" locked="0" layoutInCell="${float.inCell === false ? "0" : "1"}" allowOverlap="1"`;
519
+ const pos = (tag, p, fallback) => {
520
+ const from = p?.relativeFrom ?? fallback;
521
+ const body = p?.align !== void 0 ? `<wp:align>${p.align}</wp:align>` : `<wp:posOffset>${String(emu(p?.offsetPt))}</wp:posOffset>`;
522
+ return `<${tag} relativeFrom="${escapeAttr(from)}">${body}</${tag}>`;
523
+ };
524
+ const side = ` wrapText="${float.wrapSide ?? "bothSides"}"`;
525
+ const wrap = float.wrap === "square" ? `<wp:wrapSquare${side}/>` : float.wrap === "tight" ? `<wp:wrapTight${side}/>` : float.wrap === "through" ? `<wp:wrapThrough${side}/>` : float.wrap === "topAndBottom" || float.wrap === "notBeside" ? "<wp:wrapTopAndBottom/>" : "<wp:wrapNone/>";
526
+ return `<w:drawing><wp:anchor${WP_NS}${attrs}><wp:simplePos x="0" y="0"/>` + pos("wp:positionH", float.posH, "column") + pos("wp:positionV", float.posV, "paragraph") + `<wp:extent cx="${cx}" cy="${cy}"/>` + effectExtentXml(cx, cy, rotation60k) + wrap + head + graphic + "</wp:anchor></w:drawing>";
527
+ }
528
+ /**
529
+ * §20.4.2.3 `wp:effectExtent` — how far the drawing reaches PAST the extent.
530
+ *
531
+ * `wp:extent` is the shape's own size, unrotated: fdo75722-dml.docx states
532
+ * 1964690 × 1240790 there and repeats it in `a:ext`, then carries the turn's
533
+ * overhang here (l 198120, t 433705, r 199390, b 430530). Written as zeroes —
534
+ * which they always were — a renderer reserves the flat box for a shape that
535
+ * draws across the turned one, and 160F-2019.pdf's "Nature", set on its side
536
+ * down the middle of a column, came back lying flat across it.
537
+ *
538
+ * A box `w × h` turned by θ about its centre spans `|w·cosθ| + |h·sinθ|`
539
+ * across and `|w·sinθ| + |h·cosθ|` down; half of each growth hangs off each
540
+ * side. A turn that makes the box no wider hangs off by nothing.
541
+ */
542
+ function effectExtentXml(cx, cy, rotation60k) {
543
+ const none = "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>";
544
+ if (rotation60k === void 0 || !Number.isFinite(rotation60k) || rotation60k % 216e5 === 0) return none;
545
+ const rad = rotation60k / 6e4 * Math.PI / 180;
546
+ const c = Math.abs(Math.cos(rad));
547
+ const s = Math.abs(Math.sin(rad));
548
+ const overhang = (span, turned) => Math.max(0, Math.round((turned - span) / 2));
549
+ const h = overhang(cx, cx * c + cy * s);
550
+ const v = overhang(cy, cx * s + cy * c);
551
+ return h === 0 && v === 0 ? none : `<wp:effectExtent l="${String(h)}" t="${String(v)}" r="${String(h)}" b="${String(v)}"/>`;
552
+ }
553
+ function drawingXml(resource, widthPt, heightPt, altText, state, scope, float) {
429
554
  if (resource === void 0) return "";
430
555
  const relId = mediaRelId(resource, state, scope);
431
556
  if (relId === void 0) return "";
432
557
  const cx = Math.round(widthPt * EMU_PER_PT);
433
558
  const cy = Math.round(heightPt * EMU_PER_PT);
434
559
  const id = ++state.drawingSeq;
435
- return `<w:drawing><wp:inline xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${id}" name="Image ${id}"${altText ? ` descr="${escapeAttr(altText)}"` : ""}/><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="Image ${id}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>`;
560
+ const descr = altText ? ` descr="${escapeAttr(altText)}"` : "";
561
+ const graphic = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="Image ${id}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic>`;
562
+ return drawingFrame(float, cx, cy, `<wp:docPr id="${id}" name="Image ${id}"${descr}/>`, graphic, state);
436
563
  }
437
564
  var WPS_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
438
565
  function shapeDrawingXml(shape, losses, state, scope) {
@@ -442,7 +569,8 @@ function shapeDrawingXml(shape, losses, state, scope) {
442
569
  const descr = shape.altText ? ` descr="${escapeAttr(shape.altText)}"` : "";
443
570
  const spPr = `<wps:spPr>${xfrmXml(shape.transform, cx, cy)}${geomXml(shape.geometry)}${fillXml(shape.fill)}${shape.line ? lineXml(shape.line) : ""}</wps:spPr>`;
444
571
  const txbx = shape.text ? txbxXml(shape.text, losses, state, scope) : "";
445
- return `<w:drawing><wp:inline xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${id}" name="Shape ${id}"${descr}/><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="${WPS_URI}"><wps:wsp xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"><wps:cNvSpPr/>` + spPr + txbx + bodyPrXml(shape.text) + "</wps:wsp></a:graphicData></a:graphic></wp:inline></w:drawing>";
572
+ const graphic = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="${WPS_URI}"><wps:wsp xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"><wps:cNvSpPr/>` + spPr + txbx + bodyPrXml(shape.text) + "</wps:wsp></a:graphicData></a:graphic>";
573
+ return drawingFrame(shape.float, cx, cy, `<wp:docPr id="${id}" name="Shape ${id}"${descr}/>`, graphic, state, shape.transform?.rotation60k);
446
574
  }
447
575
  function xfrmXml(t, cx, cy) {
448
576
  return `<a:xfrm${t?.rotation60k !== void 0 ? ` rot="${t.rotation60k}"` : ""}${t?.flipH ? " flipH=\"1\"" : ""}${t?.flipV ? " flipV=\"1\"" : ""}><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reamkit",
3
- "version": "1.25.1",
3
+ "version": "1.26.0",
4
4
  "description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
5
5
  "license": "MIT",
6
6
  "author": "Alex Krassavin <info@reamkit.dev>",