ooxml.js 2.0.3 → 2.1.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/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { XMLBuilder, XMLParser } from "fast-xml-parser";
3
3
  import { unzipSync, zipSync } from "fflate";
4
+ import { AlignmentSchema, BoxSchema, COLOR_BLACK, ColorSchema, ContentBlockSchema, ContentImageBlockSchema, ContentListMembershipSchema, ContentPageBreakSchema, ContentParagraphSchema, ContentRunSchema, ContentSectionSchema, ContentSectionSchema as ContentSectionSchema$1, ContentShapeSchema, ContentSlideSchema, ContentSlideSchema as ContentSlideSchema$1, ContentTableCellSchema, ContentTableRowSchema, ContentTableSchema, MarginsSchema, PAGE_SIZE_A4, PAGE_SIZE_LETTER, PAGE_SIZE_LETTER as PAGE_SIZE_LETTER$1, PageSizeSchema, SLIDE_SIZE_STANDARD, SLIDE_SIZE_WIDESCREEN, SLIDE_SIZE_WIDESCREEN as SLIDE_SIZE_WIDESCREEN$1, colorToRgbHex, isContentBlock, rgbHexToColor, rgbHexToColor as rgbHexToColor$1 } from "document-content-model";
4
5
  //#region src/model/node.ts
5
6
  const AttributeSchema = z.object({
6
7
  name: z.string(),
@@ -27,14 +28,14 @@ const XmlPiSchema = z.object({
27
28
  target: z.string(),
28
29
  content: z.string()
29
30
  });
30
- function isRecord$2(value) {
31
+ function isRecord$1(value) {
31
32
  return typeof value === "object" && value !== null && !Array.isArray(value);
32
33
  }
33
34
  function isAttribute(value) {
34
- return isRecord$2(value) && typeof value.name === "string" && typeof value.value === "string";
35
+ return isRecord$1(value) && typeof value.name === "string" && typeof value.value === "string";
35
36
  }
36
37
  function isXmlNode(value) {
37
- if (!isRecord$2(value)) return false;
38
+ if (!isRecord$1(value)) return false;
38
39
  const t = value.type;
39
40
  if (t === "text" || t === "cdata" || t === "comment") return typeof value.value === "string";
40
41
  if (t === "declaration") return Array.isArray(value.attributes) && value.attributes.every(isAttribute);
@@ -129,7 +130,7 @@ const PARSER = new XMLParser({
129
130
  function parseXml(xml) {
130
131
  return parseNodes(PARSER.parse(xml));
131
132
  }
132
- function isRecord$1(value) {
133
+ function isRecord(value) {
133
134
  return typeof value === "object" && value !== null && !Array.isArray(value);
134
135
  }
135
136
  function isUnknownArray$1(value) {
@@ -144,7 +145,7 @@ function parseNodes(raw) {
144
145
  return raw.map(parseNode);
145
146
  }
146
147
  function parseNode(raw) {
147
- if (!isRecord$1(raw)) throw new Error("fast-xml-parser node was not an object");
148
+ if (!isRecord(raw)) throw new Error("fast-xml-parser node was not an object");
148
149
  let tagKey;
149
150
  for (const key of Object.keys(raw)) if (key !== ":@") {
150
151
  if (tagKey !== void 0) throw new Error("XML node had multiple tag keys");
@@ -182,7 +183,7 @@ function parseNode(raw) {
182
183
  }
183
184
  function parseAttributes(raw) {
184
185
  if (raw === void 0) return [];
185
- if (!isRecord$1(raw)) throw new Error("XML attributes were not an object");
186
+ if (!isRecord(raw)) throw new Error("XML attributes were not an object");
186
187
  const attrs = [];
187
188
  for (const key of Object.keys(raw)) {
188
189
  if (!key.startsWith("@_")) throw new Error(`unexpected attribute key without @_ prefix: ${key}`);
@@ -196,7 +197,7 @@ function parseAttributes(raw) {
196
197
  function scalarText(raw) {
197
198
  if (!isUnknownArray$1(raw) || raw.length === 0) throw new Error("expected a scalar-text wrapper array");
198
199
  const first = raw[0];
199
- if (!isRecord$1(first)) throw new Error("scalar-text wrapper was not an object");
200
+ if (!isRecord(first)) throw new Error("scalar-text wrapper was not an object");
200
201
  return asString(first["#text"]);
201
202
  }
202
203
  //#endregion
@@ -533,73 +534,7 @@ function encodeCompactPackage(cpkg) {
533
534
  return z.encode(compactPackageCodec, cpkg);
534
535
  }
535
536
  //#endregion
536
- //#region src/typed/shared/geometry.ts
537
- const BoxSchema = z.object({
538
- xPt: z.number(),
539
- yPt: z.number(),
540
- widthPt: z.number().nonnegative(),
541
- heightPt: z.number().nonnegative()
542
- });
543
- const PageSizeSchema = z.object({
544
- widthPt: z.number().positive(),
545
- heightPt: z.number().positive()
546
- });
547
- const MarginsSchema = z.object({
548
- topPt: z.number().nonnegative(),
549
- rightPt: z.number().nonnegative(),
550
- bottomPt: z.number().nonnegative(),
551
- leftPt: z.number().nonnegative()
552
- });
553
- const PAGE_SIZE_LETTER = {
554
- widthPt: 612,
555
- heightPt: 792
556
- };
557
- const PAGE_SIZE_A4 = {
558
- widthPt: 595.28,
559
- heightPt: 841.89
560
- };
561
- const SLIDE_SIZE_WIDESCREEN = {
562
- widthPt: 960,
563
- heightPt: 540
564
- };
565
- const SLIDE_SIZE_STANDARD = {
566
- widthPt: 720,
567
- heightPt: 540
568
- };
569
- //#endregion
570
537
  //#region src/typed/shared/color.ts
571
- const ColorSchema = z.object({
572
- r: z.number().min(0).max(1),
573
- g: z.number().min(0).max(1),
574
- b: z.number().min(0).max(1)
575
- });
576
- const COLOR_BLACK = {
577
- r: 0,
578
- g: 0,
579
- b: 0
580
- };
581
- const HEX_COLOR_PATTERN = /^#?([0-9a-fA-F]{6})$/;
582
- const HEX_BYTE_MAX = 255;
583
- function rgbHexToColor(hex) {
584
- const match = HEX_COLOR_PATTERN.exec(hex);
585
- if (match === null) throw new Error(`not a 6-digit hex colour: ${hex}`);
586
- const digits = match[1];
587
- if (digits === void 0) throw new Error(`not a 6-digit hex colour: ${hex}`);
588
- const r = Number.parseInt(digits.slice(0, 2), 16);
589
- const g = Number.parseInt(digits.slice(2, 4), 16);
590
- const b = Number.parseInt(digits.slice(4, 6), 16);
591
- return {
592
- r: r / HEX_BYTE_MAX,
593
- g: g / HEX_BYTE_MAX,
594
- b: b / HEX_BYTE_MAX
595
- };
596
- }
597
- function toHexByte(component) {
598
- return Math.round(component * HEX_BYTE_MAX).toString(16).padStart(2, "0");
599
- }
600
- function colorToRgbHex(color) {
601
- return `${toHexByte(color.r)}${toHexByte(color.g)}${toHexByte(color.b)}`;
602
- }
603
538
  const OOXML_PERCENT_SCALE = 1e5;
604
539
  function clamp01(x) {
605
540
  return Math.max(0, Math.min(1, x));
@@ -682,14 +617,6 @@ function applyColorTransforms(base, transforms) {
682
617
  return color;
683
618
  }
684
619
  //#endregion
685
- //#region src/typed/shared/style.ts
686
- const AlignmentSchema = z.enum([
687
- "left",
688
- "center",
689
- "right",
690
- "justify"
691
- ]);
692
- //#endregion
693
620
  //#region src/typed/shared/metadata.ts
694
621
  const DocumentMetadataSchema = z.object({
695
622
  title: z.string().optional(),
@@ -729,103 +656,6 @@ function readCoreProperties(pkg) {
729
656
  };
730
657
  }
731
658
  //#endregion
732
- //#region src/typed/shared/content.ts
733
- const ContentRunSchema = z.object({
734
- text: z.string(),
735
- bold: z.boolean().optional(),
736
- italic: z.boolean().optional(),
737
- underline: z.boolean().optional(),
738
- strike: z.boolean().optional(),
739
- fontFamily: z.string().optional(),
740
- sizePt: z.number().positive().optional(),
741
- color: ColorSchema.optional(),
742
- hyperlink: z.string().optional()
743
- });
744
- const ContentListMembershipSchema = z.object({
745
- numId: z.string(),
746
- level: z.number().int().nonnegative()
747
- });
748
- const ContentParagraphSchema = z.object({
749
- kind: z.literal("paragraph"),
750
- runs: z.array(ContentRunSchema),
751
- styleId: z.string().optional(),
752
- alignment: AlignmentSchema.optional(),
753
- list: ContentListMembershipSchema.optional(),
754
- spacingBeforePt: z.number().optional(),
755
- spacingAfterPt: z.number().optional(),
756
- lineSpacing: z.number().positive().optional(),
757
- indentLeftPt: z.number().optional(),
758
- indentFirstLinePt: z.number().optional()
759
- });
760
- const ContentImageBlockSchema = z.object({
761
- kind: z.literal("image"),
762
- format: z.enum(["png", "jpeg"]),
763
- base64: z.string(),
764
- widthPt: z.number().positive(),
765
- heightPt: z.number().positive(),
766
- altText: z.string().optional()
767
- });
768
- const ContentPageBreakSchema = z.object({ kind: z.literal("pageBreak") });
769
- function isRecord(value) {
770
- return typeof value === "object" && value !== null && !Array.isArray(value);
771
- }
772
- function isContentRun(value) {
773
- return isRecord(value) && typeof value.text === "string";
774
- }
775
- function isContentTableCell(value) {
776
- return isRecord(value) && Array.isArray(value.blocks) && value.blocks.every(isContentBlock);
777
- }
778
- function isContentTableRow(value) {
779
- return isRecord(value) && Array.isArray(value.cells) && value.cells.every(isContentTableCell) && (value.heightPt === void 0 || typeof value.heightPt === "number");
780
- }
781
- function isContentBlock(value) {
782
- if (!isRecord(value)) return false;
783
- const kind = value.kind;
784
- if (kind === "paragraph") return Array.isArray(value.runs) && value.runs.every(isContentRun);
785
- if (kind === "image") return (value.format === "png" || value.format === "jpeg") && typeof value.base64 === "string" && typeof value.widthPt === "number" && typeof value.heightPt === "number";
786
- if (kind === "pageBreak") return true;
787
- if (kind === "table") return Array.isArray(value.rows) && value.rows.every(isContentTableRow) && Array.isArray(value.columnWidthsPt) && value.columnWidthsPt.every((w) => typeof w === "number");
788
- return false;
789
- }
790
- const ContentBlockSchema = z.custom(isContentBlock);
791
- const ContentTableCellSchema = z.object({
792
- blocks: z.array(ContentBlockSchema),
793
- colSpan: z.number().int().positive().optional(),
794
- rowSpan: z.number().int().positive().optional(),
795
- background: ColorSchema.optional()
796
- });
797
- const ContentTableRowSchema = z.object({
798
- cells: z.array(ContentTableCellSchema),
799
- heightPt: z.number().positive().optional()
800
- });
801
- const ContentTableSchema = z.object({
802
- kind: z.literal("table"),
803
- rows: z.array(ContentTableRowSchema),
804
- columnWidthsPt: z.array(z.number().positive())
805
- });
806
- const ContentSectionSchema = z.object({
807
- pageSize: PageSizeSchema,
808
- margins: MarginsSchema,
809
- blocks: z.array(ContentBlockSchema)
810
- });
811
- const ContentShapeSchema = z.object({
812
- name: z.string().optional(),
813
- frame: BoxSchema,
814
- rotationDeg: z.number().optional(),
815
- insetLeftPt: z.number().nonnegative(),
816
- insetTopPt: z.number().nonnegative(),
817
- insetRightPt: z.number().nonnegative(),
818
- insetBottomPt: z.number().nonnegative(),
819
- fontScale: z.number().positive().optional(),
820
- lineSpacingReduction: z.number().nonnegative().optional(),
821
- blocks: z.array(ContentBlockSchema)
822
- });
823
- const ContentSlideSchema = z.object({
824
- size: PageSizeSchema,
825
- shapes: z.array(ContentShapeSchema),
826
- notes: z.string()
827
- });
828
- //#endregion
829
659
  //#region src/image/sniff.ts
830
660
  const PNG_SIGNATURE = [
831
661
  137,
@@ -908,11 +738,11 @@ const CLR_SCHEME_SLOTS = [
908
738
  function readThemeSlotColor(colorEl) {
909
739
  if (colorEl.tag === "a:srgbClr") {
910
740
  const val = attr(colorEl, "val");
911
- return val === void 0 ? void 0 : rgbHexToColor(val);
741
+ return val === void 0 ? void 0 : rgbHexToColor$1(val);
912
742
  }
913
743
  if (colorEl.tag === "a:sysClr") {
914
744
  const lastClr = attr(colorEl, "lastClr");
915
- if (lastClr !== void 0) return rgbHexToColor(lastClr);
745
+ if (lastClr !== void 0) return rgbHexToColor$1(lastClr);
916
746
  return attr(colorEl, "val") === "window" ? {
917
747
  r: 1,
918
748
  g: 1,
@@ -1001,7 +831,7 @@ function readSchemeColor(schemeClrEl, colorMap, theme) {
1001
831
  }
1002
832
  function readSrgbColor(srgbClrEl) {
1003
833
  const val = attr(srgbClrEl, "val");
1004
- return val === void 0 ? void 0 : applyColorTransforms(rgbHexToColor(val), readColorTransforms(srgbClrEl));
834
+ return val === void 0 ? void 0 : applyColorTransforms(rgbHexToColor$1(val), readColorTransforms(srgbClrEl));
1005
835
  }
1006
836
  function readSolidFillColor(solidFillEl, colorMap, theme) {
1007
837
  if (solidFillEl === void 0) return;
@@ -1043,6 +873,22 @@ function applyGroupTransform(group, childFrame) {
1043
873
  };
1044
874
  }
1045
875
  //#endregion
876
+ //#region src/typed/shared/source-path.ts
877
+ function assignSourcePaths(blocks, prefix) {
878
+ blocks.forEach((block, blockIndex) => {
879
+ const blockPath = `${prefix}.blocks[${blockIndex}]`;
880
+ block.sourcePath = blockPath;
881
+ if (block.kind === "paragraph") block.runs.forEach((run, runIndex) => {
882
+ run.sourcePath = `${blockPath}.runs[${runIndex}]`;
883
+ });
884
+ else if (block.kind === "table") block.rows.forEach((row, rowIndex) => {
885
+ row.cells.forEach((cell, cellIndex) => {
886
+ assignSourcePaths(cell.blocks, `${blockPath}.rows[${rowIndex}].cells[${cellIndex}]`);
887
+ });
888
+ });
889
+ });
890
+ }
891
+ //#endregion
1046
892
  //#region src/typed/docx/styles.ts
1047
893
  function mergeParagraphLayer(base, layer) {
1048
894
  return {
@@ -1078,7 +924,7 @@ function readUnderline(u) {
1078
924
  function readRunColor(colorEl) {
1079
925
  if (colorEl === void 0) return;
1080
926
  const val = attr(colorEl, "w:val");
1081
- return val === void 0 || val === "auto" ? void 0 : rgbHexToColor(val);
927
+ return val === void 0 || val === "auto" ? void 0 : rgbHexToColor$1(val);
1082
928
  }
1083
929
  function readRunFontFamily(rFonts, theme) {
1084
930
  if (rFonts === void 0) return;
@@ -1196,7 +1042,7 @@ const FootnoteSchema = z.object({
1196
1042
  });
1197
1043
  const DocxDocumentSchema = z.object({
1198
1044
  metadata: DocumentMetadataSchema,
1199
- sections: z.array(ContentSectionSchema),
1045
+ sections: z.array(ContentSectionSchema$1),
1200
1046
  comments: z.array(CommentSchema),
1201
1047
  footnotes: z.array(FootnoteSchema),
1202
1048
  headers: z.array(z.string()),
@@ -1216,7 +1062,7 @@ function readPageSize(sectPr) {
1216
1062
  const pgSz = childrenWithTag(sectPr, "w:pgSz")[0];
1217
1063
  const w = pgSz === void 0 ? void 0 : attr(pgSz, "w:w");
1218
1064
  const h = pgSz === void 0 ? void 0 : attr(pgSz, "w:h");
1219
- return w === void 0 || h === void 0 ? PAGE_SIZE_LETTER : {
1065
+ return w === void 0 || h === void 0 ? PAGE_SIZE_LETTER$1 : {
1220
1066
  widthPt: twipsToPt(Number(w)),
1221
1067
  heightPt: twipsToPt(Number(h))
1222
1068
  };
@@ -1332,7 +1178,7 @@ function readParagraph$1(paragraph, context, rels) {
1332
1178
  function readCellShading(tcPr) {
1333
1179
  const shd = tcPr === void 0 ? void 0 : childrenWithTag(tcPr, "w:shd")[0];
1334
1180
  const fill = shd === void 0 ? void 0 : attr(shd, "w:fill");
1335
- return fill === void 0 || fill === "auto" || fill === "none" ? void 0 : rgbHexToColor(fill);
1181
+ return fill === void 0 || fill === "auto" || fill === "none" ? void 0 : rgbHexToColor$1(fill);
1336
1182
  }
1337
1183
  function readRawCell(tc, context, rels) {
1338
1184
  const tcPr = childrenWithTag(tc, "w:tcPr")[0];
@@ -1432,10 +1278,11 @@ function readSections(body, context, rels) {
1432
1278
  currentBlocks.push(...readBodyBlocks([node], context, rels));
1433
1279
  }
1434
1280
  if (currentBlocks.length > 0 || sections.length === 0) sections.push({
1435
- pageSize: PAGE_SIZE_LETTER,
1281
+ pageSize: PAGE_SIZE_LETTER$1,
1436
1282
  margins: DEFAULT_MARGINS,
1437
1283
  blocks: currentBlocks
1438
1284
  });
1285
+ sections.forEach((section, sectionIndex) => assignSourcePaths(section.blocks, `sections[${sectionIndex}]`));
1439
1286
  return sections;
1440
1287
  }
1441
1288
  function readDocumentTheme(pkg, docRels) {
@@ -1592,7 +1439,7 @@ function resolveDefaultRunProperties(placeholderType, level, context) {
1592
1439
  //#region src/typed/pptx/read.ts
1593
1440
  const PptxDocumentSchema = z.object({
1594
1441
  metadata: DocumentMetadataSchema,
1595
- slides: z.array(ContentSlideSchema)
1442
+ slides: z.array(ContentSlideSchema$1)
1596
1443
  });
1597
1444
  const PRESENTATION_PATH = "ppt/presentation.xml";
1598
1445
  const TABLE_GRAPHIC_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
@@ -1600,7 +1447,7 @@ function readSlideSize(presentationRoot) {
1600
1447
  const sldSz = presentationRoot === void 0 ? void 0 : childrenWithTag(presentationRoot, "p:sldSz")[0];
1601
1448
  const cx = sldSz === void 0 ? void 0 : attr(sldSz, "cx");
1602
1449
  const cy = sldSz === void 0 ? void 0 : attr(sldSz, "cy");
1603
- return cx === void 0 || cy === void 0 ? SLIDE_SIZE_WIDESCREEN : {
1450
+ return cx === void 0 || cy === void 0 ? SLIDE_SIZE_WIDESCREEN$1 : {
1604
1451
  widthPt: emuToPt(Number(cx)),
1605
1452
  heightPt: emuToPt(Number(cy))
1606
1453
  };
@@ -1927,6 +1774,13 @@ function readPptx(pkg) {
1927
1774
  const presentationRoot = rootElement(pkg.parts[PRESENTATION_PATH]);
1928
1775
  const size = readSlideSize(presentationRoot);
1929
1776
  const slides = readSlidePathsInOrder(pkg, presentationRoot).map((slidePath) => readSlide(pkg, slidePath, size));
1777
+ slides.forEach((slide, slideIndex) => {
1778
+ slide.shapes.forEach((shape, shapeIndex) => {
1779
+ const shapePath = `slides[${slideIndex}].shapes[${shapeIndex}]`;
1780
+ shape.sourcePath = shapePath;
1781
+ assignSourcePaths(shape.blocks, shapePath);
1782
+ });
1783
+ });
1930
1784
  return {
1931
1785
  metadata: readCoreProperties(pkg),
1932
1786
  slides
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ooxml.js",
3
- "version": "2.0.3",
3
+ "version": "2.1.0",
4
4
  "description": "Type-safe, lossless round-trip conversion between OOXML packages (docx, pptx, xlsx) and JSON, built on Zod 4 codecs.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -61,6 +61,7 @@
61
61
  "license": "MIT",
62
62
  "packageManager": "pnpm@11.6.0",
63
63
  "dependencies": {
64
+ "document-content-model": "^1.1.0",
64
65
  "fast-xml-parser": "^5.10.1",
65
66
  "fflate": "^0.8.3",
66
67
  "zod": "^4.4.3"