@office-open/core 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Demo Macro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # @office-open/core
2
+
3
+ ![npm version](https://img.shields.io/npm/v/@office-open/core)
4
+ ![npm downloads](https://img.shields.io/npm/dw/@office-open/core)
5
+ ![npm license](https://img.shields.io/npm/l/@office-open/core)
6
+
7
+ > Shared OOXML infrastructure: XmlComponent, value validators, unit converters, chart types, and SmartArt definitions.
8
+
9
+ ## Features
10
+
11
+ - **XmlComponent Framework** - Base classes for building OOXML element trees with dynamic namespace support
12
+ - **Value Validators** - Runtime validation for OOXML spec types (ST_HexColor, ST_OnOff, ST_DecimalNumber, etc.)
13
+ - **Unit Converters** - TWIP and EMU conversions (mm/in/pt/px)
14
+ - **Chart Components** - Shared chart types (bar, line, pie, area, scatter) and chart collection for DOCX/PPTX
15
+ - **SmartArt Components** - Data model, tree-to-model converter, collection, and built-in layout/style/color definitions
16
+ - **ID Generators** - Sequential numeric IDs, nanoid, SHA-1 hash, UUID v4
17
+ - **OOXML Compliance** - All types verified against ISO/IEC 29500-4 XSD schemas
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ # Install with npm
23
+ $ npm install @office-open/core
24
+
25
+ # Install with pnpm
26
+ $ pnpm add @office-open/core
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import {
33
+ OnOffElement,
34
+ StringValueElement,
35
+ BuilderElement,
36
+ StringContainer,
37
+ hexColorValue,
38
+ decimalNumber,
39
+ convertMillimetersToTwip,
40
+ uniqueNumericIdCreator,
41
+ } from "@office-open/core";
42
+
43
+ // CT_OnOff — dynamic namespace prefix per XSD spec
44
+ new OnOffElement("w:b", false);
45
+ // → { "w:b": { _attr: { "w:val": false } } }
46
+
47
+ new OnOffElement("m:hideBot", false);
48
+ // → { "m:hideBot": { _attr: { "m:val": false } } }
49
+
50
+ // Builder with attributes + children
51
+ new BuilderElement({
52
+ name: "w:r",
53
+ attributes: { lang: { key: "xml:lang", value: "en-US" } },
54
+ children: [new StringContainer("w:t", "Hello")],
55
+ });
56
+
57
+ // Value validators
58
+ hexColorValue("#FF0000"); // → "FF0000"
59
+ hexColorValue("auto"); // → "auto"
60
+ decimalNumber(10.7); // → 10
61
+
62
+ // Unit converters
63
+ convertMillimetersToTwip(25.4); // → 1440 (1 inch)
64
+ convertPixelsToEmu(100); // → 952500
65
+ convertInchesToEmu(1); // → 914400
66
+ convertPointsToEmu(12); // → 152400
67
+
68
+ // ID generators
69
+ const gen = uniqueNumericIdCreator();
70
+ gen(); // → 1, 2, 3, ...
71
+ ```
72
+
73
+ ## OOXML Schema Compliance
74
+
75
+ | Type | XSD Reference |
76
+ | -------------------- | ---------------------------------------------- |
77
+ | `ThemeColor` | `ST_ThemeColor` (17 values) |
78
+ | `ThemeFont` | `ST_Theme` (8 values) |
79
+ | `UniversalMeasure` | `ST_UniversalMeasure` (mm, cm, in, pt, pc, pi) |
80
+ | `Percentage` | `ST_Percentage` |
81
+ | `hexColorValue` | `ST_HexColor` (auto + 3-byte hexBinary) |
82
+ | `OnOffElement` | `CT_OnOff` (dynamic namespace prefix) |
83
+ | `HpsMeasureElement` | `CT_HpsMeasure` |
84
+ | `StringValueElement` | `CT_String` |
85
+
86
+ ## Exports
87
+
88
+ | Path | Contents |
89
+ | ---------------------------- | --------------------------------------------------- |
90
+ | `@office-open/core` | XmlComponent, validators, converters, ID generators |
91
+ | `@office-open/core/values` | Validators + ThemeColor/ThemeFont only |
92
+ | `@office-open/core/chart` | Chart types, series data, chart collection, title |
93
+ | `@office-open/core/smartart` | SmartArt data model, tree-to-model, definitions |
94
+
95
+ ## Benchmark
96
+
97
+ | Operation | hz |
98
+ | ----------------------------- | ----- |
99
+ | `decimalNumber` | ~21M |
100
+ | `hexColorValue` (6-char hex) | ~13M |
101
+ | `uniqueNumericIdCreator` | ~21M |
102
+ | `uniqueId` (nanoid) | ~3.3M |
103
+ | `uniqueUuid` | ~2M |
104
+ | `OnOffElement (true)` | ~13M |
105
+ | `OnOffElement (false)` | ~1.7M |
106
+ | `BuilderElement` (attributes) | ~2M |
107
+ | `BuilderElement` (children) | ~2.3M |
108
+
109
+ ## License
110
+
111
+ - [MIT](LICENSE) © [Demo Macro](https://imst.xyz/)
@@ -0,0 +1,72 @@
1
+ import hash from "hash.js";
2
+ import { customAlphabet, nanoid } from "nanoid/non-secure";
3
+ //#region src/converters.ts
4
+ /**
5
+ * OOXML unit conversion utilities.
6
+ *
7
+ * @module
8
+ */
9
+ /**
10
+ * Converts millimeters to TWIP (twentieths of a point).
11
+ */
12
+ const convertMillimetersToTwip = (millimeters) => Math.floor(millimeters / 25.4 * 72 * 20);
13
+ /**
14
+ * Converts inches to TWIP (twentieths of a point).
15
+ */
16
+ const convertInchesToTwip = (inches) => Math.floor(inches * 72 * 20);
17
+ /**
18
+ * Converts pixels to EMU (96 DPI).
19
+ */
20
+ const convertPixelsToEmu = (pixels) => Math.round(pixels * 9525);
21
+ /**
22
+ * Converts EMU to pixels (96 DPI).
23
+ */
24
+ const convertEmuToPixels = (emus) => Math.round(emus / 9525);
25
+ /**
26
+ * Converts inches to EMU.
27
+ */
28
+ const convertInchesToEmu = (inches) => Math.round(inches * 914400);
29
+ /**
30
+ * Converts EMU to inches.
31
+ */
32
+ const convertEmuToInches = (emus) => emus / 914400;
33
+ /**
34
+ * Converts points to EMU.
35
+ */
36
+ const convertPointsToEmu = (points) => Math.round(points * 12700);
37
+ /**
38
+ * Converts EMU to points.
39
+ */
40
+ const convertEmuToPoints = (emus) => emus / 12700;
41
+ //#endregion
42
+ //#region src/id-generators.ts
43
+ /**
44
+ * Unique ID generation utilities.
45
+ *
46
+ * @module
47
+ */
48
+ /**
49
+ * Creates a unique numeric ID generator with sequential numbering.
50
+ */
51
+ const uniqueNumericIdCreator = (initial = 0) => {
52
+ let currentCount = initial;
53
+ return () => ++currentCount;
54
+ };
55
+ /**
56
+ * Generates a unique lowercase alphanumeric ID using nanoid.
57
+ */
58
+ const uniqueId = () => nanoid().toLowerCase();
59
+ /**
60
+ * Generates a SHA-1 hash of the provided data.
61
+ */
62
+ const hashedId = (data) => hash.sha1().update(data instanceof ArrayBuffer ? new Uint8Array(data) : data).digest("hex");
63
+ /**
64
+ * Generates a random hexadecimal string of specified length.
65
+ */
66
+ const generateUuidPart = (count) => customAlphabet("1234567890abcdef", count)();
67
+ /**
68
+ * Generates a UUID v4-style unique identifier.
69
+ */
70
+ const uniqueUuid = () => `${generateUuidPart(8)}-${generateUuidPart(4)}-${generateUuidPart(4)}-${generateUuidPart(4)}-${generateUuidPart(12)}`;
71
+ //#endregion
72
+ export { convertEmuToInches as a, convertInchesToEmu as c, convertPixelsToEmu as d, convertPointsToEmu as f, uniqueUuid as i, convertInchesToTwip as l, uniqueId as n, convertEmuToPixels as o, uniqueNumericIdCreator as r, convertEmuToPoints as s, hashedId as t, convertMillimetersToTwip as u };
@@ -0,0 +1,241 @@
1
+ import { r as PositiveUniversalMeasure } from "./values-BrGywpRh.mjs";
2
+ import { Element } from "@office-open/xml";
3
+
4
+ //#region src/xml-components/types.d.ts
5
+ /**
6
+ * XML-serializable object types for OOXML document generation.
7
+ *
8
+ * @module
9
+ */
10
+ /**
11
+ * Attributes for an XML element.
12
+ */
13
+ type IXmlAttribute = Readonly<Record<string, string | number | boolean>>;
14
+ /**
15
+ * Object that can be serialized to XML.
16
+ */
17
+ type IXmlableObject = Readonly<Record<string, any>>;
18
+ //#endregion
19
+ //#region src/xml-components/base.d.ts
20
+ /**
21
+ * Context object passed through the XML tree during serialization.
22
+ *
23
+ * @typeParam TFileData - The type of the root file data object (format-specific)
24
+ */
25
+ interface IContext<TFileData = unknown> {
26
+ /** The root file data object being serialized (format-specific). */
27
+ readonly fileData?: TFileData;
28
+ /** Current traversal stack of components (mutable for performance). */
29
+ readonly stack: IXmlableObject[];
30
+ }
31
+ /**
32
+ * Abstract base class for all XML components.
33
+ */
34
+ declare abstract class BaseXmlComponent {
35
+ /** The XML element name for this component (e.g., "w:p" for paragraph). */
36
+ protected readonly rootKey: string;
37
+ constructor(rootKey: string);
38
+ /**
39
+ * Prepares this component for XML serialization.
40
+ *
41
+ * @param context - The serialization context
42
+ * @returns The XML-serializable object, or undefined to exclude from output
43
+ */
44
+ abstract prepForXml(context: IContext): IXmlableObject | undefined;
45
+ }
46
+ //#endregion
47
+ //#region src/xml-components/component.d.ts
48
+ /**
49
+ * Empty object singleton used for empty XML elements.
50
+ *
51
+ * @internal
52
+ */
53
+ declare const EMPTY_OBJECT: {};
54
+ /**
55
+ * Base class for all XML components in OOXML documents.
56
+ */
57
+ declare abstract class XmlComponent extends BaseXmlComponent {
58
+ /**
59
+ * Array of child components, text nodes, and attributes.
60
+ */
61
+ root: (BaseXmlComponent | IXmlableObject | string)[];
62
+ constructor(rootKey: string);
63
+ /**
64
+ * Prepares this component and its children for XML serialization.
65
+ */
66
+ prepForXml(context: IContext): IXmlableObject | undefined;
67
+ /**
68
+ * @deprecated Internal use only.
69
+ */
70
+ addChildElement(child: BaseXmlComponent | string): XmlComponent;
71
+ }
72
+ /**
73
+ * XML component that is excluded from output if it has no meaningful content.
74
+ */
75
+ declare abstract class IgnoreIfEmptyXmlComponent extends XmlComponent {
76
+ private readonly includeIfEmpty;
77
+ constructor(rootKey: string, includeIfEmpty?: boolean);
78
+ prepForXml(context: IContext): IXmlableObject | undefined;
79
+ }
80
+ //#endregion
81
+ //#region src/xml-components/attributes.d.ts
82
+ /**
83
+ * Maps TypeScript property names to their XML attribute names.
84
+ */
85
+ type AttributeMap<T> = Record<keyof T, string>;
86
+ /**
87
+ * Simple attribute data as a key-value record.
88
+ */
89
+ type AttributeData = Record<string, boolean | number | string>;
90
+ /**
91
+ * Structured attribute payload with explicit key-value mapping.
92
+ */
93
+ type AttributePayload<T> = { readonly [P in keyof T]: {
94
+ readonly key: string;
95
+ readonly value: T[P];
96
+ } };
97
+ /**
98
+ * Base class for creating XML attributes with automatic name mapping.
99
+ */
100
+ declare abstract class XmlAttributeComponent<T extends Record<string, any>> extends BaseXmlComponent {
101
+ private readonly root;
102
+ /** Optional mapping from property names to XML attribute names. */
103
+ protected readonly xmlKeys?: AttributeMap<T>;
104
+ constructor(root: T);
105
+ prepForXml(_: IContext): IXmlableObject;
106
+ }
107
+ /**
108
+ * Next-generation attribute component with explicit key-value pairs.
109
+ */
110
+ declare class NextAttributeComponent<T> extends BaseXmlComponent {
111
+ private readonly root;
112
+ constructor(root: AttributePayload<T>);
113
+ prepForXml(_: IContext): IXmlableObject;
114
+ }
115
+ //#endregion
116
+ //#region src/xml-components/elements.d.ts
117
+ /**
118
+ * Build a CT_OnOff XML object without allocating any XmlComponent.
119
+ * `val=true` returns a frozen singleton (cached per name).
120
+ */
121
+ declare function onOffObj(name: string, val?: boolean | undefined): IXmlableObject;
122
+ /**
123
+ * Build a CT_HpsMeasure XML object (half-point size) without allocation.
124
+ */
125
+ declare function hpsMeasureObj(name: string, val: number | PositiveUniversalMeasure): IXmlableObject;
126
+ /**
127
+ * Build a CT_String XML object (string value attribute) without allocation.
128
+ */
129
+ declare function stringValObj(name: string, val: string): IXmlableObject;
130
+ /**
131
+ * Build a numeric value attribute XML object without allocation.
132
+ */
133
+ declare function numberValObj(name: string, val: number): IXmlableObject;
134
+ /**
135
+ * Build a string enum value attribute XML object without allocation.
136
+ */
137
+ declare function stringEnumValObj<T extends string>(name: string, val: T): IXmlableObject;
138
+ /**
139
+ * Build an element wrapping a text string without allocation.
140
+ */
141
+ declare function stringContainerObj(name: string, val: string): IXmlableObject;
142
+ /**
143
+ * XML element representing a boolean on/off value (CT_OnOff).
144
+ * @deprecated Use `onOffObj()` for hot-path code.
145
+ */
146
+ declare class OnOffElement extends XmlComponent {
147
+ constructor(name: string, val?: boolean | undefined);
148
+ }
149
+ /**
150
+ * XML element representing a half-point size measurement (CT_HpsMeasure).
151
+ * @deprecated Use `hpsMeasureObj()` for hot-path code.
152
+ */
153
+ declare class HpsMeasureElement extends XmlComponent {
154
+ constructor(name: string, val: number | PositiveUniversalMeasure);
155
+ }
156
+ /**
157
+ * XML element representing an empty element (CT_Empty).
158
+ */
159
+ declare class EmptyElement extends XmlComponent {}
160
+ /**
161
+ * XML element with a string value attribute (CT_String).
162
+ * @deprecated Use `stringValObj()` for hot-path code.
163
+ */
164
+ declare class StringValueElement extends XmlComponent {
165
+ constructor(name: string, val: string);
166
+ }
167
+ /**
168
+ * XML element with a numeric value attribute.
169
+ * @deprecated Use `numberValObj()` for hot-path code.
170
+ */
171
+ declare class NumberValueElement extends XmlComponent {
172
+ constructor(name: string, val: number);
173
+ }
174
+ /**
175
+ * XML element with a string enum value attribute.
176
+ * @deprecated Use `stringEnumValObj()` for hot-path code.
177
+ */
178
+ declare class StringEnumValueElement<T extends string> extends XmlComponent {
179
+ constructor(name: string, val: T);
180
+ }
181
+ /**
182
+ * XML element containing text content.
183
+ * @deprecated Use `stringContainerObj()` for hot-path code.
184
+ */
185
+ declare class StringContainer extends XmlComponent {
186
+ constructor(name: string, val: string);
187
+ }
188
+ /**
189
+ * Flexible XML element builder with explicit attribute and child configuration.
190
+ */
191
+ declare class BuilderElement<T = {}> extends XmlComponent {
192
+ constructor({
193
+ name,
194
+ attributes,
195
+ children
196
+ }: {
197
+ readonly name: string;
198
+ readonly attributes?: AttributePayload<T>;
199
+ readonly children?: readonly (BaseXmlComponent | string)[];
200
+ });
201
+ }
202
+ /**
203
+ * Creates a NextAttributeComponent with explicit XML attribute keys.
204
+ */
205
+ declare const chartAttr: (attrs: Record<string, string | number | boolean>) => BaseXmlComponent;
206
+ /**
207
+ * Wraps a component in a named XmlComponent element.
208
+ */
209
+ declare function wrapEl(elementName: string, child: BaseXmlComponent): XmlComponent;
210
+ //#endregion
211
+ //#region src/xml-components/imported.d.ts
212
+ /**
213
+ * Converts an xml-js Element into an XmlComponent tree.
214
+ */
215
+ declare const convertToXmlComponent: (element: Element) => ImportedXmlComponent | string | undefined;
216
+ /**
217
+ * XML component representing imported XML content.
218
+ */
219
+ declare class ImportedXmlComponent extends XmlComponent {
220
+ static fromXmlString(importedContent: string): ImportedXmlComponent;
221
+ constructor(rootKey: string, _attr?: any);
222
+ push(xmlComponent: XmlComponent | string): void;
223
+ }
224
+ /**
225
+ * Represents attributes for imported root elements.
226
+ */
227
+ declare class ImportedRootElementAttributes extends XmlComponent {
228
+ private readonly _attr;
229
+ constructor(_attr: any);
230
+ prepForXml(_: IContext): IXmlableObject;
231
+ }
232
+ //#endregion
233
+ //#region src/xml-components/initializable.d.ts
234
+ /**
235
+ * XML component that can be initialized from another component.
236
+ */
237
+ declare abstract class InitializableXmlComponent extends XmlComponent {
238
+ constructor(rootKey: string, initComponent?: XmlComponent);
239
+ }
240
+ //#endregion
241
+ export { IContext as A, AttributePayload as C, IgnoreIfEmptyXmlComponent as D, EMPTY_OBJECT as E, IXmlableObject as M, XmlComponent as O, AttributeMap as S, XmlAttributeComponent as T, stringContainerObj as _, BuilderElement as a, wrapEl as b, NumberValueElement as c, StringEnumValueElement as d, StringValueElement as f, onOffObj as g, numberValObj as h, convertToXmlComponent as i, IXmlAttribute as j, BaseXmlComponent as k, OnOffElement as l, hpsMeasureObj as m, ImportedRootElementAttributes as n, EmptyElement as o, chartAttr as p, ImportedXmlComponent as r, HpsMeasureElement as s, InitializableXmlComponent as t, StringContainer as u, stringEnumValObj as v, NextAttributeComponent as w, AttributeData as x, stringValObj as y };