ooxml.js 2.0.2 → 2.0.4

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;
@@ -1078,7 +908,7 @@ function readUnderline(u) {
1078
908
  function readRunColor(colorEl) {
1079
909
  if (colorEl === void 0) return;
1080
910
  const val = attr(colorEl, "w:val");
1081
- return val === void 0 || val === "auto" ? void 0 : rgbHexToColor(val);
911
+ return val === void 0 || val === "auto" ? void 0 : rgbHexToColor$1(val);
1082
912
  }
1083
913
  function readRunFontFamily(rFonts, theme) {
1084
914
  if (rFonts === void 0) return;
@@ -1196,7 +1026,7 @@ const FootnoteSchema = z.object({
1196
1026
  });
1197
1027
  const DocxDocumentSchema = z.object({
1198
1028
  metadata: DocumentMetadataSchema,
1199
- sections: z.array(ContentSectionSchema),
1029
+ sections: z.array(ContentSectionSchema$1),
1200
1030
  comments: z.array(CommentSchema),
1201
1031
  footnotes: z.array(FootnoteSchema),
1202
1032
  headers: z.array(z.string()),
@@ -1216,7 +1046,7 @@ function readPageSize(sectPr) {
1216
1046
  const pgSz = childrenWithTag(sectPr, "w:pgSz")[0];
1217
1047
  const w = pgSz === void 0 ? void 0 : attr(pgSz, "w:w");
1218
1048
  const h = pgSz === void 0 ? void 0 : attr(pgSz, "w:h");
1219
- return w === void 0 || h === void 0 ? PAGE_SIZE_LETTER : {
1049
+ return w === void 0 || h === void 0 ? PAGE_SIZE_LETTER$1 : {
1220
1050
  widthPt: twipsToPt(Number(w)),
1221
1051
  heightPt: twipsToPt(Number(h))
1222
1052
  };
@@ -1332,7 +1162,7 @@ function readParagraph$1(paragraph, context, rels) {
1332
1162
  function readCellShading(tcPr) {
1333
1163
  const shd = tcPr === void 0 ? void 0 : childrenWithTag(tcPr, "w:shd")[0];
1334
1164
  const fill = shd === void 0 ? void 0 : attr(shd, "w:fill");
1335
- return fill === void 0 || fill === "auto" || fill === "none" ? void 0 : rgbHexToColor(fill);
1165
+ return fill === void 0 || fill === "auto" || fill === "none" ? void 0 : rgbHexToColor$1(fill);
1336
1166
  }
1337
1167
  function readRawCell(tc, context, rels) {
1338
1168
  const tcPr = childrenWithTag(tc, "w:tcPr")[0];
@@ -1432,7 +1262,7 @@ function readSections(body, context, rels) {
1432
1262
  currentBlocks.push(...readBodyBlocks([node], context, rels));
1433
1263
  }
1434
1264
  if (currentBlocks.length > 0 || sections.length === 0) sections.push({
1435
- pageSize: PAGE_SIZE_LETTER,
1265
+ pageSize: PAGE_SIZE_LETTER$1,
1436
1266
  margins: DEFAULT_MARGINS,
1437
1267
  blocks: currentBlocks
1438
1268
  });
@@ -1592,7 +1422,7 @@ function resolveDefaultRunProperties(placeholderType, level, context) {
1592
1422
  //#region src/typed/pptx/read.ts
1593
1423
  const PptxDocumentSchema = z.object({
1594
1424
  metadata: DocumentMetadataSchema,
1595
- slides: z.array(ContentSlideSchema)
1425
+ slides: z.array(ContentSlideSchema$1)
1596
1426
  });
1597
1427
  const PRESENTATION_PATH = "ppt/presentation.xml";
1598
1428
  const TABLE_GRAPHIC_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
@@ -1600,7 +1430,7 @@ function readSlideSize(presentationRoot) {
1600
1430
  const sldSz = presentationRoot === void 0 ? void 0 : childrenWithTag(presentationRoot, "p:sldSz")[0];
1601
1431
  const cx = sldSz === void 0 ? void 0 : attr(sldSz, "cx");
1602
1432
  const cy = sldSz === void 0 ? void 0 : attr(sldSz, "cy");
1603
- return cx === void 0 || cy === void 0 ? SLIDE_SIZE_WIDESCREEN : {
1433
+ return cx === void 0 || cy === void 0 ? SLIDE_SIZE_WIDESCREEN$1 : {
1604
1434
  widthPt: emuToPt(Number(cx)),
1605
1435
  heightPt: emuToPt(Number(cy))
1606
1436
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ooxml.js",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
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": {
@@ -43,6 +43,7 @@
43
43
  "typecheck": "tsc --noEmit",
44
44
  "test": "vitest run --project unit",
45
45
  "test:watch": "vitest --project unit",
46
+ "test:coverage": "vitest run --project unit --coverage",
46
47
  "test:smoke": "tsdown && vitest run --project smoke",
47
48
  "prepare": "husky",
48
49
  "release": "semantic-release"
@@ -60,6 +61,7 @@
60
61
  "license": "MIT",
61
62
  "packageManager": "pnpm@11.6.0",
62
63
  "dependencies": {
64
+ "document-content-model": "^1.0.0",
63
65
  "fast-xml-parser": "^5.10.1",
64
66
  "fflate": "^0.8.3",
65
67
  "zod": "^4.4.3"
@@ -72,6 +74,7 @@
72
74
  "@semantic-release/changelog": "^7.0.0",
73
75
  "@semantic-release/git": "^11.0.1",
74
76
  "@types/node": "^26.1.1",
77
+ "@vitest/coverage-v8": "^4.1.10",
75
78
  "eslint": "^10.8.0",
76
79
  "globals": "^17.8.0",
77
80
  "husky": "^9.1.7",