import { Component } from 'svelte'; import { LanguageModel, ChatTransport, UIMessage, ToolSet } from 'ai'; import { Options } from 'html2canvas-pro'; //#region src/core/types/actions.d.ts /** * Action types: hyperlinks, slide jumps, macros, and action buttons. * * @module pptx-types/actions */ /** * A parsed shape-level action from `a:hlinkClick` or `a:hlinkHover`. * * @example * ```ts * const link: PptxAction = { * url: "https://example.com", * tooltip: "Visit Example", * highlightClick: true, * }; * * const slideJump: PptxAction = { * action: "ppaction://hlinksldjump", * targetSlideIndex: 3, * }; * // => satisfies PptxAction * ``` */ interface PptxAction { /** Relationship ID referencing the action target. */ rId?: string; /** OOXML action string (e.g. `ppaction://hlinksldjump`). */ action?: string; /** Tooltip text shown on hover. */ tooltip?: string; /** Whether the shape should highlight on click. */ highlightClick?: boolean; /** Resolved URL or file path from the slide relationship map. */ url?: string; /** Zero-based index into the slides array for internal slide jumps. */ targetSlideIndex?: number; /** Relationship ID of an optional click sound (`a:snd/@r:embed`). */ soundRId?: string; /** Resolved media target path for the optional click sound. */ soundPath?: string; } //#endregion //#region src/core/types/common.d.ts /** * Shared value types used across the entire PPTX editor type system. * * Contains primitive enums, small interfaces, and the XML object alias * that almost every other type file imports. * * @module pptx-types/common */ /** * Underline style tokens from OOXML `a:rPr/@u`. * * These map directly to the OpenXML `ST_TextUnderlineType` simple type. * * @example * ```ts * const style: UnderlineStyle = "wavy"; * // => "wavy" — one of: sng | dbl | heavy | dotted | dash | wavy | none | ... * ``` */ type UnderlineStyle = 'sng' | 'dbl' | 'heavy' | 'dotted' | 'dottedHeavy' | 'dash' | 'dashHeavy' | 'dashLong' | 'dashLongHeavy' | 'dotDash' | 'dotDashHeavy' | 'dotDotDash' | 'dotDotDashHeavy' | 'wavy' | 'wavyHeavy' | 'wavyDbl' | 'none'; /** * Connector connection point reference — links a connector endpoint to a * specific shape on the slide. * * When both `shapeId` and `connectionSiteIndex` are set, the connector * end snaps to that shapes’s connection site and “follows” the shape when * it is moved. * * @example * ```ts * const start: ConnectorConnectionPoint = { * shapeId: "shape_1", * connectionSiteIndex: 2, * }; * // => { shapeId: "shape_1", connectionSiteIndex: 2 } satisfies ConnectorConnectionPoint * ``` */ interface ConnectorConnectionPoint { /** ID of the shape this connector endpoint is attached to. */ shapeId?: string; /** Connection site index on the target shape (0-based). */ connectionSiteIndex?: number; } /** * Arrow head types for connector start/end. * * Maps to `a:headEnd/@type` and `a:tailEnd/@type` in OOXML. * * @example * ```ts * const arrow: ConnectorArrowType = "triangle"; * // => "triangle" — one of: none | triangle | stealth | diamond | oval | arrow * ``` */ type ConnectorArrowType = 'none' | 'triangle' | 'stealth' | 'diamond' | 'oval' | 'arrow'; /** * Stroke dash pattern types for lines and shape outlines. * * Maps to `a:ln/a:prstDash/@val` in OOXML. Use `"custom"` for * user-defined dash/space arrays. * * @example * ```ts * const dash: StrokeDashType = "dashDot"; * // => "dashDot" — one of: solid | dot | dash | lgDash | dashDot | custom | ... * ``` */ type StrokeDashType = 'solid' | 'dot' | 'dash' | 'lgDash' | 'dashDot' | 'lgDashDot' | 'lgDashDotDot' | 'sysDot' | 'sysDash' | 'sysDashDot' | 'sysDashDotDot' | 'custom'; /** * Shadow effect properties for a single shadow layer. * * Represents parsed values from an `` node. Multiple instances * can be stored in {@link ShapeStyle.shadows} for compound shadow effects. * * @example * ```ts * const shadow: ShadowEffect = { * color: "#000000", * opacity: 0.4, * blur: 6, * angle: 315, * distance: 4, * }; * // => { color: "#000000", opacity: 0.4, blur: 6, angle: 315, distance: 4 } satisfies ShadowEffect * ``` */ interface ShadowEffect { /** Shadow color as hex string. */ color: string; /** Shadow opacity (0-1). */ opacity: number; /** Blur radius in pixels. */ blur: number; /** Shadow angle in degrees (0-360). */ angle: number; /** Shadow distance in pixels. */ distance: number; /** Whether shadow rotates with shape. */ rotateWithShape?: boolean; } /** * Strongly-typed parsed XML node from fast-xml-parser. * * The parser is configured with `attributeNamePrefix: '@_'`, * `parseAttributeValue: false`, and `parseTagValue: false`, so attribute and * text values are always strings at runtime. This type encodes that: * * - **Attributes** — keys matching `` `@_${string}` `` return * `string | undefined` directly. * - **Text content** — `#text` returns `string | undefined`. * - **Child elements** — any other string key returns * `XmlObject | XmlObject[] | string | undefined`. The union reflects that * fast-xml-parser may emit an object (single child), an array (repeated * children), or a bare string (text-only element collapsed by the parser). * * For traversal, prefer the helpers in {@link ./../utils/xml-access} — * `xmlChild` / `xmlChildren` / `xmlAttr` / `xmlText` / `xmlPath` — which * narrow the union and normalize the single-vs-array duality. Direct * indexing works for attributes (typed as string) but chained child access * (`obj['p:spPr']?.['a:xfrm']`) requires the helpers or a narrowing cast * because TypeScript cannot index into the `XmlObject[] | string` part of * the union. */ interface XmlObject { /** Attributes (`@_`-prefixed keys) are always strings at runtime. */ [attr: `@_${string}`]: string | undefined; /** Element text content surfaces under `#text` when present. */ '#text'?: string; /** * Child elements keyed by their (namespaced) tag name. fast-xml-parser * emits a single object for unique elements, an array for repeated ones, * and a bare string for elements collapsed to their text content. Use * the helpers in `utils/xml-access` to narrow this union. */ [child: string]: XmlObject | XmlObject[] | string | undefined; } /** * Lock attributes from an element's non-visual properties node. * * When a flag is `true` the corresponding user interaction is disabled * in the editor (e.g. `noRotation` prevents free rotation of the shape). * * One bag covers every family, but the families are NOT interchangeable in * the file: `a:spLocks` (`CT_ShapeLocking`), `a:picLocks`, `a:cxnSpLocks`, * `a:grpSpLocks` (`CT_GroupLocking`) and `a:graphicFrameLocks` * (`CT_GraphicalObjectFrameLocking`) each declare their own attribute subset. * `runtime/shape-lock-containers` holds that table and is what decides which * of these fields may be written for a given element. * * @example * ```ts * const locks: PptxShapeLocks = { noMove: true, noResize: true }; * // => { noMove: true, noResize: true } satisfies PptxShapeLocks * ``` */ interface PptxShapeLocks { noGrouping?: boolean; noRotation?: boolean; noMove?: boolean; noResize?: boolean; noTextEdit?: boolean; noSelect?: boolean; noChangeAspect?: boolean; noEditPoints?: boolean; noAdjustHandles?: boolean; noChangeArrowheads?: boolean; noChangeShapeType?: boolean; /** * `a:graphicFrameLocks/@noDrilldown`: forbids selecting the individual * parts inside a graphic frame (a chart series, a SmartArt node). Declared * ONLY by `CT_GraphicalObjectFrameLocking`, so it is written for tables, * charts, SmartArt, OLE objects and graphic-frame media, and never onto * `a:spLocks` / `a:picLocks` / `a:cxnSpLocks` / `a:grpSpLocks`. */ noDrilldown?: boolean; /** * Text-box flag from `p:cNvSpPr/@txBox`. Not a lock in the strict sense, * but it lives on the same non-visual-properties node as `a:spLocks`, so * it is captured here to round-trip through the model. When `true` the * shape is a plain text box (no fill/line by default). */ txBox?: boolean; } /** * A drawing guide parsed from OOXML extension lists. * * Slide-level and presentation-level guides are shown as thin coloured * lines that help users align elements. * * @example * ```ts * const guide: PptxDrawingGuide = { * id: "g1", * orientation: "horz", * positionEmu: 457200, * color: "#FF0000", * }; * // => { id: "g1", orientation: "horz", positionEmu: 457200, color: "#FF0000" } satisfies PptxDrawingGuide * ``` */ interface PptxDrawingGuide { /** Unique identifier (from `@_id` attribute or generated). */ id: string; /** Orientation: horizontal or vertical. */ orientation: 'horz' | 'vert'; /** Position in EMU (converted from pos attribute). */ positionEmu: number; /** Optional guide colour as hex string (e.g. "#FF0000"). */ color?: string; } //#endregion //#region src/core/types/geometry.d.ts /** * Geometry types: adjustment handles, custom geometry points, segments, * paths, and custom path properties. * * @module pptx-types/geometry */ /** * Defines an adjustment handle position for a shape geometry. * * Adjustment handles allow users to interactively reshape preset shapes * (e.g. rounding a rectangle corner or adjusting arrow head width). * * @example * ```ts * const handle: GeometryAdjustmentHandle = { * guideName: "adj", * xFraction: 0.25, * minValue: 0, * maxValue: 50000, * }; * // => satisfies GeometryAdjustmentHandle * ``` */ interface GeometryAdjustmentHandle { /** Name of the adjustment guide this handle controls (e.g. "adj", "adj1"). */ guideName: string; /** X position as a fraction of shape width (0..1), or undefined if the handle only moves vertically. */ xFraction?: number; /** Y position as a fraction of shape height (0..1), or undefined if the handle only moves horizontally. */ yFraction?: number; /** Minimum allowed value for the adjustment guide. */ minValue?: number; /** Maximum allowed value for the adjustment guide. */ maxValue?: number; } /** * A single point in a custom geometry path. * * @example * ```ts * const pt: CustomGeometryPoint = { x: 100, y: 200 }; * // => satisfies CustomGeometryPoint * ``` */ interface CustomGeometryPoint { x: number; y: number; } /** * A segment within a custom geometry path. * * Discriminated union over `type` — can be a moveTo, lineTo, * cubic Bézier, quadratic Bézier, or close command. * * @example * ```ts * const segments: CustomGeometrySegment[] = [ * { type: "moveTo", pt: { x: 0, y: 0 } }, * { type: "lineTo", pt: { x: 100, y: 0 } }, * { type: "lineTo", pt: { x: 100, y: 100 } }, * { type: "close" }, * ]; * // => satisfies CustomGeometrySegment[] * ``` */ type CustomGeometrySegment = { type: 'moveTo'; pt: CustomGeometryPoint; } | { type: 'lineTo'; pt: CustomGeometryPoint; } | { type: 'cubicBezTo'; pts: [CustomGeometryPoint, CustomGeometryPoint, CustomGeometryPoint]; } | { type: 'quadBezTo'; pts: [CustomGeometryPoint, CustomGeometryPoint]; } | { type: 'arcTo'; /** Horizontal radius of the ellipse. */ wR: number; /** Vertical radius of the ellipse. */ hR: number; /** Start angle in 60000ths of a degree. */ stAng: number; /** Sweep angle in 60000ths of a degree. */ swAng: number; } | { type: 'close'; }; /** * A single sub-path in a custom geometry definition (maps to one `a:path`). * * @example * ```ts * const path: CustomGeometryPath = { * width: 100, * height: 100, * segments: [ * { type: "moveTo", pt: { x: 0, y: 0 } }, * { type: "lineTo", pt: { x: 100, y: 100 } }, * ], * }; * // => satisfies CustomGeometryPath * ``` */ interface CustomGeometryPath { /** Coordinate-space width for this sub-path. */ width: number; /** Coordinate-space height for this sub-path. */ height: number; /** Ordered list of drawing segments. */ segments: CustomGeometrySegment[]; /** Path fill mode (`a:path/@fill`): norm, lighten, lightenLess, darken, darkenLess, none. */ fillMode?: 'norm' | 'lighten' | 'lightenLess' | 'darken' | 'darkenLess' | 'none'; /** Whether the path is stroked (`a:path/@stroke`). */ stroke?: boolean; /** 3D extrusion compatibility (`a:path/@extrusionOk`). */ extrusionOk?: boolean; } /** * Auxiliary raw XML preserved from `a:custGeom` for round-trip serialization. * These are stored opaquely so adjustment guides, handles, connection sites, * and the text rectangle are not lost when a custGeom is edited and saved. */ interface CustomGeometryRawData { /** Raw `a:avLst` XML content (adjustment value list). */ avLstXml?: unknown; /** Raw `a:gdLst` XML content (guide list). */ gdLstXml?: unknown; /** Raw `a:ahLst` XML content (adjustment handles). */ ahLstXml?: unknown; /** Raw `a:cxnLst` XML content (connection sites). */ cxnLstXml?: unknown; /** Raw `a:rect` XML content (text rectangle). */ rectXml?: unknown; } /** * XY-style adjustment handle (`a:ahXY`) on a custom geometry. * * Allows interactive editing of one or two guide values constrained to a * rectangular range. Coordinates are formula references (e.g. `"adj1"`, * `"w/2"`, `"0"`) preserved verbatim so they can re-emit unchanged. * * @example * ```ts * const handle: AdjustHandleXY = { * gdRefX: "adj1", * minX: "0", * maxX: "w", * posX: "adj1", * posY: "h/2", * }; * // => satisfies AdjustHandleXY * ``` */ interface AdjustHandleXY { /** Guide reference for the X axis (`@_gdRefX`). */ gdRefX?: string; /** Guide reference for the Y axis (`@_gdRefY`). */ gdRefY?: string; /** Minimum X value, as a formula reference (`@_minX`). */ minX?: string; /** Maximum X value (`@_maxX`). */ maxX?: string; /** Minimum Y value (`@_minY`). */ minY?: string; /** Maximum Y value (`@_maxY`). */ maxY?: string; /** Handle position X (formula or literal) from `a:pos/@_x`. */ posX?: string; /** Handle position Y from `a:pos/@_y`. */ posY?: string; } /** * Polar-style adjustment handle (`a:ahPolar`) on a custom geometry. * * Drives a guide via radial distance and angle rather than XY coordinates. * * @example * ```ts * const handle: AdjustHandlePolar = { * gdRefR: "adj1", * gdRefAng: "adj2", * posX: "wd2", * posY: "hd2", * }; * // => satisfies AdjustHandlePolar * ``` */ interface AdjustHandlePolar { /** Guide reference for the radial distance (`@_gdRefR`). */ gdRefR?: string; /** Guide reference for the angle (`@_gdRefAng`). */ gdRefAng?: string; /** Minimum radial value (`@_minR`). */ minR?: string; /** Maximum radial value (`@_maxR`). */ maxR?: string; /** Minimum angle (`@_minAng`). */ minAng?: string; /** Maximum angle (`@_maxAng`). */ maxAng?: string; /** Handle position X from `a:pos/@_x`. */ posX?: string; /** Handle position Y from `a:pos/@_y`. */ posY?: string; } /** * Connection site (`a:cxn`) on a custom geometry. * * Defines a point on a custom shape that connectors may snap to. * * @example * ```ts * const cxn: ConnectionSite = { ang: "0", posX: "0", posY: "hd2" }; * // => satisfies ConnectionSite * ``` */ interface ConnectionSite { /** Approach angle (`@_ang`) — formula or literal degree-1/60000 value. */ ang?: string; /** Site position X from `a:pos/@_x`. */ posX?: string; /** Site position Y from `a:pos/@_y`. */ posY?: string; } /** * Typed text rectangle (`a:rect`) on a custom geometry. * * Each edge is the formula or literal string preserved from the source XML * (`"l"`, `"t"`, `"r"`, `"b"`, or any guide name / formula). */ interface CustomGeometryTextRect { /** Left edge formula reference (`@_l`). */ l?: string; /** Top edge (`@_t`). */ t?: string; /** Right edge (`@_r`). */ r?: string; /** Bottom edge (`@_b`). */ b?: string; } /** * Custom (non-preset) geometry path — only on shapes and pictures. * * Contains SVG path data and/or structured custom geometry paths * parsed from `a:custGeom/a:pathLst`. * * @example * ```ts * const custom: PptxCustomPathProperties = { * pathData: "M 0 0 L 100 0 L 100 100 Z", * pathWidth: 100, * pathHeight: 100, * }; * // => satisfies PptxCustomPathProperties * ``` */ interface PptxCustomPathProperties { /** SVG path data for custom shapes. */ pathData?: string; /** Coordinate-space width for the custom path. */ pathWidth?: number; /** Coordinate-space height for the custom path. */ pathHeight?: number; /** Structured custom geometry paths for editing (maps to a:custGeom/a:pathLst). */ customGeometryPaths?: CustomGeometryPath[]; /** Raw adjustment/guide/handle/connection/text-rectangle XML preserved for serialization. */ customGeometryRawData?: CustomGeometryRawData; /** * Typed XY adjustment handles parsed from `a:custGeom/a:ahLst/a:ahXY`. * SDK-built shapes can populate this and the writer will emit `` entries * even when no raw XML was preserved. */ customGeometryAdjustHandlesXY?: AdjustHandleXY[]; /** * Typed polar adjustment handles parsed from `a:custGeom/a:ahLst/a:ahPolar`. */ customGeometryAdjustHandlesPolar?: AdjustHandlePolar[]; /** * Typed connection sites parsed from `a:custGeom/a:cxnLst/a:cxn`. */ customGeometryConnectionSites?: ConnectionSite[]; /** * Typed text rectangle parsed from `a:custGeom/a:rect`. When present this is * preferred over {@link customGeometryRawData}'s `rectXml` on save. */ customGeometryTextRect?: CustomGeometryTextRect; } //#endregion //#region src/core/types/effect-dag.d.ts type EffectDagBlendMode = 'darken' | 'lighten' | 'mult' | 'over' | 'screen'; type EffectDagContainerType = 'sib' | 'tree'; type EffectDagNode = EffectDagContainer | EffectDagBlend | EffectDagXfrm | EffectDagRelOff | EffectDagBlur | EffectDagAlphaOutset | EffectDagPresetShadow | EffectDagRawLeaf; interface EffectDagContainer { kind: 'cont'; type: EffectDagContainerType; name?: string; children: EffectDagNode[]; } interface EffectDagBlend { kind: 'blend'; mode: EffectDagBlendMode; container: EffectDagContainer; } interface EffectDagXfrm { kind: 'xfrmEffect'; sx?: number; sy?: number; kx?: number; ky?: number; tx?: number; ty?: number; } interface EffectDagRelOff { kind: 'relOff'; tx?: number; ty?: number; } /** Typed CT_BlurEffect with its original payload retained for lossless edits. */ interface EffectDagBlur { kind: 'blur'; radiusEmu?: number; grow?: boolean; xml: XmlObject; } /** Typed CT_AlphaOutsetEffect with original XML retained for lossless edits. */ interface EffectDagAlphaOutset { kind: 'alphaOutset'; radiusEmu?: number; xml: XmlObject; } /** Typed CT_PresetShadowEffect with colour and extension XML retained verbatim. */ interface EffectDagPresetShadow { kind: 'prstShdw'; preset?: `shdw${number}`; distanceEmu?: number; direction?: number; xml: XmlObject; } interface EffectDagRawLeaf { kind: 'raw'; tag: string; xml: Record; } //#endregion //#region src/core/types/three-d.d.ts /** * 3-D effect properties, text warp (WordArt) presets, and scene/shape bevel * definitions parsed from OOXML `a:sp3d`, `a:scene3d`, and `a:bodyPr/a:prstTxWarp`. * * @module pptx-types/three-d */ /** * Bevel preset type tokens from OOXML `a:bevelT/@prst` / `a:bevelB/@prst`. * * @example * ```ts * const bevel: BevelPresetType = "circle"; * // => "circle" — one of: "circle" | "relaxedInset" | "cross" | "coolSlant" | "angle" | … * ``` */ type BevelPresetType = 'circle' | 'relaxedInset' | 'cross' | 'coolSlant' | 'angle' | 'softRound' | 'convex' | 'slope' | 'divot' | 'riblet' | 'hardEdge' | 'artDeco' | 'none'; /** * Material preset type tokens from OOXML `a:sp3d/@prstMaterial`. * * @example * ```ts * const mat: MaterialPresetType = "plastic"; * // => "plastic" — one of: "matte" | "warmMatte" | "plastic" | "metal" | "dkEdge" | … * ``` */ type MaterialPresetType = 'matte' | 'warmMatte' | 'plastic' | 'metal' | 'dkEdge' | 'softEdge' | 'flat' | 'softmetal' | 'clear' | 'powder' | 'translucentPowder' | 'legacyMatte' | 'legacyPlastic' | 'legacyMetal' | 'legacyWireframe'; /** * 3D text body extrusion/bevel from `a:bodyPr/a:sp3d`. * * @example * ```ts * const text3d: Text3DStyle = { * extrusionHeight: 57150, * presetMaterial: "plastic", * bevelTopType: "circle", * bevelTopWidth: 25400, * bevelTopHeight: 25400, * }; * // => satisfies Text3DStyle * ``` */ interface Text3DStyle { /** Extrusion height (depth) in EMU. */ extrusionHeight?: number; /** Extrusion colour as hex string. */ extrusionColor?: string; /** Preset material, e.g. "matte", "plastic", "metal". */ presetMaterial?: MaterialPresetType; /** Top bevel preset type. */ bevelTopType?: BevelPresetType; /** Top bevel width in EMU. */ bevelTopWidth?: number; /** Top bevel height in EMU. */ bevelTopHeight?: number; /** Bottom bevel preset type. */ bevelBottomType?: BevelPresetType; /** Bottom bevel width in EMU. */ bevelBottomWidth?: number; /** Bottom bevel height in EMU. */ bevelBottomHeight?: number; } /** * 3D scene/camera properties from `a:scene3d`. * * @example * ```ts * const scene: Pptx3DScene = { * cameraPreset: "perspectiveFront", * lightRigType: "threePt", * lightRigDirection: "t", * }; * // => satisfies Pptx3DScene * ``` */ interface Pptx3DScene { /** Camera preset type, e.g. "orthographicFront", "perspectiveFront". */ cameraPreset?: string; /** Camera field of view in 1/60000 degrees (`a:camera/@fov`). */ cameraFieldOfView?: number; /** Camera zoom as an OOXML percentage fraction (`a:camera/@zoom`, 1 = 100%). */ cameraZoom?: number; /** Camera rotation around X axis in 1/60000 degrees. */ cameraRotX?: number; /** Camera rotation around Y axis in 1/60000 degrees. */ cameraRotY?: number; /** Camera rotation around Z axis in 1/60000 degrees. */ cameraRotZ?: number; /** Light rig type, e.g. "threePt", "balanced", "harsh". */ lightRigType?: string; /** Light rig direction, e.g. "t", "b", "l", "r", "tl". */ lightRigDirection?: string; /** Light-rig rotation latitude in 1/60000 degrees. */ lightRigRotX?: number; /** Light-rig rotation longitude in 1/60000 degrees. */ lightRigRotY?: number; /** Light-rig rotation revolution in 1/60000 degrees. */ lightRigRotZ?: number; /** Whether a 3D backdrop plane is present (`a:backdrop`). */ hasBackdrop?: boolean; /** Backdrop plane anchor X in EMU. */ backdropAnchorX?: number; /** Backdrop plane anchor Y in EMU. */ backdropAnchorY?: number; /** Backdrop plane anchor Z in EMU. */ backdropAnchorZ?: number; /** Backdrop normal vector X component. */ backdropNormalX?: number; /** Backdrop normal vector Y component. */ backdropNormalY?: number; /** Backdrop normal vector Z component. */ backdropNormalZ?: number; /** Backdrop up vector X component. */ backdropUpX?: number; /** Backdrop up vector Y component. */ backdropUpY?: number; /** Backdrop up vector Z component. */ backdropUpZ?: number; } /** * 3D shape extrusion/bevel from `a:sp3d`. * * @example * ```ts * const shape3d: Pptx3DShape = { * extrusionHeight: 76200, * extrusionColor: "#4F81BD", * presetMaterial: "metal", * bevelTopType: "circle", * bevelTopWidth: 12700, * bevelTopHeight: 12700, * }; * // => satisfies Pptx3DShape * ``` */ interface Pptx3DShape { /** * Position of the shape along the Z axis, in EMU (`a:sp3d/@z`, default 0). * Independent of {@link extrusionHeight}: it moves the whole shape forward * or back in 3D space rather than adding depth to it, most commonly used to * stack several shapes at different depths under one `a:scene3d` camera. */ positionZ?: number; /** Extrusion height in EMU. */ extrusionHeight?: number; /** Extrusion colour. */ extrusionColor?: string; /** Contour width in EMU. */ contourWidth?: number; /** Contour colour. */ contourColor?: string; /** Preset material, e.g. "matte", "warmMatte", "metal". */ presetMaterial?: string; /** Top bevel type, e.g. "circle", "relaxedInset". */ bevelTopType?: string; /** Top bevel width in EMU. */ bevelTopWidth?: number; /** Top bevel height in EMU. */ bevelTopHeight?: number; /** Bottom bevel type, e.g. "circle", "relaxedInset". */ bevelBottomType?: string; /** Bottom bevel width in EMU. */ bevelBottomWidth?: number; /** Bottom bevel height in EMU. */ bevelBottomHeight?: number; } /** * Known OOXML preset text warp types (WordArt transforms). * * Falls back to `string` for unknown presets not yet catalogued. * * @example * ```ts * const warp: PptxTextWarpPreset = "textArchUp"; * // => "textArchUp" — one of: "textNoShape" | "textPlain" | "textStop" | "textArchUp" | … * ``` */ type PptxTextWarpPreset = 'textNoShape' | 'textPlain' | 'textStop' | 'textTriangle' | 'textTriangleInverted' | 'textChevron' | 'textChevronInverted' | 'textRingInside' | 'textRingOutside' | 'textArchUp' | 'textArchDown' | 'textCircle' | 'textButton' | 'textArchUpPour' | 'textArchDownPour' | 'textCirclePour' | 'textButtonPour' | 'textCurveUp' | 'textCurveDown' | 'textCanUp' | 'textCanDown' | 'textWave1' | 'textWave2' | 'textWave4' | 'textDoubleWave1' | 'textInflate' | 'textDeflate' | 'textInflateBottom' | 'textDeflateBottom' | 'textInflateTop' | 'textDeflateTop' | 'textFadeRight' | 'textFadeLeft' | 'textFadeUp' | 'textFadeDown' | 'textSlantUp' | 'textSlantDown' | 'textCascadeUp' | 'textCascadeDown' | 'textDeflateInflate' | 'textDeflateInflateDeflate' | string; //#endregion //#region src/core/types/shape-style.d.ts interface PptxCustomDashSegment { /** Dash length as a non-negative percentage in thousandths of one percent. */ dash: number; /** Space length as a non-negative percentage in thousandths of one percent. */ space: number; } /** * Comprehensive visual style for a shape, connector, or image element. * * All fields are optional. When absent, the element inherits from theme * or layout defaults. The interface models both simple styling (solid fill + * basic stroke) and advanced effects (multiple shadow layers, gradient * fills, 3-D extrusion). * * @example * ```ts * // Simple blue filled shape with a thin black outline: * const simple: ShapeStyle = { * fillColor: "#0055AA", * fillMode: "solid", * strokeColor: "#000000", * strokeWidth: 1, * }; * * // Gradient fill with a soft shadow: * const fancy: ShapeStyle = { * fillMode: "gradient", * fillGradientType: "linear", * fillGradientAngle: 135, * fillGradientStops: [ * { color: "#FF6B6B", position: 0 }, * { color: "#556270", position: 1 }, * ], * shadowColor: "#000000", * shadowBlur: 10, * shadowOffsetX: 4, * shadowOffsetY: 4, * shadowOpacity: 0.3, * }; * // => both satisfy the ShapeStyle interface * ``` */ interface ShapeStyle { fillColor?: string; /** * Raw XML colour-choice node preserved from `a:solidFill` for round-trip * serialisation. Captures `a:schemeClr` / `a:sysClr` / `a:prstClr` / * `a:srgbClr` plus colour transforms (`lumMod`, `lumOff`, `tint`, * `shade`, `satMod`, `alpha`, …). On save we re-emit verbatim when the * resolved {@link fillColor} still matches this node, otherwise we fall * back to canonical ``. */ fillColorXml?: XmlObject; fillGradient?: string; /** Original `gradFill` XML retained for unknown-child and extension round-tripping. */ fillGradientXml?: XmlObject; fillMode?: 'solid' | 'gradient' | 'pattern' | 'none' | 'image' | 'theme' | 'group'; /** * ``: the shape paints with the SLIDE BACKGROUND's fill * rather than its own or its theme style's. * * PowerPoint's designer emits full-bleed rectangles this way, and they also * carry an `a:fillRef` pointing at `accent1`. Ignoring the attribute painted * those panels in the accent colour, so a black-and-white title slide came out * black-and-blue. The load pipeline copies the resolved slide background onto * the fill fields; the flag stays for round-trip and for renderers that want * to re-resolve against a changed background. */ useBackgroundFill?: boolean; fillPatternPreset?: string; fillPatternBackgroundColor?: string; /** Original `pattFill` XML retained for unknown-child round-tripping. */ fillPatternXml?: XmlObject; /** Raw XML node for pattern fill foreground colour (preserves color transforms). */ fillPatternFgClrXml?: XmlObject; /** Raw XML node for pattern fill background colour (preserves color transforms). */ fillPatternBgClrXml?: XmlObject; /** Data-URI or URL for image fill (when fillMode === "image"). */ fillImageUrl?: string; /** How the image is sized within the shape: stretch to fill, or tile/repeat. */ fillImageMode?: 'stretch' | 'tile'; fillGradientStops?: Array<{ color: string; position: number; opacity?: number; /** Raw XML colour node preserved for round-trip (e.g. a:schemeClr with transforms). */ originalColorXml?: XmlObject; }>; fillGradientAngle?: number; fillGradientType?: 'linear' | 'radial'; /** Path gradient sub-type from `a:path/@path` (e.g. "circle", "rect", "shape"). */ fillGradientPathType?: 'circle' | 'rect' | 'shape'; /** Focal point for path (radial) gradients, derived from `a:fillToRect`. * Values are 0..1 fractions relative to shape bounds. */ fillGradientFocalPoint?: { x: number; y: number; }; /** Raw fillToRect LTRB values (0..1 fractions) from `a:fillToRect`. * Defines the inner rectangle where the gradient reaches its final stop. * l/t are insets from left/top edges; r/b are insets from right/bottom edges. */ fillGradientFillToRect?: { l: number; t: number; r: number; b: number; }; /** Raw tileRect LTRB values (0..1 fractions, may be negative) from * `a:gradFill/a:tileRect`. Defines the rectangle the gradient tile occupies * before any flip/tiling is applied. */ fillGradientTileRect?: { l: number; t: number; r: number; b: number; }; /** Gradient tile flip mode (`a:gradFill/@flip`). * `none` = no tiling flip (default), `x|y|xy` = mirror in the named axis. */ fillGradientFlip?: 'none' | 'x' | 'y' | 'xy'; /** Whether the gradient rotates with the shape (`a:gradFill/@rotWithShape`). * Defaults to true per the schema; preserved for round-trip when the source * authored the attribute explicitly. */ fillGradientRotWithShape?: boolean; /** Whether the linear gradient is scaled to the shape (`a:lin/@scaled`). * Defaults to true per the schema; preserved for round-trip. */ fillGradientScaled?: boolean; fillOpacity?: number; strokeColor?: string; /** * Raw XML colour-choice node preserved from `a:ln/a:solidFill` for * round-trip serialisation. See {@link fillColorXml} for the rationale. */ strokeColorXml?: XmlObject; /** * Kind of fill painted on the outline (`a:ln` child). Distinguishes a solid * outline from a gradient/pattern/none outline so save can emit the correct * single line fill instead of collapsing every outline to `a:solidFill` * (which, alongside a preserved `a:gradFill`/`a:pattFill`, produces an * invalid dual-fill ``). */ strokeFillMode?: 'solid' | 'gradient' | 'pattern' | 'none'; /** Raw `a:ln/a:gradFill` XML preserved for round-trip when the outline is * gradient-filled. Re-emitted verbatim as the line's single fill on save. */ strokeGradientXml?: XmlObject; /** Raw `a:ln/a:pattFill` XML preserved for round-trip when the outline is * pattern-filled. Re-emitted verbatim as the line's single fill on save. */ strokePatternXml?: XmlObject; /** * Structured stops of a gradient outline (`a:ln/a:gradFill/a:gsLst`), in the * same shape as {@link fillGradientStops}. * * The raw XML above round-trips a gradient outline on save, but a renderer * cannot paint from it: it needs resolved colours and positions. Without * these, every binding fell back to {@link strokeColor} - a single averaged * colour - so a two-tone outline painted flat and a fade-to-transparent * outline painted fully opaque. */ strokeGradientStops?: ShapeStyle['fillGradientStops']; /** Gradient outline angle in OOXML degrees (`a:lin/@ang`), 0 = left to right. */ strokeGradientAngle?: number; /** Gradient outline kind: `linear` (`a:lin`) or `radial` (`a:path`). */ strokeGradientType?: ShapeStyle['fillGradientType']; /** Path-gradient shape for a radial outline (`a:path/@path`). */ strokeGradientPathType?: ShapeStyle['fillGradientPathType']; /** Preset name of a pattern outline (`a:ln/a:pattFill/@prst`). */ strokePatternPreset?: string; /** Background colour of a pattern outline (`a:ln/a:pattFill/a:bgClr`). */ strokePatternBackgroundColor?: string; strokeWidth?: number; strokeOpacity?: number; strokeDash?: StrokeDashType; /** Line join style (`a:ln/@join`): round, bevel, or miter. */ lineJoin?: 'round' | 'bevel' | 'miter'; /** Miter limit (`a:miter/@lim`) in EMU-percent units (default 800000 = 8.0). Only meaningful when lineJoin is 'miter'. */ miterLimit?: number; /** Line cap style (`a:ln/@cap`): flat, rnd, or sq. */ lineCap?: 'flat' | 'rnd' | 'sq'; /** Compound line type (`a:ln/@cmpd`). */ compoundLine?: 'sng' | 'dbl' | 'thickThin' | 'thinThick' | 'tri'; /** Pen line alignment (`a:ln/@algn`): `ctr` (centre, default) or `in` (inside). */ lineAlignment?: 'ctr' | 'in'; shadowColor?: string; /** Preserved source `a:effectLst`, including unknown effects and extensions. */ effectListXml?: XmlObject; /** Original outer-shadow node used for lossless surgical updates. */ outerShadowXml?: XmlObject; /** Resolved source shadow colour used to detect colour edits. */ outerShadowOriginalColor?: string; /** Source shadow opacity used to detect alpha edits. */ outerShadowOriginalOpacity?: number; shadowBlur?: number; shadowOffsetX?: number; shadowOffsetY?: number; shadowOpacity?: number; /** Preset shadow name from `a:prstShdw/@prst` (e.g. "shdw1"..."shdw20"). */ presetShadowName?: string; /** Shadow angle in degrees (0-360). Parsed from `@_dir` (60000ths of a degree). */ shadowAngle?: number; /** Shadow distance in pixels. Parsed from `@_dist` (EMUs). */ shadowDistance?: number; /** Whether shadow rotates with shape. Parsed from `@_rotWithShape`. */ shadowRotateWithShape?: boolean; /** Outer-shadow horizontal scaling (`a:outerShdw/@sx`) in 1000ths of a percent (default 100000 = 100%). */ shadowScaleX?: number; /** Outer-shadow vertical scaling (`a:outerShdw/@sy`). */ shadowScaleY?: number; /** Outer-shadow horizontal skew (`a:outerShdw/@kx`) in 60000ths of a degree. */ shadowSkewX?: number; /** Outer-shadow vertical skew (`a:outerShdw/@ky`). */ shadowSkewY?: number; /** Outer-shadow alignment (`a:outerShdw/@algn`). */ shadowAlignment?: 'tl' | 't' | 'tr' | 'l' | 'ctr' | 'r' | 'bl' | 'b' | 'br'; /** Inner-shadow rotateWithShape (`a:innerShdw/@rotWithShape`). */ innerShadowRotateWithShape?: boolean; /** Reflection fade direction (`a:reflection/@fadeDir`) in 60000ths of a degree. */ reflectionFadeDirection?: number; /** Reflection horizontal scaling (`a:reflection/@sx`). */ reflectionScaleX?: number; /** Reflection vertical scaling (`a:reflection/@sy`). */ reflectionScaleY?: number; /** Reflection horizontal skew (`a:reflection/@kx`). */ reflectionSkewX?: number; /** Reflection vertical skew (`a:reflection/@ky`). */ reflectionSkewY?: number; /** Reflection alignment (`a:reflection/@algn`). */ reflectionAlignment?: 'tl' | 't' | 'tr' | 'l' | 'ctr' | 'r' | 'bl' | 'b' | 'br'; /** Reflection rotateWithShape (`a:reflection/@rotWithShape`). */ reflectionRotateWithShape?: boolean; /** Reflection start position (`a:reflection/@stPos`) as 0-1 fraction. */ reflectionStartPosition?: number; /** Multiple shadow layers (for advanced effects). */ shadows?: ShadowEffect[]; glowColor?: string; /** Original glow node used for lossless surgical updates. */ glowXml?: XmlObject; /** Resolved source glow colour used to detect colour edits. */ glowOriginalColor?: string; /** Source glow opacity used to detect alpha edits. */ glowOriginalOpacity?: number; glowRadius?: number; glowOpacity?: number; softEdgeRadius?: number; /** Inner shadow colour (`a:innerShdw`). */ innerShadowColor?: string; /** Original inner-shadow node used for lossless surgical updates. */ innerShadowXml?: XmlObject; /** Resolved source inner-shadow colour used to detect colour edits. */ innerShadowOriginalColor?: string; /** Source inner-shadow opacity used to detect alpha edits. */ innerShadowOriginalOpacity?: number; /** Inner shadow opacity (0-1). */ innerShadowOpacity?: number; /** Inner shadow blur radius in px. */ innerShadowBlur?: number; /** Inner shadow horizontal offset in px. */ innerShadowOffsetX?: number; /** Inner shadow vertical offset in px. */ innerShadowOffsetY?: number; /** Original soft-edge node, including vendor attributes and extensions. */ softEdgeXml?: XmlObject; /** Reflection effect — distance from shape bottom in px. */ reflectionBlurRadius?: number; /** Original reflection node, including vendor attributes and extensions. */ reflectionXml?: XmlObject; /** Reflection start opacity (0-1). */ reflectionStartOpacity?: number; /** Reflection end opacity (0-1). */ reflectionEndOpacity?: number; /** Reflection end position (0-1 fraction of shape height). */ reflectionEndPosition?: number; /** Reflection direction in degrees. */ reflectionDirection?: number; /** Reflection rotation in degrees (`a:reflection/@rot` in 60000ths). */ reflectionRotation?: number; /** Reflection distance in px. */ reflectionDistance?: number; /** Standalone blur effect radius in px (`a:effectLst > a:blur`). */ blurRadius?: number; /** Whether the blur effect grows the bounds of the shape (`a:blur/@grow`). */ blurGrow?: boolean; connectorStartArrow?: ConnectorArrowType; /** Start arrow width size ('sm' | 'med' | 'lg'). */ connectorStartArrowWidth?: 'sm' | 'med' | 'lg'; /** Start arrow length size ('sm' | 'med' | 'lg'). */ connectorStartArrowLength?: 'sm' | 'med' | 'lg'; connectorEndArrow?: ConnectorArrowType; /** End arrow width size ('sm' | 'med' | 'lg'). */ connectorEndArrowWidth?: 'sm' | 'med' | 'lg'; /** End arrow length size ('sm' | 'med' | 'lg'). */ connectorEndArrowLength?: 'sm' | 'med' | 'lg'; /** Connection point for the start of a connector. */ connectorStartConnection?: ConnectorConnectionPoint; /** Connection point for the end of a connector. */ connectorEndConnection?: ConnectorConnectionPoint; /** Custom dash pattern, measured relative to line width in thousandths of one percent. */ customDashSegments?: PptxCustomDashSegment[]; /** Original `a:ds` payloads retained by index for lossless edits. */ customDashSegmentXml?: XmlObject[]; /** Original `a:custDash` payload retained for lossless edits. */ customDashXml?: XmlObject; /** 3D scene/camera settings from `a:scene3d`. */ scene3d?: Pptx3DScene; /** 3D shape extrusion/bevel from `a:sp3d`. */ shape3d?: Pptx3DShape; /** Line-level shadow colour from `a:ln/a:effectLst/a:outerShdw`. */ lineShadowColor?: string; /** Line-level shadow opacity (0-1). */ lineShadowOpacity?: number; /** Line-level shadow blur radius in px. */ lineShadowBlur?: number; /** Line-level shadow horizontal offset in px. */ lineShadowOffsetX?: number; /** Line-level shadow vertical offset in px. */ lineShadowOffsetY?: number; /** Line-level glow colour from `a:ln/a:effectLst/a:glow`. */ lineGlowColor?: string; /** Line-level glow radius in px. */ lineGlowRadius?: number; /** Line-level glow opacity (0-1). */ lineGlowOpacity?: number; /** Raw `a:effectDag` XML node preserved for round-trip serialisation. */ effectDagXml?: XmlObject; /** * Typed effect graph parsed from {@link ShapeStyle.effectDagXml}. The four * structural container nodes (`a:cont`, `a:blend`, `a:xfrmEffect`, * `a:relOff`) are fully typed; any other leaf effect (e.g. `a:outerShdw`, * `a:glow`, `a:alphaInv`) is captured as * {@link import('./effect-dag').EffectDagRawLeaf} so we never have to * recurse into the full effect taxonomy. */ effectDagTree?: EffectDagContainer; /** Grayscale flag from effectDag `a:grayscl`. */ dagGrayscale?: boolean; /** Bi-level threshold (0-100) from effectDag `a:biLevel`. */ dagBiLevel?: number; /** Brightness adjustment (-100 to 100) from effectDag `a:lum/@bright`. */ dagLumBrightness?: number; /** Contrast adjustment (-100 to 100) from effectDag `a:lum/@contrast`. */ dagLumContrast?: number; /** Hue rotation in degrees (0-360) from effectDag `a:hsl/@hue`. */ dagHslHue?: number; /** Saturation adjustment from effectDag `a:hsl/@sat`. */ dagHslSaturation?: number; /** Luminance adjustment from effectDag `a:hsl/@lum`. */ dagHslLuminance?: number; /** Alpha modulation fixed (0-100) from effectDag `a:alphaModFix`. */ dagAlphaModFix?: number; /** Tint hue in degrees from effectDag `a:tint/@hue`. */ dagTintHue?: number; /** Tint amount (0-100) from effectDag `a:tint/@amt`. */ dagTintAmount?: number; /** Duotone colour pair from effectDag `a:duotone`. */ dagDuotone?: { color1: string; color2: string; }; /** Fill overlay blend mode from effectDag `a:fillOverlay/@blend`. */ dagFillOverlayBlend?: 'over' | 'mult' | 'screen' | 'darken' | 'lighten'; /** * Fill overlay tint colour (hex `#RRGGBB`) from effectDag `a:fillOverlay`'s * `a:solidFill`/`a:gradFill`. Painted as a blended overlay layer over the * element; the blend mode comes from {@link dagFillOverlayBlend}. */ dagFillOverlayColor?: string; /** Fill overlay tint opacity (0-1), from the overlay fill colour's alpha. */ dagFillOverlayOpacity?: number; /** `` — 1-based index into the theme's lnStyleLst. */ lnRefIdx?: number; /** Raw XML colour child of `` (e.g. `` with transforms). */ lnRefColorXml?: XmlObject; /** `` — 1-based index into fillStyleLst (1-3) or bgFillStyleLst (1001-1003). */ fillRefIdx?: number; /** Raw XML colour child of ``. */ fillRefColorXml?: XmlObject; /** `` — 1-based index into the theme's effectStyleLst. */ effectRefIdx?: number; /** Raw XML colour child of ``. */ effectRefColorXml?: XmlObject; /** `` — typically `major`, `minor`, or `none`. */ fontRefIdx?: string; /** Raw XML colour child of ``. */ fontRefColorXml?: XmlObject; /** * The fill `` resolved to, recorded ONLY when the shape's own * `spPr` authored no fill at all, so the reference is what paints it. * * Its absence therefore means "the fill is the shape's own", and its * presence plus an unchanged flat fill means "still purely inherited": see * `authored-shape-style.ts`, the shape-scope twin of `TextStyle`'s * `inheritedRunStyle`. */ inheritedFillStyle?: ShapeStyle; /** * The outline `` resolved to, recorded before `spPr/a:ln` was * layered on top. A property that still equals this baseline was never * authored on the shape and must not be written back as if it were. */ inheritedLineStyle?: ShapeStyle; /** * The shadow/glow/reflection/soft-edge/3D properties `` * resolved from the theme's `effectStyleLst`, recorded ONLY for the * properties the shape had not already authored itself. A shape whose * effects still match this baseline was never given its own effects and * must not have them written back as a literal `spPr/a:effectLst`; see * `authored-shape-style.ts`'s `effectIsPurelyStyleMatrix`. */ inheritedEffectStyle?: ShapeStyle; } //#endregion //#region src/core/types/text.d.ts /** * Rich text style properties for a text run or paragraph. * * Combines character-level formatting (font, bold, colour …), * paragraph-level controls (alignment, spacing, indentation), and * body-level properties (autofit, insets, text direction). All * fields are optional — unset properties inherit from layout/master * placeholders or theme defaults. * * @remarks * Font sizes are stored in **points**. Spatial measurements (insets, * margins) are in **pixels** (pre-converted from EMU during parsing). * * @example * ```ts * const heading: TextStyle = { * fontFamily: "Montserrat", * fontSize: 36, * bold: true, * color: "#1A1A2E", * align: "center", * lineSpacing: 1.15, * }; * * const body: TextStyle = { * fontFamily: "Open Sans", * fontSize: 14, * color: "#444444", * align: "left", * paragraphSpacingAfter: 8, * }; * // => both satisfy the TextStyle interface * ``` */ interface TextStyle { /** Original `a:rPr` XML retained by projections that share the shape-text model. */ runPropertiesXml?: XmlObject; /** * The properties this run's OWN `a:rPr` authored, and nothing else. * * A run style is assembled as * `{...inheritedRunStyle, ...authoredRunStyle}`, so the flat style is a * fully RESOLVED view: it cannot say whether `fontSize: 60` came from the * run, from the shape's `a:lstStyle`, from the layout placeholder, from the * master `p:txStyles` or from the theme. Omission is meaningful in OOXML * (§21.1.2.3), so a writer that re-emits the resolved view converts every * inherited value into an authored one and the deck stops being * theme-driven after one save. * * This is the run-scope twin of {@link TextSegment.paragraphProperties}, * which is parsed strictly from the paragraph's own `a:pPr` for the same * reason. Present only for runs that came from a parsed deck; absent for * SDK-built text, where the flat style IS the only description and must be * written out in full. */ authoredRunStyle?: TextStyle; /** * The resolved inheritance baseline {@link authoredRunStyle} was layered * on top of (shape `a:lstStyle` -> placeholder -> layout -> master * `p:txStyles` -> theme -> `p:defaultTextStyle`). * * Kept alongside the authored half because the two answer different * questions. The authored half says "the source pinned this"; the baseline * says "this value is what inheritance already produces", which is how an * EDIT is told apart from an inherited value: an editor mutates the flat * style without knowing about either field, so a property that now differs * from the baseline was either authored or edited and must be written, * while one that still matches can be left to inherit. * * Holds a reference to the per-paragraph baseline object rather than a * copy, so carrying it costs one pointer per run. */ inheritedRunStyle?: TextStyle; /** * Snapshot of the ELEMENT-scope paragraph geometry (alignment, margins, * indent, line and paragraph spacing, tab stops, rtl, line-break flags) as * the load pipeline resolved it. * * Present only on an `element.textStyle` that came from a parsed deck, and * populated only with the geometry keys. It exists so the save path can * answer one question it otherwise cannot: has the user CHANGED the body's * alignment or indent, or is the value simply what the shape's * `a:lstStyle`, its layout placeholder and the master already produce? * Element-level text panels (`textAdvancedPatch`, `alignPatch` and friends * in `pptx-viewer-shared`) write `element.textStyle` and never touch * `segment.paragraphProperties`, so a diff against this snapshot is the * only way to tell an edit from an inheritance artefact. * * @see element-paragraph-geometry.ts */ resolvedParagraphGeometry?: TextStyle; fontFamily?: string; fontSize?: number; /** When true, some form of autofit is in effect; see {@link autoFitMode} for which. */ autoFit?: boolean; /** Explicit autofit mode from OOXML body properties. * - 'shrink': `a:spAutoFit` - resize the SHAPE to fit the text (never the font) * - 'normal': `a:normAutofit` - shrink the TEXT to fit the shape (via `fontScale`/`lnSpcReduction`) * - 'none': `a:noAutofit` - explicitly no auto-fit (text overflows) * - undefined: no autofit element present (inherit from layout/master) */ autoFitMode?: 'shrink' | 'normal' | 'none'; /** Font scale percentage for normAutofit (e.g. 0.9 = 90%). Only meaningful when autoFit is true. */ autoFitFontScale?: number; /** Line spacing reduction for normAutofit (e.g. 0.2 = reduce by 20%). Only meaningful when autoFit is true. */ autoFitLineSpacingReduction?: number; bold?: boolean; italic?: boolean; underline?: boolean; /** Specific underline style (e.g. "sng", "dbl", "wavy"). Falls back to "sng" when `underline` is true. */ underlineStyle?: UnderlineStyle; /** Underline colour as hex string (`a:uFill` / `a:uLn`). When absent, inherits text colour. */ underlineColor?: string; /** * When true, the source authored `` to explicitly suppress * underline (rather than omitting the attribute entirely). Preserved so the * writer can re-emit the explicit `none` token instead of dropping it. */ underlineExplicitNone?: boolean; /** * Underline line properties parsed from `` — width, dash * preset, and end caps. Captured as a typed object so the writer can * round-trip the line styling that previously was dropped (only the * solidFill colour was carried before). */ underlineLine?: { /** Line width in EMU (raw OOXML) for `a:uLn/@w`. */ widthEmu?: number; /** Compound line type (`a:uLn/@cmpd`). */ compound?: string; /** Cap style (`a:uLn/@cap`). */ cap?: string; /** Pen alignment (`a:uLn/@algn`). */ algn?: string; /** Preset dash value (`a:uLn/a:prstDash/@val`). */ prstDash?: string; /** Raw `a:uLn/a:headEnd` XML preserved verbatim. */ headEndXml?: XmlObject; /** Raw `a:uLn/a:tailEnd` XML preserved verbatim. */ tailEndXml?: XmlObject; }; /** When `` is present — underline line follows the text run line. */ underlineLineFollowsText?: boolean; /** When `` is present — underline fill follows the text run fill. */ underlineFillFollowsText?: boolean; strikethrough?: boolean; /** Specific strike type: single or double from `a:rPr/@strike`. */ strikeType?: 'sngStrike' | 'dblStrike'; /** Text outline width in px (`a:rPr > a:ln/@w` in EMU). */ textOutlineWidth?: number; /** Text outline colour as hex string (`a:rPr > a:ln > a:solidFill`). */ textOutlineColor?: string; /** When true, the text body has no fill (`a:rPr > a:noFill`), producing hollow/outline-only text. */ textFillNone?: boolean; /** Superscript/subscript baseline shift as percentage (`a:rPr/@baseline`). Positive = super, negative = sub. */ baseline?: number; /** Character spacing in hundredths of a point (`a:rPr/@spc`). */ characterSpacing?: number; /** Kerning threshold in hundredths of a point (`a:rPr/@kern`). 0 = none. */ kerning?: number; /** Text highlight colour as hex string (`a:highlight`). */ highlightColor?: string; /** * Raw colour-choice XML preserved from `a:highlight` so a themed highlight * (`a:schemeClr` / `a:sysClr` / `a:prstClr`) re-emits with its original * identity rather than being flattened to `` on save. On save we * re-emit verbatim when the resolved {@link highlightColor} still matches. */ highlightColorXml?: XmlObject; /** Text-level gradient fill CSS string (from `a:rPr > a:gradFill`). */ textFillGradient?: string; /** Structured gradient stops for text fill round-trip serialization. */ textFillGradientStops?: Array<{ color: string; position: number; opacity?: number; }>; /** Gradient angle in degrees for text fill round-trip. */ textFillGradientAngle?: number; /** Gradient type for text fill round-trip ('linear' | 'radial'). */ textFillGradientType?: 'linear' | 'radial'; /** Text-level pattern fill preset (from `a:rPr > a:pattFill`). */ textFillPattern?: string; /** Text-level pattern foreground colour. */ textFillPatternForeground?: string; /** Text-level pattern background colour. */ textFillPatternBackground?: string; hyperlink?: string; /** Relationship ID for the hyperlink (`a:hlinkClick/@r:id`) — preserved for round-trip serialization. */ hyperlinkRId?: string; /** Hyperlink tooltip text (`a:hlinkClick/@tooltip`). */ hyperlinkTooltip?: string; /** Hyperlink action type (`a:hlinkClick/@action`). */ hyperlinkAction?: string; /** Whether the hyperlink target is an internal slide jump (targetSlideIndex style). */ hyperlinkTargetSlideIndex?: number; color?: string; /** * Raw XML colour-choice node preserved from `a:rPr/a:solidFill` for * round-trip serialisation. Captures `a:schemeClr` / `a:sysClr` / * `a:prstClr` / `a:srgbClr` plus colour transforms. On save we re-emit * verbatim when the resolved {@link color} still matches this node. */ colorXml?: XmlObject; align?: 'left' | 'center' | 'right' | 'justify' | 'justLow' | 'dist' | 'thaiDist'; vAlign?: 'top' | 'middle' | 'bottom'; /** Right-to-left paragraph/run direction (`a:pPr/@rtl`, `a:rPr/@rtl`). */ rtl?: boolean; /** Body text direction (`a:bodyPr/@vert`). * * Values map to OOXML `a:bodyPr/@vert` attribute values: * - `"horizontal"` — default horizontal text (`horz`) * - `"vertical"` — standard vertical text, right-to-left columns (`vert`) * - `"vertical270"` — text rotated 270 degrees (`vert270`) * - `"eaVert"` — East Asian vertical text with CJK glyphs upright (`eaVert`) * - `"wordArtVert"` — WordArt vertical, each character upright stacked (`wordArtVert`) * - `"wordArtVertRtl"` — WordArt vertical, right-to-left direction (`wordArtVertRtl`) * - `"mongolianVert"` — Mongolian vertical text, left-to-right columns (`mongolianVert`) */ textDirection?: 'horizontal' | 'vertical' | 'vertical270' | 'eaVert' | 'wordArtVert' | 'wordArtVertRtl' | 'mongolianVert'; /** Body column count (`a:bodyPr/@numCol`). */ columnCount?: number; /** Column spacing in px (`a:bodyPr/@spcCol` in EMU). */ columnSpacing?: number; /** Horizontal overflow mode from `a:bodyPr/@hOverflow`. */ hOverflow?: 'overflow' | 'clip'; /** Vertical overflow mode from `a:bodyPr/@vertOverflow`. */ vertOverflow?: 'overflow' | 'clip' | 'ellipsis'; /** Body text left inset in px (`a:bodyPr/@lIns` in EMU). */ bodyInsetLeft?: number; /** Body text top inset in px (`a:bodyPr/@tIns` in EMU). */ bodyInsetTop?: number; /** Body text right inset in px (`a:bodyPr/@rIns` in EMU). */ bodyInsetRight?: number; /** Body text bottom inset in px (`a:bodyPr/@bIns` in EMU). */ bodyInsetBottom?: number; /** Paragraph spacing before in px. */ paragraphSpacingBefore?: number; /** Paragraph spacing after in px. */ paragraphSpacingAfter?: number; /** Line spacing multiplier (e.g. 1.2 = 120%). Used when mode is proportional (spcPct). */ lineSpacing?: number; /** Exact line spacing in points (from `a:lnSpc > a:spcPts`). Takes priority over `lineSpacing` when set. */ lineSpacingExactPt?: number; /** Paragraph left margin in px (`a:pPr/@marL` in EMU). */ paragraphMarginLeft?: number; /** Paragraph right margin in px (`a:pPr/@marR` in EMU). */ paragraphMarginRight?: number; /** Paragraph first-line indent in px (`a:pPr/@indent` in EMU). */ paragraphIndent?: number; /** Tab stop positions and alignments (`a:pPr/a:tabLst/a:tab`). */ tabStops?: Array<{ position: number; align: 'l' | 'ctr' | 'r' | 'dec'; leader?: 'none' | 'dot' | 'hyphen' | 'underscore'; }>; /** Body text wrapping mode from `a:bodyPr/@wrap`. */ textWrap?: 'square' | 'none'; /** Preset text warp type from `a:bodyPr/a:prstTxWarp`. */ textWarpPreset?: PptxTextWarpPreset; /** Primary adjustment value for text warp (from `a:prstTxWarp/a:avLst/a:gd` with name "adj"). * Stored as raw OOXML 1/60000th units (e.g. 50000 = default for many presets). */ textWarpAdj?: number; /** Secondary adjustment value for text warp (from `a:prstTxWarp/a:avLst/a:gd` with name "adj2"). * Stored as raw OOXML 1/60000th units. */ textWarpAdj2?: number; /** Text capitalization style from `a:rPr/@cap`. */ textCaps?: 'all' | 'small' | 'none'; /** * When true, the source authored `` explicitly. This * differs from {@link textCaps} = `"none"` only because the writer must * preserve the explicit token rather than collapse it to omission. */ textCapsExplicitNone?: boolean; /** Symbol font family from `a:sym`. */ symbolFont?: string; /** East Asian font family from `a:ea`. */ eastAsiaFont?: string; /** Complex Script font family from `a:cs`. */ complexScriptFont?: string; /** * Theme-font token (`+mj-lt` / `+mn-lt` / ...) authored on `a:latin`, when * present. {@link fontFamily} holds the resolved concrete face for * rendering; this preserves the token linkage so the writer re-emits the * token rather than the flattened face (see #84). */ latinFontThemeToken?: string; /** Theme-font token authored on `a:ea` (e.g. `+mn-ea`), when present. */ eastAsiaFontThemeToken?: string; /** Theme-font token authored on `a:cs` (e.g. `+mn-cs`), when present. */ complexScriptFontThemeToken?: string; /** * Automatic per-script fallback face resolved from the theme's * `` overrides for a run whose text is dominantly * CJK / Arabic / Hebrew / Thai (see #83). A rendering hint only: it is not * serialised back on save, so it never disturbs the round-trip typefaces. */ scriptFallbackFont?: string; /** Text language from `a:rPr/@lang`. */ language?: string; /** Hyperlink mouse-over target from `a:hlinkMouseOver`. */ hyperlinkMouseOver?: string; /** * Raw `a:snd` (embedded WAV audio) child of `a:hlinkClick`, preserved * verbatim (carries `@r:embed` + `@name`). Round-tripped on save so the * click sound survives instead of being dropped. */ hyperlinkSoundXml?: XmlObject; /** Raw `a:snd` child of `a:hlinkMouseOver`, preserved verbatim for round-trip. */ hyperlinkMouseOverSoundXml?: XmlObject; /** Hyperlink invalidUrl attribute (`a:hlinkClick/@invalidUrl`). */ hyperlinkInvalidUrl?: string; /** Hyperlink target frame (`a:hlinkClick/@tgtFrame`). */ hyperlinkTargetFrame?: string; /** Whether hyperlink history is tracked (`a:hlinkClick/@history`). */ hyperlinkHistory?: boolean; /** Whether hyperlink uses highlight-click effect (`a:hlinkClick/@highlightClick`). */ hyperlinkHighlightClick?: boolean; /** Whether hyperlink ends a sound (`a:hlinkClick/@endSnd`). */ hyperlinkEndSound?: boolean; /** Kumimoji (ideographic text combining) flag for vertical CJK text (`a:rPr/@kumimoji`). */ kumimoji?: boolean; /** Normalize height flag (`a:rPr/@normalizeH`). */ normalizeHeight?: boolean; /** No proofing flag (`a:rPr/@noProof`). */ noProof?: boolean; /** Dirty flag indicating run has been edited (`a:rPr/@dirty`). */ dirty?: boolean; /** Error flag indicating spelling error (`a:rPr/@err`). */ spellingError?: boolean; /** Smart tag clean flag (`a:rPr/@smtClean`). */ smartTagClean?: boolean; /** Bookmark link target (`a:rPr/@bmk`). */ bookmark?: string; /** Alternative language for the run (`a:rPr/@altLang`). Populated for runs * authored in mixed-script documents (e.g. Asian/Latin combined). */ altLanguage?: string; /** SmartTag (Office grammar tag) GUID id (`a:rPr/@smtId`). Round-tripped * verbatim — the engine doesn't interpret it. */ smartTagId?: number; /** Latin font PANOSE classification string from `a:rPr > a:latin/@panose`. */ latinFontPanose?: string; /** Latin font pitch + family flag from `a:rPr > a:latin/@pitchFamily`. */ latinFontPitchFamily?: number; /** Latin font character set id from `a:rPr > a:latin/@charset`. */ latinFontCharset?: number; /** East-Asian font PANOSE from `a:rPr > a:ea/@panose`. */ eastAsiaFontPanose?: string; /** East-Asian font pitch + family flag from `a:rPr > a:ea/@pitchFamily`. */ eastAsiaFontPitchFamily?: number; /** East-Asian font character set id from `a:rPr > a:ea/@charset`. */ eastAsiaFontCharset?: number; /** Complex-script font PANOSE from `a:rPr > a:cs/@panose`. */ complexScriptFontPanose?: string; /** Complex-script font pitch + family flag from `a:rPr > a:cs/@pitchFamily`. */ complexScriptFontPitchFamily?: number; /** Complex-script font character set id from `a:rPr > a:cs/@charset`. */ complexScriptFontCharset?: number; /** Symbol-font PANOSE from `a:rPr > a:sym/@panose`. */ symbolFontPanose?: string; /** Symbol-font pitch + family flag from `a:rPr > a:sym/@pitchFamily`. */ symbolFontPitchFamily?: number; /** Symbol-font character set id from `a:rPr > a:sym/@charset`. */ symbolFontCharset?: number; /** Paragraph list type for toggling bullet / numbered lists via the toolbar. * - `'bullet'` — character bullet (default "•") * - `'numbered'` — auto-numbered list (arabicPeriod) * - `'none'` — explicitly no list */ listType?: 'bullet' | 'numbered' | 'none'; /** Default tab size in px (`a:pPr/@defTabSz` in EMU). */ defaultTabSize?: number; /** East Asian line break flag (`a:pPr/@eaLnBrk`). */ eaLineBreak?: boolean; /** Latin line break flag (`a:pPr/@latinLnBrk`). */ latinLineBreak?: boolean; /** Font alignment (`a:pPr/@fontAlgn`): 'auto' | 'base' | 'ctr' | 't' | 'b'. */ fontAlignment?: string; /** Hanging punctuation flag (`a:pPr/@hangingPunct`). */ hangingPunctuation?: boolean; /** Whether to space first and last paragraph from body edges (`a:bodyPr/@spcFirstLastPara`). */ spaceFirstLastParagraph?: boolean; /** Right-to-left column flow (`a:bodyPr/@rtlCol`). */ rtlColumns?: boolean; /** Whether text originates from WordArt (`a:bodyPr/@fromWordArt`). */ fromWordArt?: boolean; /** Whether text anchoring is centered (`a:bodyPr/@anchorCtr`). */ anchorCenter?: boolean; /** Force anti-aliasing (`a:bodyPr/@forceAA`). */ forceAntiAlias?: boolean; /** Upright text in 3D views (`a:bodyPr/@upright`). */ upright?: boolean; /** Compatible line spacing flag (`a:bodyPr/@compatLnSpc`). */ compatibleLineSpacing?: boolean; /** * Text body rotation in **degrees** (`a:bodyPr/@rot`). * * OOXML stores the value as 60000ths of a degree. Positive values rotate * the body clockwise. When undefined, the attribute is omitted on save * (PowerPoint treats absent `rot` as inherit/none). */ textBodyRotation?: number; /** Text shadow colour as hex string (`a:outerShdw`). */ textShadowColor?: string; /** Text shadow blur radius in px. */ textShadowBlur?: number; /** Text shadow horizontal offset in px. */ textShadowOffsetX?: number; /** Text shadow vertical offset in px. */ textShadowOffsetY?: number; /** Text shadow opacity (0-1). */ textShadowOpacity?: number; /** Text inner shadow colour (`a:innerShdw`). */ textInnerShadowColor?: string; /** Text inner shadow opacity (0-1). */ textInnerShadowOpacity?: number; /** Text inner shadow blur radius in px. */ textInnerShadowBlur?: number; /** Text inner shadow horizontal offset in px. */ textInnerShadowOffsetX?: number; /** Text inner shadow vertical offset in px. */ textInnerShadowOffsetY?: number; /** Preset shadow type from `a:prstShdw/@prst` (e.g. "shdw1"..."shdw20"). */ textPresetShadowName?: string; /** Preset shadow colour as hex string. */ textPresetShadowColor?: string; /** Preset shadow opacity (0-1). */ textPresetShadowOpacity?: number; /** Preset shadow distance in px. */ textPresetShadowDistance?: number; /** Preset shadow direction in degrees. */ textPresetShadowDirection?: number; /** Text blur effect radius in px (`a:blur`). */ textBlurRadius?: number; /** * Raw `a:effectDag` XML node from `a:rPr`, preserved verbatim for * round-trip serialisation. Mirrors the shape-level * {@link import('./shape-style').ShapeStyle.effectDagXml} field. */ textEffectDagXml?: XmlObject; /** * Typed effect graph parsed from `textEffectDagXml`. The four structural * container nodes (`a:cont`, `a:blend`, `a:xfrmEffect`, `a:relOff`) are * fully typed; any other leaf effect is captured as * {@link import('./effect-dag').EffectDagRawLeaf} so we never have to * recurse into the full effect taxonomy. */ textEffectDagTree?: EffectDagContainer; /** Text alpha modulation fixed (0-100) from `a:alphaModFix`. */ textAlphaModFix?: number; /** Text alpha modulation from `a:alphaMod` (0-100 percentage). */ textAlphaMod?: number; /** Text hue shift in degrees from `a:hsl/@hue`. */ textHslHue?: number; /** Text saturation adjustment from `a:hsl/@sat`. */ textHslSaturation?: number; /** Text luminance adjustment from `a:hsl/@lum`. */ textHslLuminance?: number; /** Text colour change from colour as hex string (`a:clrChange`). */ textClrChangeFrom?: string; /** Text colour change to colour as hex string. */ textClrChangeTo?: string; /** Text duotone colour pair (`a:duotone`). */ textDuotone?: { color1: string; color2: string; }; /** Text glow colour as hex string (`a:glow`). */ textGlowColor?: string; /** Text glow radius in px. */ textGlowRadius?: number; /** Text glow opacity (0-1). */ textGlowOpacity?: number; /** Text reflection enabled flag. */ textReflection?: boolean; /** Text reflection blur radius in px. */ textReflectionBlur?: number; /** Text reflection start opacity (0-1). */ textReflectionStartOpacity?: number; /** Text reflection end opacity (0-1). */ textReflectionEndOpacity?: number; /** Text reflection offset distance in px. */ textReflectionOffset?: number; /** * Text reflection fade direction (`a:rPr/a:effectLst/a:reflection/@fadeDir`) * in degrees. Mirrors `ShapeStyle.reflectionFadeDirection`. */ textReflectionFadeDirection?: number; /** * Text reflection horizontal scaling (`@sx`), same units as * `ShapeStyle.reflectionScaleX` (1000ths of a percent, e.g. 100000 = 100%). */ textReflectionScaleX?: number; /** Text reflection vertical scaling (`@sy`). See `ShapeStyle.reflectionScaleY`. */ textReflectionScaleY?: number; /** * Text reflection horizontal skew (`@kx`) in 60000ths of a degree. See * `ShapeStyle.reflectionSkewX`. */ textReflectionSkewX?: number; /** Text reflection vertical skew (`@ky`). See `ShapeStyle.reflectionSkewY`. */ textReflectionSkewY?: number; /** * Text reflection independent rotation (`@rot`) in degrees. See * `ShapeStyle.reflectionRotation`. */ textReflectionRotation?: number; /** Text reflection anchor (`@algn`). See `ShapeStyle.reflectionAlignment`. */ textReflectionAlignment?: 'tl' | 't' | 'tr' | 'l' | 'ctr' | 'r' | 'bl' | 'b' | 'br'; /** 3D extrusion/bevel settings on the text body. */ text3d?: Text3DStyle; /** 3D scene (camera + light rig) settings on the text body (`a:bodyPr/a:scene3d`). */ textBodyScene3d?: Pptx3DScene; /** Raw `a:scene3d` subtree used to preserve extensions and unmodelled children. */ textBodyScene3dXml?: XmlObject; /** * `a:bodyPr/a:flatTx` - an explicit "render this text flat" marker. `sp3d` * and `flatTx` are a mutually exclusive OOXML choice (`EG_Text3D`), so a * shape/run that overrides an inherited 3D text body with `` * carries no `text3d` of its own; without this explicit flag a later * inheritance merge has no signal to stop `text3d`/`textBodyScene3d` from * an ancestor (layout/master) leaking back in, the way `noFill` stops an * inherited fill. A renderer must short-circuit 3D-text application * whenever this is `true`, regardless of what `text3d`/`textBodyScene3d` * otherwise hold. */ flatText?: boolean; /** * Raw `` subtree captured from ``. Preserved verbatim so * authored extensions (e.g. content placeholders, custom application data) * survive a round-trip even though the engine doesn't interpret them. */ bodyPropertiesExtLstXml?: XmlObject; /** * Raw `` subtree captured from ``. Only meaningful on the * paragraph-level style (paragraphs propagate this via the first segment). */ paragraphPropertiesExtLstXml?: XmlObject; /** * Raw `` subtree captured from ``. Persisted verbatim on * save when present — covers run-level extensions the typed model doesn't * model (e.g. `a14:hiddenFill` and similar). */ runPropertiesExtLstXml?: XmlObject; /** * Raw `` XML node captured from ``. The schema permits * `defRPr` directly inside `pPr` so that paragraph defaults can specify the * end-paragraph run formatting; previously this was dropped on save. We * persist the parsed XML object so it round-trips verbatim. * * Only meaningful on the *first* segment of each paragraph (matches the * convention used for {@link bulletInfo} / {@link endParaRunProperties}). */ paragraphDefaultRunPropertiesXml?: XmlObject; } /** * Structured bullet metadata attached to the first {@link TextSegment} * of each paragraph. * * Describes how the paragraph bullet should render: character bullets * (`char`), auto-numbered lists (`autoNumType`), or picture bullets * (`imageRelId` / `imageDataUrl`). Set `none: true` when `a:buNone` * explicitly suppresses the bullet. * * @example * ```ts * // Simple character bullet: * const bullet: BulletInfo = { char: "•", color: "#333333" }; * * // Auto-numbered list starting at 1: * const numbered: BulletInfo = { * autoNumType: "arabicPeriod", * autoNumStartAt: 1, * }; * // => { char: "•", color: "#333333" } and { autoNumType: "arabicPeriod", autoNumStartAt: 1 } * ``` */ interface BulletInfo { /** Bullet character (e.g. "•", "-", "»") from `a:buChar`. */ char?: string; /** Auto-numbering type (e.g. "arabicPeriod", "romanUcPeriod") from `a:buAutoNum`. */ autoNumType?: string; /** Auto-numbering start value. */ autoNumStartAt?: number; /** * Auto-numbering ORDINAL OFFSET: the zero-based distance of this paragraph * within its own numbered list, such that * `autoNumStartAt + paragraphIndex` is the ordinal to render. Despite the * name it is NOT the paragraph's position in the text body; the two agree * only for a list that starts at the first paragraph and is never * interrupted. * * It has to be the offset rather than the raw position because every * consumer that re-derives a marker from `BulletInfo` alone (the renderer's * `resolveParagraphBullet`, the Markdown converter's `resolveListMarker`) * computes `autoNumStartAt + paragraphIndex`. The load path resolves the * real sequence itself, restarting the count after any paragraph that * interrupts the list, and publishes the offset here so those consumers * land on the same number. With the raw position they did not, and BOTH * markers were painted ("3.1. Item"), because the paragraph builder drops * the parsed marker segment only when the two strings agree. * * Runtime-only: derived at parse time and never serialized. OOXML has no * counterpart (`a:buAutoNum` carries only `@type` and `@startAt`), so the * writer neither reads nor emits it. */ paragraphIndex?: number; /** Bullet font family from `a:buFont`. */ fontFamily?: string; /** Bullet size as percentage of text font size from `a:buSzPct`. */ sizePercent?: number; /** Bullet size in points from `a:buSzPts`. */ sizePts?: number; /** Bullet color as hex string from `a:buClr`. */ color?: string; /** * Raw colour-choice XML captured from `` so that themed bullets * (`a:schemeClr`, `a:sysClr`, `a:prstClr`) round-trip with their original * identity rather than being flattened to `` on save. */ colorXml?: XmlObject; /** True when `a:buNone` explicitly suppresses bullets. */ none?: boolean; /** Picture bullet: relationship ID from `a:buBlip` → `a:blip[@r:embed]`. */ imageRelId?: string; /** Picture bullet: data URL of the embedded image. */ imageDataUrl?: string; /** * Raw `` XML captured at parse time. Carries the full blipFill * subtree (`a:tile`, `a:stretch`, `a:srcRect`, `a:blip > a:extLst`) so the * writer can emit the complete original definition rather than the bare * `a:blip[@r:embed]` mapping. When set, the writer prefers it over * {@link imageRelId} for emission. */ imageBlipFillXml?: XmlObject; /** When true, `` was specified — inherit the bullet font from * the run text, not from a buFont declaration. */ fontInherit?: boolean; /** When true, `` was specified — inherit the bullet colour from * the run text. */ colorInherit?: boolean; /** When true, `` was specified — inherit the bullet size from * the run text font size. */ sizeInherit?: boolean; } /** * A single text run within a paragraph. * * A text body is decomposed into an array of `TextSegment` objects, * each with its own style. Paragraph breaks are represented as * segments with `isParagraphBreak: true`. * * @example * ```ts * const segments: TextSegment[] = [ * { text: "Bold intro ", style: { bold: true, fontSize: 16 } }, * { text: "and normal text.", style: { fontSize: 16 } }, * { text: "", style: {}, isParagraphBreak: true }, * { text: "Second paragraph.", style: { fontSize: 14 } }, * ]; * // => 4 segments: 2 styled runs, 1 paragraph break, 1 normal run * ``` */ interface TextSegment { text: string; style: TextStyle; /** When this segment originated from an `a:fld` element, stores the field type (e.g. "slidenum", "datetime"). */ fieldType?: string; /** When this segment originated from an `a:fld` element, stores the field GUID. */ fieldGuid?: string; /** * Original attribute name used to author the field GUID — `'uuid'` for the * `a:fld/@uuid` form authored by some legacy producers, `'id'` for the * canonical `a:fld/@id` form. Preserved so the writer round-trips whichever * spelling the source used (PowerPoint accepts both). Defaults to `'id'` * on save when undefined. */ fieldGuidAttr?: 'uuid' | 'id'; /** * Raw per-field paragraph properties (`a:fld > a:pPr`). The schema permits * `pPr` inside an `a:fld` so the field can carry its own paragraph-level * formatting; preserved verbatim on save when present. */ fieldParagraphPropertiesXml?: XmlObject; /** Raw OMML XML node for equation segments (from `a14:m` / `m:oMathPara`). */ equationXml?: Record; /** * Optional equation number for numbered equations (e.g. "(1)", "(2.3)"). * When present, the equation is rendered centered with the number right-aligned. */ equationNumber?: string; /** Whether this segment represents a paragraph break rather than renderable text. */ isParagraphBreak?: boolean; /** * Whether this segment represents a soft line break (`a:br`) rather than * a paragraph terminator. Soft line breaks remain inside the same paragraph * but force a line wrap and may carry their own run properties. * * The renderer should treat the segment text as `"\n"` when present. */ isLineBreak?: true; /** * Raw `a:rPr` XML for an `a:br` (soft line break) segment, captured verbatim * during parse so the writer can re-emit attributes/colours/fonts that the * typed model doesn't represent. Only meaningful when {@link isLineBreak} * is `true`. */ breakRunProperties?: Record; /** Structured bullet info for the first segment of a paragraph. */ bulletInfo?: BulletInfo; /** * Outline level for the paragraph this segment starts (`a:p/@lvl`). * * Only meaningful on the first segment of a paragraph (matching the * convention used for {@link bulletInfo}). Stored as the raw OOXML * value (0 = top level, 1-8 = nested) and serialised back when non-zero. */ paragraphLevel?: number; /** * Raw `a:endParaRPr` XML node for the paragraph this segment starts. * * Captured verbatim on parse so attributes and child colours/fonts that * the typed model doesn't represent survive a round-trip. Only meaningful * on the first segment of a paragraph. */ endParaRunProperties?: Record; /** * Per-paragraph properties (alignment, spacing, margins, indent, tab stops, * rtl) authored on this paragraph's own `a:pPr` (#69). Only meaningful on * the first segment of a paragraph. When present, the writer emits these * per paragraph instead of collapsing one shape-level pPr onto every * paragraph. Only the paragraph-geometry keys of {@link TextStyle} are * populated; unrelated fields fall back to the shape-level style. */ paragraphProperties?: TextStyle; /** * Phonetic annotation text from `a:ruby > a:rt` (e.g. furigana, pinyin). * When present, the renderer should wrap the base text with an HTML `` tag. */ rubyText?: string; /** * Ruby text alignment from `a:rubyPr > @val` attribute. * Values: "ctr" (center), "l" (left), "r" (right), "dist" (distribute), "distCat", "distLetter". * @default "ctr" */ rubyAlignment?: string; /** * Ruby text font size as a percentage of the base text font size * from `a:rubyPr/@hps` (half-point size) or inferred from rt run font size. * Stored in **points** for consistency with `TextStyle.fontSize`. */ rubyFontSize?: number; /** * Style for the ruby (phonetic) text run, parsed from `a:rt > a:r > a:rPr`. * Used by the renderer to apply font family, colour, etc. to the `` element. */ rubyStyle?: TextStyle; } //#endregion //#region src/core/types/element-base.d.ts /** * Properties shared by **every** element on a slide. * * Position and size are in pixels (converted from EMU at parse time). * Optional properties apply to subsets of elements or may be absent in * the original OOXML. * * @example * ```ts * const base: PptxElementBase = { * id: "el_001", * x: 100, y: 50, * width: 400, height: 200, * rotation: 15, * opacity: 0.9, * }; * // => satisfies PptxElementBase * ``` */ interface PptxElementBase { id: string; /** * The shape's native OOXML id from `p:cNvPr/@id` (an unsigned integer, as a * string), captured on load. Distinct from {@link id}, which is a synthetic * positional identity (`${slidePath}-shape-${index}`) the loader assigns for * selection / undo / template tracking. Animations target shapes by this * native id (`p:spTgt/@spid`), so it is the stable key used to reconcile an * animation to the element it animates across a save/reload round trip. * Absent on SDK-created elements until one is minted at save time. */ shapeId?: string; /** Element name from `cNvPr/@name`. Used for morph transition matching via the `!!` naming convention. */ name?: string; /** * `p:nvSpPr/p:nvPr/p:ph/@type` (lower-cased) when the shape is a placeholder: * `title`, `ctrtitle`, `body`, `subtitle`, `ftr`, `dt`, `sldnum`, ... * * Captured on load so consumers can tell a footer placeholder from a text box * without re-walking `rawXml`. Absent on non-placeholder shapes and on * SDK-created elements. */ placeholderType?: string; /** * `p:nvSpPr/p:nvPr/p:ph/@sz` (lower-cased): `"full"`, `"half"`, or * `"quarter"`. Captured on load for round-trip completeness. Per * ECMA-376 §19.3.1.36 (CT_Placeholder) this size hint is only meaningful * when NO `a:xfrm` exists anywhere in the placeholder's inheritance * chain (slide -> layout -> master); every real-world corpus placeholder * that carries `@sz` already has an explicit `a:xfrm` at the master * level, so no renderer currently derives a size from this field. */ placeholderSz?: string; /** * `p:nvSpPr/p:nvPr/p:ph/@orient` (only `"vert"` is meaningful per * `ST_Direction`). Captured on load for round-trip completeness. In * practice every placeholder observed with `orient="vert"` also carries * an explicit `a:bodyPr/@vert`, which already drives vertical-text * rendering, so this field is not currently read by any renderer. */ placeholderOrient?: 'vert'; x: number; y: number; width: number; height: number; rotation?: number; /** Skew along the X axis in degrees (parsed from `@_skewX` in 1/60000ths of a degree). */ skewX?: number; /** Skew along the Y axis in degrees (parsed from `@_skewY` in 1/60000ths of a degree). */ skewY?: number; flipHorizontal?: boolean; flipVertical?: boolean; /** Whether this element is hidden (used by the Elements Panel visibility toggle). */ hidden?: boolean; /** Element-level opacity (0-1). */ opacity?: number; rawXml?: XmlObject; /** Shape-level click action (from `a:hlinkClick` on `p:cNvPr`). */ actionClick?: PptxAction; /** Shape-level hover action (from `a:hlinkHover` on `p:cNvPr`). */ actionHover?: PptxAction; /** Shape lock attributes parsed from `p:cNvSpPr/a:spLocks`. */ locks?: PptxShapeLocks; /** * Opaque `` children captured from the shape's `` whose * URI is not recognised by a typed extractor (hidden fill/line, image * effects, …). Preserved verbatim and re-emitted on save so unknown * vendor extensions survive a round-trip. * * Mirrors the existing `effectDagXml` / `endParaRunProperties` raw-XML * preservation pattern. */ extLstXml?: XmlObject[]; } /** * Text content mixin — present on text boxes and shapes. * * Shapes can contain text overlaid on the shape geometry, so both * `TextPptxElement` and `ShapePptxElement` extend this interface. * * @example * ```ts * const props: PptxTextProperties = { * text: "Hello World", * textStyle: { fontSize: 24, bold: true, color: "#333333" }, * }; * // => satisfies PptxTextProperties * ``` */ interface PptxTextProperties { text?: string; textStyle?: TextStyle; /** Rich text segments with individual styling. */ textSegments?: TextSegment[]; /** Per-paragraph indentation (marginLeft, indent) for multi-level bullet support. */ paragraphIndents?: Array<{ marginLeft?: number; indent?: number; }>; /** Placeholder prompt text inherited from layout/master (e.g. "Click to add title"). Shown as a greyed-out hint when the shape has no user-entered text. */ promptText?: string; /** * The string {@link text} was INHERITED from, when this is a header / footer / * date / slide-number placeholder whose own body the file leaves empty. * * PowerPoint keeps the footer string on the slide master and writes each * slide's copy of the `ftr` placeholder empty, so the empty body means * "render the master's footer here". Rendering needs the resolved string, but * SAVING it into the slide would pin that slide to today's master text and * silently detach it from the Header & Footer dialog. The save writer * therefore leaves the authored empty body alone while `text` still equals * this value, and writes a genuine per-slide override once it does not. */ inheritedPlaceholderText?: string; /** Linked text box chain ID from `a:bodyPr > a:linkedTxbx/@id` or `a:txbx > a:linkedTxbx/@id`. Text overflows from one linked frame to the next. */ linkedTxbxId?: number; /** Sequence number within a linked text box chain (0-based). */ linkedTxbxSeq?: number; } /** * Shape styling & geometry mixin — present on shapes, connectors, and images. * * @example * ```ts * const props: PptxShapeProperties = { * shapeType: "roundRect", * shapeStyle: { fillColor: "#0055AA", strokeWidth: 2 }, * shapeAdjustments: { adj: 16667 }, * }; * // => satisfies PptxShapeProperties * ``` */ interface PptxShapeProperties { shapeStyle?: ShapeStyle; /** Preset geometry name, e.g. "rect", "ellipse", "roundRect". */ shapeType?: string; /** Geometry adjustment values, e.g. `{ adj: 16667 }`. */ shapeAdjustments?: Record; /** Adjustment handles for interactive shape modification (yellow diamond handles). */ adjustmentHandles?: GeometryAdjustmentHandle[]; } /** * Text styling for a single indent level (0–8) inside a placeholder’s * `a:lstStyle`. * * Used during placeholder inheritance to fill in defaults for font, * bullet, and spacing properties the slide element does not override. * * @example * ```ts * const level0: PlaceholderTextLevelStyle = { * fontSize: 32, * bold: true, * bulletChar: "•", * }; * // => satisfies PlaceholderTextLevelStyle * ``` */ interface PlaceholderTextLevelStyle { fontFamily?: string; fontSize?: number; bold?: boolean; italic?: boolean; color?: string; /** * The `a:defRPr/a:solidFill` node this level's {@link color} came from. * * Master and layout text styles are parsed and cached before any slide is, * so a scheme alias such as `tx1` was resolved through the map that was * active then. A slide carrying `p:clrMapOvr` routes the same alias * somewhere else, so the alias has to be resolved again against the slide * that is inheriting it; {@link color} is only the reading taken at parse * time. Absent when the level declares no colour, or declares a literal one. */ colorChoiceXml?: XmlObject; bulletChar?: string; bulletAutoNumType?: string; bulletFontFamily?: string; bulletSizePercent?: number; /** Bullet colour from `a:buClr` as hex string. */ bulletColor?: string; /** Bullet size in points from `a:buSzPts`. */ bulletSizePts?: number; /** True when `a:buNone` is present at this level. */ bulletNone?: boolean; marginLeft?: number; indent?: number; alignment?: string; lineSpacing?: number; lineSpacingExactPt?: number; spaceBefore?: number; spaceAfter?: number; } //#endregion //#region src/core/types/chart-axis.d.ts /** Tick-mark placement from ChartML `ST_TickMark`. */ type PptxChartTickMark = 'cross' | 'in' | 'none' | 'out'; /** Typed axis tick and category/date label controls. */ interface PptxChartAxisLabelFormatting { /** Primary and secondary tick-mark placement. */ majorTickMark?: PptxChartTickMark; minorTickMark?: PptxChartTickMark; /** Tick-label position from ChartML `ST_TickLblPos`. */ tickLblPos?: 'high' | 'low' | 'nextTo' | 'none'; /** Automatic category/date axis behavior (`c:auto`). */ auto?: boolean; /** Category-axis label alignment (`c:lblAlgn`). */ labelAlignment?: 'ctr' | 'l' | 'r'; /** Category/date label distance, from 0 through 1000 percent. */ labelOffset?: number; /** Number of category/date labels between rendered labels. */ tickLabelSkip?: number; /** Number of category/date tick positions between major tick marks. */ tickMarkSkip?: number; /** Suppress multi-level category labels (`c:noMultiLvlLbl`). */ noMultiLevelLabels?: boolean; } //#endregion //#region src/core/types/chart-pivot-format.d.ts interface PptxChartPivotFormat { index: number; shapePropertiesXml?: XmlObject | null; markerXml?: XmlObject | null; dataLabelXml?: XmlObject | null; extensionListXml?: XmlObject | null; rawXml?: XmlObject; } /** Editable classic ChartML `c:pivotFmts` collection. */ interface PptxChartPivotFormats { formats: PptxChartPivotFormat[]; rawXml?: XmlObject; } //#endregion //#region src/core/types/chart-pivot-source.d.ts /** Editable classic ChartML `c:pivotSource` metadata. */ interface PptxChartPivotSource { /** Pivot table reference stored as `c:name` text. */ name: string; /** Required unsigned format identifier stored in `c:fmtId/@val`. */ formatId: number; /** Internal source subtree used to preserve extensions and foreign markup. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/chart-print-settings.d.ts /** Headers and footers used when a classic ChartML chart is printed. */ interface PptxChartPrintHeaderFooter { oddHeader?: string; oddFooter?: string; evenHeader?: string; evenFooter?: string; firstHeader?: string; firstFooter?: string; alignWithMargins?: boolean; differentOddEven?: boolean; differentFirst?: boolean; /** Original subtree retained for foreign attributes and extension children. */ rawXml?: unknown; } /** Required page margins from ChartML `c:pageMargins`, measured in inches. */ interface PptxChartPageMargins { left: number; right: number; top: number; bottom: number; header: number; footer: number; /** Original leaf retained for foreign attributes. */ rawXml?: unknown; } /** Printer page configuration from ChartML `c:pageSetup`. */ interface PptxChartPageSetup { paperSize?: number; firstPageNumber?: number; orientation?: 'default' | 'portrait' | 'landscape'; blackAndWhite?: boolean; draft?: boolean; useFirstPageNumber?: boolean; horizontalDpi?: number; verticalDpi?: number; copies?: number; /** Original leaf retained for foreign attributes. */ rawXml?: unknown; } /** Editable `c:printSettings` content from a classic ChartML chart space. */ interface PptxChartPrintSettings { headerFooter?: PptxChartPrintHeaderFooter | null; pageMargins?: PptxChartPageMargins | null; pageSetup?: PptxChartPageSetup | null; /** `null` removes the legacy header/footer drawing relationship element. */ legacyDrawingHeaderFooterRelationshipId?: string | null; /** Original subtree retained for unknown and extension content. */ rawXml?: unknown; } //#endregion //#region src/core/types/chart-protection.d.ts /** Editable classic ChartML `c:protection` settings. */ interface PptxChartProtection { /** Prevent editing the chart object. */ chartObject?: boolean | null; /** Prevent editing the chart data. */ data?: boolean | null; /** Prevent editing chart formatting. */ formatting?: boolean | null; /** Prevent selecting chart elements. */ selection?: boolean | null; /** Prevent chart user-interface operations. */ userInterface?: boolean | null; /** Internal source subtree used to preserve foreign markup during edits. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/chart-user-shapes.d.ts /** * Types for chart drawing-overlay shapes (`c:userShapes`). * * A chart's `c:userShapes` element carries an `r:id` that references a * separate drawing part (`ppt/drawings/drawingN.xml`) whose root is a * `c:userShapes` element populated with `cdr:relSizeAnchor` / * `cdr:absSizeAnchor` wrappers around `sp` / `pic` / `cxnSp` shapes drawn on * top of the chart plot. These interfaces describe the parsed, renderable * overlay model. The raw reference is preserved separately on * {@link PptxChartData.userShapesXml} for verbatim round-trip save; this model * is render-only. * * @module pptx-types/chart-user-shapes */ /** A single paragraph of overlay-shape text with light formatting. */ interface PptxChartUserShapeParagraph { /** Joined run text of the paragraph. */ text: string; /** Font size in points (`a:rPr/@sz` divided by 100), when present. */ fontSize?: number; /** Whether the first run is bold (`a:rPr/@b`). */ bold?: boolean; /** Whether the first run is italic (`a:rPr/@i`). */ italic?: boolean; /** Resolved run colour hex (e.g. `"#FF0000"`), when present. */ color?: string; /** Paragraph alignment (`a:pPr/@algn`): left / centre / right. */ align?: 'l' | 'ctr' | 'r'; } /** * A parsed chart-overlay shape positioned by a drawing anchor. * * Position is expressed as chart-relative fractions in {@link from}. For a * `relSizeAnchor` the opposite corner is {@link to} (also fractional); for an * `absSizeAnchor` the extent is {@link ext} in EMU. */ interface PptxChartUserShape { /** Shape kind: text/preset shape, connector, or picture. */ kind: 'sp' | 'cxnSp' | 'pic'; /** Anchor kind that positioned the shape. */ anchor: 'rel' | 'abs'; /** Top-left corner as chart-relative fractions (0-1). */ from: { x: number; y: number; }; /** Bottom-right corner as chart-relative fractions (0-1); relSizeAnchor only. */ to?: { x: number; y: number; }; /** Extent in EMU (cx, cy); absSizeAnchor only. */ ext?: { cx: number; cy: number; }; /** Preset geometry name (`a:prstGeom/@prst`), defaulting to `"rect"`. */ prst?: string; /** Resolved solid-fill hex colour, when present. */ fill?: string; /** Resolved line/stroke hex colour, when present. */ stroke?: string; /** Line width in points (`a:ln/@w` divided by 12700), when present. */ strokeWidth?: number; /** Text paragraphs of the shape's `txBody`, when present. */ paragraphs?: PptxChartUserShapeParagraph[]; } //#endregion //#region src/core/types/chart.d.ts /** * Supported chart type discriminators. * * @example * ```ts * const type: PptxChartType = "bar"; * // => "bar" — one of: "bar" | "line" | "pie" | "doughnut" | "area" | "scatter" | … * ``` */ type PptxChartType = 'bar' | 'line' | 'pie' | 'ofPie' | 'doughnut' | 'area' | 'scatter' | 'bubble' | 'radar' | 'stock' | 'bar3D' | 'line3D' | 'pie3D' | 'area3D' | 'surface' | 'histogram' | 'waterfall' | 'funnel' | 'treemap' | 'sunburst' | 'boxWhisker' | 'regionMap' | 'combo' | 'unknown'; /** * Bar series direction (OOXML `ST_BarDir`): `"col"` is a vertical column * chart, `"bar"` a horizontal bar chart. * * @example * ```ts * const dir: PptxChartBarDirection = "col"; * // => "col" - one of: "col" | "bar" * ``` */ type PptxChartBarDirection = 'col' | 'bar'; /** * Supported trendline regression types. * * @example * ```ts * const type: PptxChartTrendlineType = "linear"; * // => "linear" — one of: "linear" | "exponential" | "logarithmic" | "polynomial" | "power" | "movingAvg" * ``` */ type PptxChartTrendlineType = 'linear' | 'exponential' | 'logarithmic' | 'polynomial' | 'power' | 'movingAvg'; /** * Configuration for a chart trendline (regression line). * * @example * ```ts * const trendline: PptxChartTrendline = { * trendlineType: "linear", * displayEq: true, * displayRSq: true, * color: "#FF0000", * }; * // => satisfies PptxChartTrendline * ``` */ interface PptxChartTrendline { trendlineType: PptxChartTrendlineType; name?: string; order?: number; period?: number; forward?: number; backward?: number; intercept?: number; displayRSq?: boolean; displayEq?: boolean; color?: string; label?: PptxChartTrendlineLabel | null; } /** Typed, commonly edited properties of `c:trendlineLbl`. */ interface PptxChartTrendlineLabel { layout?: PptxChartManualLayout; numberFormatCode?: string; sourceLinked?: boolean; } /** Error-bar direction axis. */ type PptxChartErrBarDir = 'x' | 'y'; /** Error-bar display type (both sides, negative only, or positive only). */ type PptxChartErrBarType = 'both' | 'minus' | 'plus'; /** * How the error-bar value is calculated. * * @example * ```ts * const valType: PptxChartErrValType = "percentage"; * // => "percentage" — one of: "cust" | "fixedVal" | "percentage" | "stdDev" | "stdErr" * ``` */ type PptxChartErrValType = 'cust' | 'fixedVal' | 'percentage' | 'stdDev' | 'stdErr'; /** * Error bars for a chart series. * * @example * ```ts * const bars: PptxChartErrBars = { * direction: "y", * barType: "both", * valType: "percentage", * val: 5, * }; * // => satisfies PptxChartErrBars * ``` */ interface PptxChartErrBars { direction: PptxChartErrBarDir; barType: PptxChartErrBarType; valType: PptxChartErrValType; val?: number; customPlus?: number[]; customMinus?: number[]; noEndCap?: boolean; color?: string; } /** * Visibility flags for the chart data table (axes + legend keys). * * @example * ```ts * const dt: PptxChartDataTable = { * showHorzBorder: true, * showVertBorder: true, * showOutline: true, * showKeys: true, * }; * // => satisfies PptxChartDataTable * ``` */ interface PptxChartDataTable { showHorzBorder?: boolean; showVertBorder?: boolean; showOutline?: boolean; showKeys?: boolean; /** Table border/fill formatting (`c:dTable/c:spPr`). */ spPr?: PptxChartShapeProps; /** * Cell text defaults (`c:dTable/c:txPr/a:p/a:pPr/a:defRPr`). Reuses the same * shape as a legend entry's text override since both are a flat paragraph * default-run-property style (size/bold/italic/font/colour). */ txPr?: PptxChartLegendTextStyle; } /** * Line appearance for chart helper lines (drop lines, hi-low lines). * * @example * ```ts * const style: PptxChartLineStyle = { * color: "#AAAAAA", * width: 1, * dashStyle: "dash", * }; * // => satisfies PptxChartLineStyle * ``` */ interface PptxChartLineStyle { color?: string; width?: number; dashStyle?: string; } /** Marker symbol types for line/scatter chart data points. */ type PptxChartMarkerSymbol = 'circle' | 'dash' | 'diamond' | 'dot' | 'none' | 'picture' | 'plus' | 'square' | 'star' | 'triangle' | 'x' | 'auto'; /** * `ST_ScatterStyle` (ECMA-376 §21.2.3.40): how a scatter chart joins its points. * `line`/`lineMarker` connect them with straight segments, `smooth`/ * `smoothMarker` with a bezier, `marker`/`none` not at all. */ type PptxChartScatterStyle = 'none' | 'line' | 'lineMarker' | 'marker' | 'smooth' | 'smoothMarker'; /** Shape properties extracted from c:spPr for chart formatting. */ interface PptxChartShapeProps { fillColor?: string; strokeColor?: string; strokeWidth?: number; /** Line dash style (a:prstDash/@val), e.g. 'solid', 'dash', 'dot', 'lgDash'. */ strokeDashStyle?: string; } /** Up/down bar formatting on line and stock charts (`c:upDownBars`). */ interface PptxChartUpDownBars { /** Gap between bars as a percentage, constrained to 0 through 500. */ gapWidth?: number; upBars?: PptxChartShapeProps; downBars?: PptxChartShapeProps; } /** Marker appearance on a chart series or data point. */ interface PptxChartMarker { symbol: PptxChartMarkerSymbol; /** Marker size in points, constrained by ST_MarkerSize to 2 through 72. */ size?: number; spPr?: PptxChartShapeProps; } /** Per-data-point formatting override (c:dPt). */ interface PptxChartDataPoint { idx: number; spPr?: PptxChartShapeProps; explosion?: number; invertIfNegative?: boolean; marker?: PptxChartMarker; /** Render a bubble-chart point with a 3-D appearance. */ bubble3D?: boolean; } /** Schema values accepted by `c:dLblPos`. */ type PptxChartDataLabelPosition = 'bestFit' | 'b' | 'ctr' | 'inBase' | 'inEnd' | 'l' | 'outEnd' | 'r' | 't'; /** Individual data label override (c:dLbl). */ interface PptxChartDataLabel { idx: number; /** Suppress this data point's automatically generated label. */ deleted?: boolean; showVal?: boolean; showCatName?: boolean; showSerName?: boolean; showPercent?: boolean; showLegendKey?: boolean; showBubbleSize?: boolean; position?: PptxChartDataLabelPosition; text?: string; separator?: string; showLeaderLines?: boolean; } /** Axis number format. */ interface PptxChartAxisNumFmt { formatCode: string; sourceLinked?: boolean; } /** Typed contents of a value-axis display-unit label (`c:dispUnitsLbl`). */ interface PptxChartDisplayUnitsLabel { /** Literal label text. Omit to preserve the source text subtree. */ text?: string; /** Manual label placement. `null` removes only the manual layout. */ layout?: PptxChartManualLayout | null; /** Label shape formatting. `null` removes `c:spPr`. */ spPr?: PptxChartShapeProps | null; } /** Axis formatting for category, value, or date axes. */ interface PptxChartAxisFormatting extends PptxChartAxisLabelFormatting { axisType: 'catAx' | 'valAx' | 'dateAx' | 'serAx'; /** Axis position: "b" (bottom), "l" (left), "r" (right), "t" (top). */ axPos?: 'b' | 'l' | 'r' | 't'; /** Unique axis identifier (c:axId/@val) used to link series to axes. */ axisId?: number; /** Cross-axis identifier — the axis this axis crosses. */ crossAxisId?: number; /** Automatic crossing mode (`c:crosses`). Mutually exclusive with `crossesAt`. */ crosses?: 'autoZero' | 'min' | 'max'; /** Explicit crossing value (`c:crossesAt`). Units depend on the axis type. */ crossesAt?: number; /** Whether a value axis crosses between or at category tick marks. */ crossBetween?: 'between' | 'midCat'; numFmt?: PptxChartAxisNumFmt; titleText?: string; spPr?: PptxChartShapeProps; fontFamily?: string; fontSize?: number; fontBold?: boolean; fontColor?: string; /** Whether major gridlines are present (`c:majorGridlines`). */ majorGridlines?: boolean; /** Whether minor gridlines are present (`c:minorGridlines`). */ minorGridlines?: boolean; majorGridlinesSpPr?: PptxChartShapeProps; minorGridlinesSpPr?: PptxChartShapeProps; /** Minimum axis value override (c:min/@val). */ min?: number; /** Maximum axis value override (c:max/@val). */ max?: number; /** Axis value direction (`c:scaling/c:orientation/@val`). */ orientation?: 'minMax' | 'maxMin'; /** Whether the axis is deleted/hidden (c:delete/@val). */ deleted?: boolean; /** * Display units for value axis (c:dispUnits/c:builtInUnit/@val). * When set to 'custom', the actual divisor is in {@link displayUnitsValue}. */ displayUnits?: 'hundreds' | 'thousands' | 'tenThousands' | 'hundredThousands' | 'millions' | 'tenMillions' | 'hundredMillions' | 'billions' | 'trillions' | 'custom'; /** Custom display unit divisor value (c:dispUnits/c:custUnit/@val). Only used when displayUnits is 'custom'. */ displayUnitsValue?: number; /** * Display-unit label contents (`c:dispUnits/c:dispUnitsLbl`). A string is * retained as a compatibility shorthand for `{ text: string }`; `null` * explicitly removes the label. */ displayUnitsLabel?: string | PptxChartDisplayUnitsLabel | null; /** Whether logarithmic scaling is enabled (presence of c:scaling/c:logBase). */ logScale?: boolean; /** Logarithmic base value (c:scaling/c:logBase/@val), typically 10 or e. */ logBase?: number; /** Major-unit interval between primary tick marks (c:majorUnit/@val). */ majorUnit?: number; /** Minor-unit interval between secondary tick marks (c:minorUnit/@val). */ minorUnit?: number; /** Calendar unit used to interpret date-axis serial values. */ baseTimeUnit?: 'days' | 'months' | 'years'; majorTimeUnit?: 'days' | 'months' | 'years'; minorTimeUnit?: 'days' | 'months' | 'years'; } /** 3D wall or floor element formatting. */ interface PptxChart3DSurface { thickness?: number; spPr?: PptxChartShapeProps; } /** * One colour band for a surface chart (`c:bandFmts/c:bandFmt`, * ECMA-376 §21.2.2.19 / CT_BandFmt). `index` is the band's position * (`c:idx/@val`) among the value axis's major-unit bands, in authored order. */ interface PptxChartBandFmt { index: number; spPr?: PptxChartShapeProps; } /** Office 2016 ChartEx box-and-whisker series layout options. */ interface PptxChartBoxWhiskerOptions { quartileMethod?: 'inclusive' | 'exclusive'; showMeanLine?: boolean; showMeanMarker?: boolean; /** Show non-outlier (inner) data points. */ showInnerPoints?: boolean; showOutlierPoints?: boolean; } /** Office 2016 ChartEx histogram and Pareto series layout options. */ interface PptxChartHistogramOptions { /** Maps to `clusteredColumn` for histogram columns or `paretoLine`. */ layout?: 'histogram' | 'pareto'; /** Exactly one of binSize and binCount is emitted by the ChartEx writer. */ binSize?: number; binCount?: number; intervalClosed?: 'l' | 'r'; underflow?: number | 'auto'; overflow?: number | 'auto'; } /** Office 2016 ChartEx waterfall series layout options. */ interface PptxChartWaterfallOptions { /** Zero-based data point indexes rendered as absolute subtotal or total bars. */ subtotalIndices?: number[]; /** Whether connector lines are visible between adjacent bars. */ connectorLines?: boolean; } /** Office 2016 ChartEx geographic series dimensions and layout options. */ interface PptxChartRegionMapOptions { /** Optional provider entity identifiers aligned with categories and values. */ entityIds?: string[]; /** Original `cx:pt/@idx` values for category points. */ categorySourceIndices?: number[]; /** Original `cx:pt/@idx` values for colour-value points. */ valueSourceIndices?: number[]; /** Original `cx:pt/@idx` values for entity-ID points. */ entityIdSourceIndices?: number[]; regionLabelLayout?: 'none' | 'bestFitOnly' | 'showAll'; projectionType?: 'mercator' | 'miller' | 'robinson' | 'albers'; viewedRegionType?: 'dataOnly' | 'postalCode' | 'county' | 'state' | 'countryRegion' | 'countryRegionList' | 'world'; cultureLanguage?: string; /** ISO-3166-1 alpha-2 region code. */ cultureRegion?: string; attribution?: string; /** Opaque authored provider cache under `cx:geography/cx:geoCache`. */ geographyCache?: XmlObject; } /** Layout for parent category labels in a hierarchical ChartEx treemap. */ type PptxChartParentLabelLayout = 'none' | 'banner' | 'overlapping'; /** Per-series layout options for an Office 2016+ ChartEx treemap. */ interface PptxChartTreemapOptions { parentLabelLayout?: PptxChartParentLabelLayout; } /** * A single data series within a chart. * * @example * ```ts * const series: PptxChartSeries = { * name: "Revenue", * values: [100, 120, 140], * color: "#4F81BD", * trendlines: [{ trendlineType: "linear" }], * }; * // => satisfies PptxChartSeries * ``` */ interface PptxChartSeries { name: string; values: number[]; /** * Per-series x values from `c:ser/c:xVal` (scatter and bubble series only). * * Every `CT_ScatterSer` / `CT_BubbleSer` carries its OWN `c:xVal`, so two * series in one scatter chart routinely plot against different x ranges (the * normal case for measurement data). Reading the x values off the first * series and reusing them everywhere plotted every series against series 1's * x axis. Absent for category-axis chart kinds, where * {@link PptxChartData.categories} is the x axis. */ xValues?: number[]; /** * Per-series bubble sizes from `c:ser/c:bubbleSize` (bubble series only), * aligned index-for-index with {@link values}. * * `CT_BubbleSer` carries x, y AND size, so a one-series bubble chart is fully * specified. Absent when the source omits `c:bubbleSize`. */ bubbleSizes?: number[]; /** * Series-level data-label content flags from `c:ser/c:dLbls`. * * PowerPoint writes the flags a user picks in "Format Data Labels" onto the * SERIES, and leaves the chart-type-level `c:dLbls` all-zero, so reading only * the chart-level group reports "show nothing" for a chart that visibly shows * percentages. These override {@link PptxChartStyle.dataLabels}. */ dataLabelOptions?: PptxChartDataLabelOptions; /** * Whether the series line is explicitly suppressed * (`c:ser/c:spPr/a:ln/a:noFill`). Line-drawn kinds (line, scatter, radar) * use this to decide whether to draw a connecting line at all; a marker-only * scatter is authored as `scatterStyle="lineMarker"` PLUS this flag, never by * changing the scatter style. */ lineNoFill?: boolean; /** * Blank-value mask aligned index-for-index with {@link values}: `true` marks * a category whose numeric cache point (`c:numCache/c:pt`) was absent or * empty, i.e. a genuine blank rather than a real `0`. Present only when the * source series actually contains blanks; when set, blank slots in * {@link values} carry `0` as a placeholder. Renderers honour * `c:dispBlanksAs` (gap / zero / span) using this mask. */ blanks?: boolean[]; /** * ECMA-376 number-format code for this series' values, resolved from * `c:ser/c:dLbls/c:numFmt/@formatCode` and falling back to the value cache's * own `c:numCache/c:formatCode` (which is what `@sourceLinked="1"` means). * Data labels render through it: a percentage series caches fractions, so * without the code `0.52` reaches the label where PowerPoint shows `52%`. */ numberFormat?: string; color?: string; trendlines?: PptxChartTrendline[]; errBars?: PptxChartErrBars[]; dataPoints?: PptxChartDataPoint[]; marker?: PptxChartMarker; dataLabels?: PptxChartDataLabel[]; explosion?: number; /** * Series-level `c:invertIfNegative`: when true, bar/column data points with a * negative value are drawn with an inverted (lightened) fill. A per-point * `c:dPt/c:invertIfNegative` overrides this for that point. Absent when the * source XML omits the flag. */ invertIfNegative?: boolean; /** * Whether this line/scatter series is drawn with bezier smoothing * (`c:ser/c:smooth/@val`). Absent when the source XML omits `c:smooth`. */ smooth?: boolean; /** Axis ID this series is plotted against (links to PptxChartAxisFormatting.axisId). */ axisId?: number; /** * Per-series chart type, used for combo charts where individual series are * plotted with different chart types (e.g. a bar series and a line series in * the same chart). Maps to the OOXML chart-type container that holds the * series (`c:barChart`, `c:lineChart`, etc.). Omitted for single-type charts, * where the chart-level {@link PptxChartData.chartType} applies to every * series. */ seriesChartType?: PptxChartType; boxWhiskerOptions?: PptxChartBoxWhiskerOptions; histogramOptions?: PptxChartHistogramOptions; waterfallOptions?: PptxChartWaterfallOptions; regionMapOptions?: PptxChartRegionMapOptions; treemapOptions?: PptxChartTreemapOptions; } /** * Chart-level data-label options (`c:dLbls` directly under a chart-type * container, applying to every series). Mirrors the OOXML `c:show*` flags * and `c:dLblPos`. */ interface PptxChartDataLabelOptions { /** Show the numeric value (`c:showVal`). */ showValue?: boolean; /** Show the category name (`c:showCatName`). */ showCategory?: boolean; /** Show the series name (`c:showSerName`). */ showSeriesName?: boolean; /** Show the percentage (`c:showPercent`, pie/doughnut). */ showPercent?: boolean; /** Show the legend key swatch (`c:showLegendKey`). */ showLegendKey?: boolean; /** Show bubble size (`c:showBubbleSize`). */ showBubbleSize?: boolean; /** Text placed between combined label components (`c:separator`). */ separator?: string; /** Show leader lines where supported (`c:showLeaderLines`). */ showLeaderLines?: boolean; /** * Label position (`c:dLblPos`). Valid values depend on the chart type * (`ctr`, `inEnd`, `inBase`, `outEnd`, `bestFit`, `l`, `r`, `t`, `b`). * Omit to let PowerPoint use the type default. */ position?: PptxChartDataLabelPosition; } /** Typed text defaults for a single chart legend entry. */ interface PptxChartLegendTextStyle { fontFamily?: string; fontSize?: number; bold?: boolean; italic?: boolean; color?: string; } /** Per-series legend entry override (`c:legendEntry`). */ interface PptxChartLegendEntry { index: number; deleted?: boolean; textStyle?: PptxChartLegendTextStyle; } /** * Style / formatting metadata for a chart. * * @example * ```ts * const style: PptxChartStyle = { * styleId: 2, * hasLegend: true, * legendPosition: "b", * hasDataLabels: true, * }; * // => satisfies PptxChartStyle * ``` */ interface PptxChartStyle { /** Chart style index from `c:style/@val`. */ styleId?: number; /** Whether the chart has a visible legend. */ hasLegend?: boolean; /** Legend position (t, b, l, r, tr). */ legendPosition?: string; /** Per-series visibility and text-style overrides. */ legendEntries?: PptxChartLegendEntry[]; /** Whether the chart has a title. */ hasTitle?: boolean; /** Whether gridlines are visible. */ hasGridlines?: boolean; /** * Chart-area fill from `c:chartSpace/c:spPr`: a resolved colour, or the * literal `'none'` when the source declares ``. Absent when the * chart says nothing, in which case the renderer picks its own default. * PowerPoint decks routinely set `a:noFill` so the chart floats on the slide * background; painting a panel behind it boxes the chart in. */ chartAreaFill?: string; /** Plot-area fill from `c:plotArea/c:spPr`. See {@link chartAreaFill}. */ plotAreaFill?: string; /** Whether data labels are shown. */ hasDataLabels?: boolean; /** Chart-level data-label content/position options (when `hasDataLabels`). */ dataLabels?: PptxChartDataLabelOptions; } /** * External data source reference for a chart (c:externalData). * * Charts can reference an external Excel workbook via a relationship ID * that points to an external file (TargetMode="External"). The * `autoUpdate` flag indicates whether the chart should refresh its * cached data from the external source on open. * * @example * ```ts * const ext: PptxExternalData = { * relId: "rId2", * targetPath: "file:///C:/Data/budget.xlsx", * autoUpdate: true, * }; * // => satisfies PptxExternalData * ``` */ interface PptxExternalData { /** Relationship ID referencing the external data source in the chart .rels. */ relId: string; /** Resolved external file path or URL from the relationship target. */ targetPath?: string; /** Whether to auto-update data from the external source on open. */ autoUpdate?: boolean; /** Raw binary data of the embedded xlsx workbook (from ppt/embeddings/). */ embeddedWorkbookData?: Uint8Array; } /** * Options specific to the OOXML "Pie of Pie" / "Bar of Pie" chart * (`c:ofPieChart`, ECMA-376 §21.2.2.126 / CT_OfPieChart). * * The primary discriminator is {@link ofPieType}: `"pie"` produces a * pie-of-pie chart whose secondary plot is itself a pie, while `"bar"` * produces a bar-of-pie chart whose secondary plot is a horizontal bar. * * - {@link splitType} chooses the split rule. * - {@link splitPos} is the threshold value used by `pos`/`val`/`percent`. * - {@link secondPieSize} controls the secondary plot's size (5–200%). * - {@link serLines} toggles the leader lines connecting the plots. * - {@link gapWidth} is the gap between the plots in percent (0–500). */ interface PptxChartOfPieOptions { ofPieType: 'pie' | 'bar'; splitType?: 'auto' | 'cust' | 'percent' | 'pos' | 'val'; splitPos?: number; custSplit?: number[]; secondPieSize?: number; serLines?: boolean; gapWidth?: number; } /** Classic `c:bubbleChart` options from CT_BubbleChart. */ interface PptxBubbleChartOptions { bubble3D?: boolean; /** Bubble diameter scale in percent, constrained to 0 through 300. */ bubbleScale?: number; showNegativeBubbles?: boolean; sizeRepresents?: 'area' | 'w'; } /** * 3D viewing parameters for a chart (`c:view3D`, ECMA-376 §21.2.2.228 / * CT_View3D). * * All fields are optional and round-trip verbatim. * * - {@link rotX} — X-axis rotation in degrees (-90…90). * - {@link rotY} — Y-axis rotation in degrees (0…360). * - {@link depthPercent} — chart depth as a percentage of base width. * - {@link rAngAx} — `true` if axes meet at right angles. * - {@link perspective} — perspective angle in degrees (0…240). * - {@link hPercent} — height as a percentage of chart width. */ interface PptxChartView3D { rotX?: number; rotY?: number; depthPercent?: number; rAngAx?: boolean; perspective?: number; hPercent?: number; } /** * Chart "chrome" flags from `c:chart` that round-trip cleanly even when * rendering ignores them. * * - {@link autoTitleDeleted} — `c:autoTitleDeleted/@val`. Suppresses the * auto-generated title for single-series charts. * - {@link dispBlanksAs} — `c:dispBlanksAs/@val`. How blank cells * render: `"gap"`, `"zero"`, or `"span"`. * - {@link showDLblsOverMax} — `c:showDLblsOverMax/@val`. Keeps data * labels visible for points exceeding the value-axis maximum. * * `c:plotVisOnly` lives on {@link PptxChartData.plotVisibleOnly} and is * intentionally not duplicated here. */ interface PptxChartChrome { autoTitleDeleted?: boolean; dispBlanksAs?: 'gap' | 'zero' | 'span'; showDLblsOverMax?: boolean; } /** Manual chart placement from `c:layout/c:manualLayout` (CT_ManualLayout). */ interface PptxChartManualLayout { layoutTarget?: 'inner' | 'outer'; xMode?: 'edge' | 'factor'; yMode?: 'edge' | 'factor'; widthMode?: 'edge' | 'factor'; heightMode?: 'edge' | 'factor'; x?: number; y?: number; width?: number; height?: number; /** * Raw `c:extLst` (CT_ExtensionList) of the `c:manualLayout`, captured * verbatim so it round-trips through the typed model. Without this, a dirty * write of an edited layout would drop the extension list (the manual node * is rebuilt from the typed fields). Emitted as the trailing child, matching * the CT_ManualLayout schema order. */ ext?: XmlObject; } /** * Typed manual layouts for chart regions that accept `c:layout`. * A `null` region removes its manual layout without removing extensions. */ interface PptxChartLayouts { title?: PptxChartManualLayout | null; plotArea?: PptxChartManualLayout | null; legend?: PptxChartManualLayout | null; } /** Parsed data extracted from an embedded xlsx workbook. */ interface PptxEmbeddedWorkbookData { /** Category labels from the first column/row. */ categories: string[]; /** Data series extracted from worksheet cells. */ series: Array<{ name: string; values: number[]; }>; /** Whether the workbook uses the 1904 date system. */ date1904?: boolean; } /** Raw numeric category cache used by a classic ChartML date axis. */ interface PptxChartDateCategories { values: number[]; /** False/default uses Excel's 1900 date system; true uses the 1904 system. */ date1904?: boolean; /** Number format copied from the numeric category cache. */ formatCode?: string; } /** * Complete parsed chart data for a {@link ChartPptxElement}. * * @example * ```ts * const chart: PptxChartData = { * title: "Q4 Sales", * chartType: "bar", * categories: ["Jan", "Feb", "Mar"], * series: [ * { name: "Revenue", values: [100, 120, 140] }, * ], * grouping: "clustered", * style: { hasLegend: true, legendPosition: "b" }, * }; * // => satisfies PptxChartData * ``` */ interface PptxChartData { title?: string; chartType: PptxChartType; categories: string[]; /** * Hierarchical category levels in source XML order, for both ChartEx * hierarchy charts (`cx:multiLvlStrRef`) and classic multi-level category * axes (`c:cat/c:multiLvlStrRef`, e.g. a PowerPoint Quarter > Month * grouping). Level 0 contains the leaf labels and remains mirrored by * {@link categories} for consumers that only understand a flat category * axis. Parent (grouping) levels are forward-filled: a blank cache slot * continues the previous group's label, matching how the source stores a * merged category header sparsely. */ categoryLevels?: string[][]; dateCategories?: PptxChartDateCategories; series: PptxChartSeries[]; /** Chart style/formatting metadata. */ style?: PptxChartStyle; /** Grouping mode for bar/area/line charts: 'clustered' | 'stacked' | 'percentStacked' */ grouping?: 'clustered' | 'stacked' | 'percentStacked'; /** * Whether the first (or only) series varies its point colours * (`c:varyColors/@val`). Pie/doughnut default this on; single-series * bar/column honour it by giving each point a distinct palette colour. * Absent when the source XML omits `c:varyColors`. */ varyColors?: boolean; /** * Pie/doughnut start angle in degrees clockwise from 12 o'clock * (`c:firstSliceAng/@val`, 0 through 360). Absent uses the default 0. */ firstSliceAngle?: number; /** * Doughnut hole diameter as a percentage of the outer diameter * (`c:holeSize/@val`, 10 through 90). Absent uses the renderer default. */ doughnutHoleSize?: number; /** * Bar series direction (`c:barDir/@val`): `"col"` draws vertical columns, * `"bar"` draws horizontal bars. Absent means `"col"` (PowerPoint's own * default), so only horizontal bar charts need to carry the field. */ barDirection?: PptxChartBarDirection; /** * Scatter presentation mode (`c:scatterChart/c:scatterStyle/@val`). * * `lineMarker` (PowerPoint's own default for every scatter it writes) and * `smoothMarker` draw a connecting line; `marker` and `none` do not. Whether * the MARKERS appear is decided separately by `c:marker/c:symbol`, and * whether the LINE appears is further gated by * {@link PptxChartSeries.lineNoFill} - PowerPoint expresses "markers only" as * `lineMarker` plus an `a:ln/a:noFill`, not as `marker`. */ scatterStyle?: PptxChartScatterStyle; /** * Bar/column gap between category clusters as a percentage of bar width * (`c:gapWidth/@val`, 0 through 500). Absent uses the renderer default. */ barGapWidth?: number; /** * Clustered bar/column overlap between series within a category as a * percentage (`c:overlap/@val`, -100 through 100). Absent uses 0. */ barOverlap?: number; /** Internal: path to the chart XML part in the PPTX archive (for round-trip save). */ chartPartPath?: string; /** Internal: relationship ID linking the graphic frame to the chart part. */ chartRelationshipId?: string; /** `null` explicitly removes an existing ChartML data table. */ dataTable?: PptxChartDataTable | null; dropLines?: PptxChartLineStyle; hiLowLines?: PptxChartLineStyle; /** `null` explicitly removes an existing up/down-bars container. */ upDownBars?: PptxChartUpDownBars | null; axes?: PptxChartAxisFormatting[]; floor?: PptxChart3DSurface; sideWall?: PptxChart3DSurface; backWall?: PptxChart3DSurface; /** Per-band surface-chart colour overrides (`c:surfaceChart/c:bandFmts`). */ bandFmts?: PptxChartBandFmt[]; /** External data source reference (c:externalData) linking to an external workbook. */ externalData?: PptxExternalData; /** * Parsed data from the embedded xlsx workbook (from ppt/embeddings/). * * When a chart references an embedded Excel workbook via `c:externalData`, * the xlsx is parsed to extract categories and series. This data serves as * a fallback when the chart XML's cached series data is empty or incomplete. */ embeddedWorkbookData?: PptxEmbeddedWorkbookData; /** * Pivot table data source reference (c:pivotSource). * * When present, the chart's data originates from a PivotTable. * The chart still renders using its cached series data; this field * is metadata about the data origin, preserved for round-trip fidelity. */ pivotSource?: PptxChartPivotSource | null; /** * Whether only visible cells are plotted (c:plotVisOnly). * When `true` (the default), hidden cells are excluded from the chart. * When `false`, hidden data IS plotted. */ plotVisibleOnly?: boolean; /** * Color palette extracted from the chart's Office 2013+ color style part * (`chartColorStyle*.xml`). When present, this palette takes priority over * the `c:style/@val`-derived palette in `getChartStylePalette`. * * Each entry is a resolved hex colour string (e.g. `"#4472C4"`). */ colorPalette?: string[]; /** * Color cycling method from the chart color style part's `meth` attribute. * * - `"cycle"` — repeat the palette colours in order (default) * - `"withinLinear"` — gradient within each series * - `"acrossLinear"` — gradient across series */ colorMethod?: 'cycle' | 'withinLinear' | 'acrossLinear'; /** Internal source color-style part path used for lossless dirty saves. */ colorStylePartPath?: string; /** Internal parsed palette snapshot used to detect actual edits. */ colorStyleOriginalPalette?: string[]; /** Internal parsed method snapshot used to detect actual edits. */ colorStyleOriginalMethod?: 'cycle' | 'withinLinear' | 'acrossLinear'; /** * Pie-of-pie / Bar-of-pie options (`c:ofPieChart`, CT_OfPieChart). * * Present only when {@link chartType} is `"ofPie"`. Carries the split * configuration, secondary plot size, and serLines flag so that an * `ofPieChart` element can be re-emitted on save with full fidelity. */ ofPieOptions?: PptxChartOfPieOptions; /** Classic bubble-chart display options (`c:bubbleChart`). */ bubbleOptions?: PptxBubbleChartOptions; /** * 3D viewing parameters (`c:view3D`, CT_View3D). * * Parsed from and emitted to `c:chart/c:view3D`. Absent when the * chart XML has no `c:view3D` element. */ view3D?: PptxChartView3D; /** * Top-level chart chrome flags (`c:autoTitleDeleted`, * `c:dispBlanksAs`, `c:showDLblsOverMax`). * * Each flag is omitted from the emitted XML when absent on the * source data, so absence does not produce empty `` placeholders. */ chartChrome?: PptxChartChrome; /** `c:chartSpace/c:printSettings`; `null` removes the container on save. */ printSettings?: PptxChartPrintSettings | null; /** `c:chartSpace/c:protection`; `null` removes the container on save. */ protection?: PptxChartProtection | null; /** Editable manual placement for the title, plot area, and legend. */ layouts?: PptxChartLayouts; /** * Raw `c:userShapes` XML subtree (a drawing tree) preserved verbatim. * * `c:userShapes` references a separate drawing part containing * shapes drawn over the chart. The reference is preserved as-is so * that round-trip save re-emits the original element without * attempting to parse the nested drawing tree. */ userShapesXml?: unknown; /** * Parsed, renderable drawing-overlay shapes resolved from the separate * drawing part referenced by `c:userShapes/@r:id` * (`ppt/drawings/drawingN.xml`). Each entry carries chart-relative anchor * geometry plus light shape/text formatting so the viewer can render an * overlay on top of the chart plot. Render-only: {@link userShapesXml} * remains the source of truth for round-trip save. */ userShapes?: PptxChartUserShape[]; /** * Raw `c:pivotFmts` XML subtree preserved verbatim. * * `c:pivotFmts` carries a list of `c:pivotFmt` formatting overrides * for charts whose data originates from a PivotTable. Preserved * verbatim for round-trip fidelity. */ /** Typed pivot-chart format persistence; `null` removes `c:pivotFmts`. */ pivotFormats?: PptxChartPivotFormats | null; /** * Color-map override (`c:clrMapOvr`) carrying 12 attributes that * remap theme colour roles for this chart only. Preserved as a flat * `attribute → value` map for round-trip fidelity. */ clrMapOvr?: Record; } //#endregion //#region src/core/types/image.d.ts /** * Image recolour/adjustment properties parsed from blip extensions. * * These effects are stored in the OpenXML `` extension list * and applied non-destructively to the original image data. * * @example * ```ts * const fx: PptxImageEffects = { * brightness: 20, * contrast: -10, * grayscale: true, * }; * // => { brightness: 20, contrast: -10, grayscale: true } satisfies PptxImageEffects * ``` */ /** * One `a14:foregroundMark` / `a14:backgroundMark` polyline hint recorded while * the user painted over the picture in PowerPoint's "Remove Background" mode. * Coordinates are 0..1 fractions of the image. */ interface PptxBackgroundRemovalMark { x1: number; y1: number; x2: number; y2: number; } /** * PowerPoint "Remove Background" state (`a14:backgroundRemoval`). * * The four edges are the RETAINED rectangle as 0..1 fractions of the image * (OOXML stores them as per-100000 relative units), and the mark lists are the * segmentation hints the user painted. * * **This is edit-time metadata, not a render instruction.** PowerPoint bakes the * removal into the bitmap referenced by the main `a:blip/@r:embed` and keeps the * pristine original in `a14:imgLayer/@r:embed`. Verified against PowerPoint COM: * a slide exported with and without this element is byte-identical. A renderer * that clips to the retained rectangle would clip an image whose background has * already been removed. * * @example * ```ts * const removal: PptxBackgroundRemoval = { top: 0.12, bottom: 0.88, left: 0.07, right: 0.93 }; * // => retains the middle of the image; the marks list stays empty * ``` */ interface PptxBackgroundRemoval { /** Top edge of the retained rectangle (0..1 fraction of the image height). */ top: number; /** Bottom edge of the retained rectangle (0..1 fraction of the image height). */ bottom: number; /** Left edge of the retained rectangle (0..1 fraction of the image width). */ left: number; /** Right edge of the retained rectangle (0..1 fraction of the image width). */ right: number; /** Strokes marking regions the user forced to be foreground. */ foregroundMarks?: PptxBackgroundRemovalMark[]; /** Strokes marking regions the user forced to be background. */ backgroundMarks?: PptxBackgroundRemovalMark[]; /** Original effect XML, retained for lossless re-emission. */ rawXml?: XmlObject; } interface PptxImageEffects { /** Brightness adjustment (-100 to 100). */ brightness?: number; /** Contrast adjustment (-100 to 100). */ contrast?: number; /** Duotone colour pair. */ duotone?: { color1: string; color2: string; /** Original effect XML, retained while the resolved colours are unchanged. */ rawXml?: XmlObject; }; /** Grayscale flag. */ grayscale?: boolean; /** Saturation adjustment (-100 to 100). */ saturation?: number; /** Color wash overlay. */ colorWash?: { color: string; opacity: number; }; /** Artistic effect name (blur, pencilGrayscale, paintStrokes, etc.). */ artisticEffect?: string; /** Artistic effect radius/amount, normalised to 0..100. */ artisticRadius?: number; /** * Every numeric attribute of the source `a14:artistic*` element, raw and * un-normalised (`trans`, `pencilSize`, `crackSpacing`, …). The attribute set * differs per effect, so this is the lossless companion to the single * {@link PptxImageEffects.artisticRadius} number. */ artisticParams?: Record; /** * Name of the artistic effect ALREADY baked into the image data, which a * renderer must not apply a second time. Set from the `a14` blip extension, * which PowerPoint writes alongside a pre-rendered bitmap (see * {@link PptxBackgroundRemoval}), and normally equal to * {@link PptxImageEffects.artisticEffect}. * * It records the NAME rather than a boolean so that picking a different * effect in this library's inspector (which patches `artisticEffect` alone) * still renders: the two names then differ. */ artisticPrerenderedEffect?: string; /** * PowerPoint "Remove Background" state (`a14:backgroundRemoval`). Edit-time * metadata: the removal is already baked into the image data. */ backgroundRemoval?: PptxBackgroundRemoval; /** * `a14:imgLayer/@r:embed` — relationship id of the PRISTINE original image * the baked effects were derived from (PowerPoint stores it as an HD Photo * `.wdp` part, which browsers cannot decode). */ originalImageRelId?: string; /** Alpha modulation fixed: non-negative percentage (100 means unchanged opacity). */ alphaModFix?: number; /** Original alpha modulation fixed node, including foreign attributes. */ alphaModFixRawXml?: XmlObject; /** Bi-level threshold — converts to 1-bit black/white (0-100). */ biLevel?: number; /** Colour change — swap one colour range for another (used for transparency keying). */ clrChange?: { clrFrom: string; clrTo: string; /** Whether the target colour is fully transparent (alpha = 0). */ clrToTransparent?: boolean; /** Original effect XML, including colour transforms and extensions. */ rawXml?: XmlObject; }; /** Original grayscale node, including extension or foreign attributes. */ grayscaleRawXml?: XmlObject; /** Original bi-level node, including extension or foreign attributes. */ biLevelRawXml?: XmlObject; /** * Alpha inverse effect (`a:alphaInv`). Inverts the alpha channel; an optional * colour child shifts the inversion baseline. */ alphaInv?: { /** Optional baseline colour (hex). */ color?: string; /** Original effect XML, including colour transforms and foreign attributes. */ rawXml?: XmlObject; }; /** Alpha ceiling (`a:alphaCeiling`) — clamps any non-zero alpha to fully opaque. Boolean flag. */ alphaCeiling?: boolean; /** Original alpha ceiling node, including foreign attributes. */ alphaCeilingRawXml?: XmlObject; /** Alpha floor (`a:alphaFloor`) — clamps any non-fully-opaque alpha to fully transparent. Boolean flag. */ alphaFloor?: boolean; /** Original alpha floor node, including foreign attributes. */ alphaFloorRawXml?: XmlObject; /** * Alpha modulate (`a:alphaMod`). The schema requires a single `cont` (effect * container) child; we preserve the inner XML opaquely for round-trip. */ alphaMod?: { /** Raw opaque XML for the `a:cont` child to preserve on save. */ contRawXml?: Record; /** Original effect XML, including foreign attributes. */ rawXml?: XmlObject; /** * Multiplicative alpha percentage (0..100+), read from a nested * `` inside `contRawXml` when present - the common * real-world shape of `a:alphaMod` (` * `). Derived/read-only: a renderer multiplies the source alpha * by this, distinct from the top-level {@link alphaModFix} sibling * effect. Not written back independently on save; `contRawXml` is what * round-trips. */ amt?: number; }; /** Alpha replace (`a:alphaRepl`) — replaces alpha with the given fixed-percent value (0..100). */ alphaRepl?: number; /** Original alpha replace node, including foreign attributes. */ alphaReplRawXml?: XmlObject; /** Alpha bi-level (`a:alphaBiLevel`) — threshold (0..100) above which alpha becomes fully opaque. */ alphaBiLevel?: number; /** Original alpha bi-level node, including foreign attributes. */ alphaBiLevelRawXml?: XmlObject; /** * Colour replace (`a:clrRepl`) — replaces all colour information in an image * with the given solid colour. Stores the raw colour child to preserve scheme * colour references and modifiers. */ clrRepl?: { /** Resolved hex colour. */ color: string; /** Raw opaque colour XML for round-trip. */ rawXml?: Record; }; /** Luminance modulation (`a:lum`) — bright/contrast as fixed percentages (0..100). */ lum?: { bright?: number; contrast?: number; }; /** HSL modulation (`a:hsl`) — hue (0..360 degrees), saturation/luminance (-100..100). */ hsl?: { hue?: number; sat?: number; lum?: number; }; /** Image-effect tint (`a:tint` inside blip) — hue (0..360), amount (-100..100). */ tint?: { hue?: number; amt?: number; }; /** * Fill overlay (`a:fillOverlay`) — overlays a fill on top of the blip. * Stores blend mode and the raw inner fill XML for round-trip. */ fillOverlay?: { blend: 'over' | 'mult' | 'screen' | 'darken' | 'lighten'; /** Raw opaque fill XML preserved for round-trip. */ fillRawXml?: Record; /** * Resolved hex colour, when the overlay fill is a plain `a:solidFill` * (the common case for a picture-style colour overlay). `undefined` for * a gradient/pattern/picture overlay fill - see {@link resolvedGradient} / * {@link resolvedPattern} instead. `fillRawXml` still round-trips * losslessly regardless of which of the three resolved. */ resolvedColor?: string; /** Resolved opacity (0-1) of the `a:solidFill` overlay colour, when resolved. */ resolvedOpacity?: number; /** * Resolved gradient, when the overlay fill is `a:gradFill`. A renderer * composites this as an SVG paint server (`` / * ``) rather than a flat flood colour. */ resolvedGradient?: { type: 'linear' | 'radial'; /** Gradient angle in degrees (`a:lin/@ang`), for a linear gradient. */ angle?: number; stops: Array<{ color: string; position: number; opacity?: number; }>; }; /** * Resolved preset pattern, when the overlay fill is `a:pattFill`. A * renderer composites this as a tiled SVG paint server. */ resolvedPattern?: { preset: string; foreground?: string; background?: string; }; }; /** Blur (`a:blur`) — radius in EMU and grow flag. */ blur?: { rad?: number; grow?: boolean; }; } /** * Shape names used for crop-to-shape (CSS `clip-path` equivalent). * * @example * ```ts * const shape: PptxCropShape = "ellipse"; * // => "ellipse" — one of: none | ellipse | roundedRect | triangle | diamond | pentagon | hexagon | star * ``` */ type PptxCropShape = 'none' | 'ellipse' | 'roundedRect' | 'triangle' | 'diamond' | 'pentagon' | 'hexagon' | 'star'; /** * Image content mixin — present on image and picture elements. * * Contains the decoded image data (base64 data URL or archive path), * alt text, crop insets, tiling settings, and image effects. * * @example * ```ts * const props: PptxImageProperties = { * imagePath: "ppt/media/image1.png", * altText: "Company logo", * cropLeft: 0.05, * cropRight: 0.05, * }; * // => { imagePath: "ppt/media/image1.png", altText: "Company logo", cropLeft: 0.05, cropRight: 0.05 } * ``` */ interface PptxImageProperties { /** Base64 data-URL for the decoded image. */ imageData?: string; /** Path within the PPTX ZIP archive. */ imagePath?: string; /** Base64 data-URL for an SVG variant (from blip extension asvg:svgBlip). Preferred over raster when available. */ svgData?: string; /** Path to the SVG file within the PPTX ZIP archive. */ svgPath?: string; /** Alt text / description from `p:cNvPr/@descr`. */ altText?: string; /** Crop from left edge as 0..1 fraction (OOXML `a:srcRect/@l`). */ cropLeft?: number; /** Crop from top edge as 0..1 fraction (OOXML `a:srcRect/@t`). */ cropTop?: number; /** Crop from right edge as 0..1 fraction (OOXML `a:srcRect/@r`). */ cropRight?: number; /** Crop from bottom edge as 0..1 fraction (OOXML `a:srcRect/@b`). */ cropBottom?: number; /** * Stretch target inset from the left frame edge as a signed fraction * (OOXML `a:stretch/a:fillRect/@l`). Unlike `cropLeft` (which selects a * region of the SOURCE bitmap), a fill-rect selects the region of the * FRAME the whole image is stretched into; negative values push the * image beyond the frame edge, and the overflow is clipped. */ fillRectLeft?: number; /** Stretch target inset from the top frame edge (`a:fillRect/@t`). */ fillRectTop?: number; /** Stretch target inset from the right frame edge (`a:fillRect/@r`). */ fillRectRight?: number; /** Stretch target inset from the bottom frame edge (`a:fillRect/@b`). */ fillRectBottom?: number; /** Image tiling offset X in px. */ tileOffsetX?: number; /** Image tiling offset Y in px. */ tileOffsetY?: number; /** Image tiling scale X as percentage (100 = 100%). */ tileScaleX?: number; /** Image tiling scale Y as percentage (100 = 100%). */ tileScaleY?: number; /** Image tiling flip mode. */ tileFlip?: 'none' | 'x' | 'y' | 'xy'; /** Image tiling alignment. */ tileAlignment?: string; /** * Print-resolution hint in DPI (`a:blipFill/@dpi`). PowerPoint records this * when it downsamples an embedded image for a target print quality; it has * no on-screen rendering effect (a `0`/absent value means "use the source * image's native resolution"). Parsed for round-trip / API fidelity only. */ dpi?: number; /** Image recolour/artistic effect properties. */ imageEffects?: PptxImageEffects; /** Crop-to-shape — CSS clip-path shape name. */ cropShape?: PptxCropShape; } //#endregion //#region src/core/types/media.d.ts /** * Discriminator for embedded media element types. * * @example * ```ts * const kind: PptxMediaType = "video"; * // => "video" — one of: "video" | "audio" | "unknown" * ``` */ type PptxMediaType = 'video' | 'audio' | 'unknown'; type PptxMediaReferenceKind = 'audioCd' | 'wavAudioFile' | 'audioFile' | 'videoFile' | 'quickTimeFile'; interface PptxAudioCdPosition { track: number; time?: number; /** Original `st` or `end` node, retained for lossless edits. */ rawXml?: XmlObject; } /** * A named bookmark within a media clip timeline. * * @example * ```ts * const bm: MediaBookmark = { * id: "bm1", * time: 12.5, * label: "Intro ends", * }; * // => satisfies MediaBookmark * ``` */ interface MediaBookmark { id: string; /** Position in seconds from the start of the clip. */ time: number; /** User-visible label for this bookmark. */ label: string; } /** * Runtime-extracted metadata about a media clip (populated from HTMLMediaElement). * * @example * ```ts * const meta: MediaMetadata = { * duration: 120.5, * videoWidth: 1920, * videoHeight: 1080, * codecInfo: "video/mp4; codecs=\"avc1.640028\"", * }; * // => satisfies MediaMetadata * ``` */ interface MediaMetadata { /** Duration in seconds. */ duration?: number; /** Video width in pixels (video only). */ videoWidth?: number; /** Video height in pixels (video only). */ videoHeight?: number; /** MIME type / codec string reported by the browser. */ codecInfo?: string; } /** * A closed-caption / subtitle track associated with a media element. * * @example * ```ts * const track: MediaCaptionTrack = { * id: "t1", * label: "English", * language: "en", * kind: "subtitles", * isDefault: true, * }; * // => satisfies MediaCaptionTrack * ``` */ interface MediaCaptionTrack { /** Unique ID for this track. */ id: string; /** Human-readable label (e.g. "English", "Spanish"). */ label: string; /** BCP-47 language code (e.g. "en", "es"). */ language: string; /** Track kind: subtitles, captions, or descriptions. */ kind: 'subtitles' | 'captions' | 'descriptions'; /** Data URL or path to the VTT/SRT content within the PPTX archive. */ src?: string; /** Inline VTT content (for embedded captions). */ content?: string; /** Whether this track is the default/active one. */ isDefault?: boolean; } //#endregion //#region src/core/types/smart-art-constraint-rules.d.ts type PptxSmartArtConstraintRelationship = 'self' | 'ch' | 'des'; type PptxSmartArtConstraintOperator = 'none' | 'equ' | 'gte' | 'lte'; type PptxSmartArtConstraintPointType = 'all' | 'doc' | 'node' | 'norm' | 'nonNorm' | 'asst' | 'nonAsst' | 'parTrans' | 'pres' | 'sibTrans'; interface PptxSmartArtConstraintTarget { for?: PptxSmartArtConstraintRelationship; forName?: string; pointType?: PptxSmartArtConstraintPointType; } /** Editable DiagramML CT_Constraint. */ interface PptxSmartArtConstraint extends PptxSmartArtConstraintTarget { type: string; referenceType?: string; referenceFor?: PptxSmartArtConstraintRelationship; referenceForName?: string; referencePointType?: PptxSmartArtConstraintPointType; operator?: PptxSmartArtConstraintOperator; value?: number; factor?: number; /** Original constraint retained for foreign attributes and extension content. */ rawXml?: XmlObject; } /** Editable DiagramML CT_NumericRule. */ interface PptxSmartArtNumericRule extends PptxSmartArtConstraintTarget { type: string; value?: number; factor?: number; max?: number; /** Original rule retained for foreign attributes and extension content. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/smart-art-layout-definition.d.ts interface PptxSmartArtLocalizedText { value: string; language?: string; } interface PptxSmartArtLayoutCategory { type: string; priority: number; } interface PptxSmartArtAlgorithmParameter { type: string; value?: string; } /** Typed DiagramML CT_Algorithm data attached to a layout node. */ interface PptxSmartArtLayoutAlgorithm { type: string; revision?: number; parameters?: PptxSmartArtAlgorithmParameter[]; } interface PptxSmartArtIteratorAttributes { name?: string; reference?: string; axis?: string[]; pointTypes?: string[]; hideLastTransition?: boolean[]; start?: number[]; count?: number[]; step?: number[]; } interface PptxSmartArtForEach extends PptxSmartArtIteratorAttributes { rawXml?: XmlObject; } interface PptxSmartArtWhen extends PptxSmartArtIteratorAttributes { function: string; argument?: string; operator: string; value: string; rawXml?: XmlObject; } interface PptxSmartArtChoose { name?: string; when: PptxSmartArtWhen[]; otherwise?: { name?: string; rawXml?: XmlObject; } | null; rawXml?: XmlObject; } /** A single `dgm:adj/@val` adjustment, keyed by its `@idx` (1-based, like `a:gd`). */ interface PptxSmartArtShapeAdjustment { index: number; value: number; } /** * Typed DiagramML CT_Shape data (`dgm:shape`) attached to a layout node: the * per-node preset geometry override real (and third-party/custom) layout * definitions use so a layoutNode can be e.g. an ellipse or a chevron instead * of the arranger family's hardcoded default shape. */ interface PptxSmartArtLayoutNodeShape { /** `dgm:shape/@type`: a preset geometry name (`roundRect`, `ellipse`, `chevron`, `conn`, ...). */ presetGeometry?: string; /** `dgm:adjLst/dgm:adj` entries (adjustment index -> value, as authored). */ adjustments?: PptxSmartArtShapeAdjustment[]; /** `dgm:shape/@hideGeom`: the shape is present only to size text, never painted. */ hideGeometry?: boolean; } /** Identity and ordering metadata from DiagramML CT_LayoutNode. */ interface PptxSmartArtLayoutNode { name?: string; styleLabel?: string; childOrder?: 'b' | 't'; moveWith?: string; algorithm?: PptxSmartArtLayoutAlgorithm; forEach?: PptxSmartArtForEach[]; choose?: PptxSmartArtChoose[]; constraints?: PptxSmartArtConstraint[]; rules?: PptxSmartArtNumericRule[]; /** `dgm:shape`: this node's own preset geometry override, when present. */ shape?: PptxSmartArtLayoutNodeShape; children?: PptxSmartArtLayoutNode[]; } /** Metadata and root node from DiagramML CT_DiagramDefinition. */ interface PptxSmartArtLayoutDefinition { uniqueId?: string; minimumVersion?: string; defaultStyle?: string; titles?: PptxSmartArtLocalizedText[]; descriptions?: PptxSmartArtLocalizedText[]; categories?: PptxSmartArtLayoutCategory[]; rootNode: PptxSmartArtLayoutNode; /** Original definition retained for constraint evaluation and foreign rules. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/smart-art-node.d.ts /** * A single run of text inside a SmartArt node, capturing the run text and the * raw `a:rPr` run-properties object verbatim so per-run formatting (bold, * colour, size, etc.) survives a load -> edit -> save round-trip instead of * collapsing to a single unstyled run. * * @example * ```ts * const run: PptxSmartArtTextRun = { * text: "Bold", * rPr: { "@_b": "1", "@_lang": "en-US" }, * }; * // => satisfies PptxSmartArtTextRun * ``` */ interface PptxSmartArtTextRun { /** Run text content. */ text: string; /** * Raw parsed `a:rPr` run-properties object, preserved verbatim for * round-trip. Untyped XML, hence the loose record shape. */ rPr?: Record; /** Resolved standard shape-text style derived from {@link rPr}. */ style?: TextStyle; /** Raw run XML used to retain unmodelled extension children on save. */ rawXml?: Record; /** Original direct-child order, including unmodelled extension children. */ childOrder?: string[]; } /** An ordered item within a SmartArt text paragraph. */ type PptxSmartArtTextParagraphItem = { kind: 'run'; run: PptxSmartArtTextRun; } | { kind: 'break'; rPr?: Record; style?: TextStyle; rawXml?: Record; childOrder?: string[]; } | { kind: 'field'; id?: string; fieldType?: string; text: string; rPr?: Record; style?: TextStyle; pPr?: Record; rawXml?: Record; childOrder?: string[]; } | { kind: 'tab'; rawXml?: Record; childOrder?: string[]; } | { kind: 'raw'; name: string; value: unknown; }; /** A complete `a:p` paragraph in a SmartArt data-model text body. */ interface PptxSmartArtTextParagraph { /** Paragraph properties (`a:pPr`) preserved verbatim. */ pPr?: Record; /** Text children in source order. */ items: PptxSmartArtTextParagraphItem[]; /** End-paragraph run properties (`a:endParaRPr`) preserved verbatim. */ endParaRPr?: Record; /** Resolved style for the paragraph terminator. */ endParaStyle?: TextStyle; /** Raw paragraph XML used to retain unmodelled extension children on save. */ rawXml?: Record; } /** * Per-node visual override for a SmartArt node. * * Captures the individual fill / line / font colour and the bold / italic * emphasis a user has set on one specific node, independent of the diagram's * colour scheme and quick style. All colours are hex strings (e.g. "#FF0000"). * Every field is optional: only the overridden aspects are carried, so an * empty object means "no per-node override". * * The parser reads these from the data point's `spPr` solid fill / line colour * and the first run's `rPr` (b / i / solidFill) when present, and the save path * writes them back so the override survives a load -> edit -> save round-trip. * * @example * ```ts * const style: PptxSmartArtNodeStyle = { * fillColor: "#FF0000", * fontColor: "#FFFFFF", * bold: true, * }; * // => satisfies PptxSmartArtNodeStyle * ``` */ interface PptxSmartArtNodeStyle { /** Solid fill colour override (hex, e.g. "#4F81BD"). */ fillColor?: string; /** Outline / line colour override (hex). */ lineColor?: string; /** Text (font) colour override (hex). */ fontColor?: string; /** Bold emphasis override for the node's runs. */ bold?: boolean; /** Italic emphasis override for the node's runs. */ italic?: boolean; } /** * Manual layout override for a `type="pres"` presentation point, read from its * `dgm:prSet` attributes. PowerPoint writes these when the user drags, resizes, * rotates, or flips a SmartArt node by hand in its own diagram editor; without * them the node silently reverts to its algorithmic position whenever there is * no cached `dsp:` drawing part to fall back on. * * Every field is optional: only the attributes actually present on `prSet` are * populated. Angle and scale/factor units are already normalised to degrees and * plain ratios (a `custScaleX="150000"` becomes `scaleX: 1.5`), so a consumer * never has to know the raw `60000ths-of-a-degree` / `100000ths-of-a-percent` * XML encodings. * * @example * ```ts * const custom: SmartArtNodeCustomLayout = { angle: 15, scaleX: 1.2 }; * // => a node manually rotated 15 degrees and widened 20% in PowerPoint * ``` */ interface SmartArtNodeCustomLayout { /** `custAng`: additional rotation in degrees. */ angle?: number; /** `custScaleX`: horizontal scale ratio (1 = no change). */ scaleX?: number; /** `custScaleY`: vertical scale ratio (1 = no change). */ scaleY?: number; /** `custSzX`: horizontal size ratio, layered on top of {@link scaleX}. */ sizeX?: number; /** `custSzY`: vertical size ratio, layered on top of {@link scaleY}. */ sizeY?: number; /** `custFlipHor`: the node was manually mirrored horizontally. */ flipHorizontal?: boolean; /** `custFlipVert`: the node was manually mirrored vertically. */ flipVertical?: boolean; /** `custLinFactX`: manual position nudge along X, as a fraction of the container width. */ linearFactorX?: number; /** `custLinFactY`: manual position nudge along Y, as a fraction of the container height. */ linearFactorY?: number; /** * `custLinFactNeighborX`: spacing compensation applied to a NEIGHBOURING * node when this one is resized. Parsed for round-trip completeness; not * applied by the per-node final transform (it has no effect on this node's * own geometry; folding it into a neighbour's geometry would require * whole-layout awareness the final transform pass does not have). */ linearFactorNeighborX?: number; /** `custLinFactNeighborY`: see {@link linearFactorNeighborX} (Y axis). */ linearFactorNeighborY?: number; /** `custRadScaleRad`: manual radius scale ratio for a radial/cycle node. */ radialScaleRadius?: number; /** `custRadScaleInc`: manual angular-position nudge for a radial/cycle node. */ radialScaleIncrement?: number; /** `custT`: whether `prSet` declares a custom transform is present at all. */ hasCustomTransform?: boolean; } /** * A single node in the SmartArt data model. * * @example * ```ts * const node: PptxSmartArtNode = { * id: "1", * text: "CEO", * children: [ * { id: "2", text: "VP Marketing", parentId: "1" }, * { id: "3", text: "VP Engineering", parentId: "1" }, * ], * }; * // => satisfies PptxSmartArtNode * ``` */ interface PptxSmartArtNode { id: string; text: string; /** CT_Pt connection identifier, when the point references a connection. */ connectionId?: string | null; parentId?: string; children?: PptxSmartArtNode[]; /** Node type from `@_type` attribute (e.g. "doc", "node", "asst", "pres"). */ nodeType?: string; /** * The node's own quick-style role (`dgm:prSet/@presStyleLbl` from its * paired `type="pres"` presentation point, resolved via a `presOf` * connection back to this content point). Structural names like `node1`, * `asst2`, `bgShp`, `revTx`; distinct from {@link nodeType}, which is the * data-model `@_type` ("node"/"asst"/...). Used to pick this node's own * colour list from a colour transform's per-role palettes instead of the * generic cycled palette (see `applySmartArtRoleColors`). */ styleRole?: string; /** * Per-run text + run-properties for the node's first paragraph, captured at * parse time. When the joined run text still equals {@link text} (the node * was not edited, or was edited only in ways that preserve the run split), * the save path rebuilds the paragraph from these runs so per-run rich text * is not flattened. When {@link text} diverges, the runs are ignored. */ runs?: PptxSmartArtTextRun[]; /** * Complete typed paragraph model. Unlike {@link runs}, this retains every * paragraph and the ordered run, field, break, and tab children within it. */ paragraphs?: PptxSmartArtTextParagraph[]; /** * Optional per-node visual override (fill / line / font colour, bold / * italic). Read at parse time from the point's `spPr` / first-run `rPr`, set * by the editing op, honoured by the render path, and written back on save so * it round-trips. */ style?: PptxSmartArtNodeStyle; /** * Manual layout override read from the node's `dgm:prSet` `cust*` * attributes (drag/resize/rotate/flip performed in PowerPoint's own diagram * editor). Applied as a final transform after algorithmic layout by * {@link module:smartart-layout-interpreter-custom} so it survives even * when there is no cached `dsp:` drawing to fall back on. */ customLayout?: SmartArtNodeCustomLayout; } //#endregion //#region src/core/types/smart-art-style-definition.d.ts /** Editable metadata shared by DiagramML quick-style and color definitions. */ interface PptxSmartArtDefinitionText { value: string; language?: string; } interface PptxSmartArtDefinitionCategory { type: string; priority: number; } type PptxSmartArtColorApplicationMethod = 'span' | 'cycle' | 'repeat'; type PptxSmartArtHueDirection = 'cw' | 'ccw'; /** CT_Colors application metadata. Color-choice children remain preserved XML. */ interface PptxSmartArtColorListMetadata { method?: PptxSmartArtColorApplicationMethod; hueDirection?: PptxSmartArtHueDirection; } /** CT_StyleLabel metadata from a quick-style definition. */ interface PptxSmartArtQuickStyleLabel { name: string; } /** CT_CTStyleLabel metadata from a color-transform definition. */ interface PptxSmartArtColorStyleLabel { name: string; fill?: PptxSmartArtColorListMetadata; line?: PptxSmartArtColorListMetadata; effect?: PptxSmartArtColorListMetadata; textLine?: PptxSmartArtColorListMetadata; textFill?: PptxSmartArtColorListMetadata; textEffect?: PptxSmartArtColorListMetadata; } interface PptxSmartArtDefinitionMetadata { uniqueId?: string; minimumVersion?: string; titles?: PptxSmartArtDefinitionText[]; descriptions?: PptxSmartArtDefinitionText[]; categories?: PptxSmartArtDefinitionCategory[]; } /** Typed CT_ColorTransform metadata and the resolved legacy color palette. */ interface PptxSmartArtColorTransform extends PptxSmartArtDefinitionMetadata { /** Legacy resolved display name. */ name?: string; /** Ordered resolved fill colors for rendering. */ fillColors: string[]; /** Ordered resolved line colors for rendering. */ lineColors: string[]; /** Ordered resolved text-fill colors (primary styleLbl `txFillClrLst`). */ textFillColors?: string[]; /** Ordered resolved text-line colors (primary styleLbl `txLinClrLst`). */ textLineColors?: string[]; /** Ordered resolved effect colors (primary styleLbl `effectClrLst`). */ effectColors?: string[]; /** Ordered resolved text-effect colors (primary styleLbl `txEffectClrLst`). */ textEffectColors?: string[]; /** Fill-list span/cycle + hue-direction interpolation of the primary styleLbl. */ fillInterpolation?: PptxSmartArtColorListMetadata; /** Line-list span/cycle + hue-direction interpolation of the primary styleLbl. */ lineInterpolation?: PptxSmartArtColorListMetadata; /** Ordered CT_CTStyleLabel metadata. */ labels?: PptxSmartArtColorStyleLabel[]; /** * Every `styleLbl`'s own resolved fill/line colour list, keyed by name * (e.g. `node1`, `asst0`, `bgShp`, `revTx`). Unlike {@link fillColors} / * {@link lineColors} (which collapse to ONE "primary" node-role list), * this keeps every role so a node can be coloured from its OWN role's * palette (see `PptxSmartArtNode.styleRole` and `applySmartArtRoleColors`) * instead of a generic cycled colour. */ roleColors?: Record; } /** Typed CT_StyleDefinition metadata and legacy rendering hint. */ interface PptxSmartArtQuickStyle extends PptxSmartArtDefinitionMetadata { /** Legacy resolved display name. */ name?: string; /** Legacy effect-intensity rendering hint. */ effectIntensity?: string; /** Ordered CT_StyleLabel metadata. Complex style payload remains preserved XML. */ labels?: PptxSmartArtQuickStyleLabel[]; } //#endregion //#region src/core/types/smart-art.d.ts /** * Resolved SmartArt layout category. * * @example * ```ts * const cat: SmartArtLayoutType = "hierarchy"; * // => "hierarchy" — one of: "list" | "process" | "cycle" | "hierarchy" | "relationship" | … * ``` */ type SmartArtLayoutType = 'list' | 'process' | 'cycle' | 'hierarchy' | 'relationship' | 'matrix' | 'pyramid' | 'funnel' | 'gear' | 'target' | 'timeline' | 'venn' | 'chevron' | 'bending' | 'unknown'; /** * Named SmartArt layout presets for creation (subset of PowerPoint layouts). * * @example * ```ts * const layout: SmartArtLayout = "hierarchy"; * // => "hierarchy" — one of: "basicBlockList" | "alternatingHexagons" | "hierarchy" | … * ``` */ type SmartArtLayout = 'basicBlockList' | 'alternatingHexagons' | 'basicChevronProcess' | 'basicCycle' | 'basicPie' | 'basicRadial' | 'basicVenn' | 'continuousBlockProcess' | 'convergingRadial' | 'hierarchy' | 'horizontalBulletList' | 'linearVenn' | 'segmentedProcess' | 'stackedList' | 'tableList' | 'trapezoidList' | 'upwardArrow' | 'basicFunnel' | 'basicTarget' | 'interlockingGears' | 'basicTimeline' | 'basicMatrix' | 'basicPyramid' | 'invertedPyramid' | 'bendingProcess' | 'stepDownProcess' | 'alternatingFlow' | 'descendingProcess' | 'pictureAccentList' | 'verticalBlockList' | 'groupedList' | 'pyramidList' | 'horizontalPictureList' | 'accentProcess' | 'verticalChevronList'; /** * SmartArt colour scheme presets. * * @example * ```ts * const scheme: SmartArtColorScheme = "colorful1"; * // => "colorful1" — one of: "colorful1" | "colorful2" | "colorful3" | "monochromatic1" | "monochromatic2" * ``` */ type SmartArtColorScheme = 'colorful1' | 'colorful2' | 'colorful3' | 'monochromatic1' | 'monochromatic2'; /** * SmartArt visual style intensity. * * @example * ```ts * const style: SmartArtStyle = "moderate"; * // => "moderate" — one of: "flat" | "moderate" | "intense" * ``` */ type SmartArtStyle = 'flat' | 'moderate' | 'intense'; /** * A connection between two SmartArt data-model nodes. * * @example * ```ts * const conn: PptxSmartArtConnection = { * sourceId: "1", * destId: "2", * type: "parOf", * }; * // => satisfies PptxSmartArtConnection * ``` */ interface PptxSmartArtConnection { /** Stable CT_Cxn model identifier. Required when serialized. */ modelId?: string | null; /** Model ID of the source node. */ sourceId: string; /** Model ID of the destination node. */ destId: string; /** Connection type (e.g. "parOf", "presOf", "sibTrans"). */ type?: string; /** Source index for ordering sibling connections. */ srcOrd?: number; /** Destination index for ordering. */ destOrd?: number; /** Model ID of the parent transition point associated with this edge. */ parentTransitionId?: string | null; /** Model ID of the sibling transition point associated with this edge. */ siblingTransitionId?: string | null; /** Layout presentation identifier used by presentation connections. */ presentationId?: string | null; /** * Connector text, read from the linked `parTrans`/`sibTrans` transition * point's `dgm:t` (via {@link parentTransitionId} / {@link siblingTransitionId}). * PowerPoint's own diagram editor lets a user type text directly onto an * org-chart relationship connector; `undefined` when the transition point * carries no text. Written back to that point on save. */ label?: string; } /** * A pre-computed shape from `ppt/diagrams/drawing*.xml`. * * @example * ```ts * const shape: PptxSmartArtDrawingShape = { * id: "s1", * shapeType: "roundRect", * x: 100, y: 50, width: 200, height: 80, * fillColor: "#4F81BD", * text: "CEO", * }; * // => satisfies PptxSmartArtDrawingShape * ``` */ interface PptxSmartArtDrawingShape extends PptxCustomPathProperties { /** Shape ID within the drawing. */ id: string; /** Preset geometry type (e.g. "roundRect", "ellipse"). */ shapeType?: string; /** Position and size in EMU-based pixels. */ x: number; y: number; width: number; height: number; /** Rotation in degrees. */ rotation?: number; /** Skew along the X axis in degrees. */ skewX?: number; /** Skew along the Y axis in degrees. */ skewY?: number; /** * The cached shape declares `a:noFill`. Renderers must leave it unpainted * rather than substituting a palette colour, because these shapes usually sit * on top of a painted shape whose fill has to stay visible. */ fillNone?: boolean; /** Solid fill colour (hex). */ fillColor?: string; /** * Gradient fill stops when the cached shape uses `a:gradFill`. Positions are * 0..100 (percent). Renderers emit an SVG/CSS gradient instead of a flat box. */ fillGradientStops?: Array<{ color: string; position: number; opacity?: number; }>; /** Gradient geometry type (`linear` for `a:lin`, `radial` for `a:path`). */ fillGradientType?: 'linear' | 'radial'; /** Linear gradient angle in degrees (0..360). */ fillGradientAngle?: number; /** Pattern fill preset name from `a:pattFill/@prst` (e.g. "pct50", "cross"). */ fillPatternPreset?: string; /** Pattern fill foreground colour (hex) from `a:pattFill/a:fgClr`. */ fillPatternForegroundColor?: string; /** Pattern fill background colour (hex) from `a:pattFill/a:bgClr`. */ fillPatternBackgroundColor?: string; /** * Relationship id of a picture (blip) fill's embedded image, from * `a:blipFill/a:blip/@r:embed`. The image bytes are resolved separately; see * {@link fillImageUrl}. */ fillBlipEmbedId?: string; /** * Resolved data-URI/URL for a picture (blip) fill, when the embedded image * part could be resolved. Absent when only {@link fillBlipEmbedId} is known. */ fillImageUrl?: string; /** Whether the cached shape carries an outer-shadow effect (`a:effectLst`). */ hasShadow?: boolean; /** Resolved outer-shadow colour (hex), when present. */ shadowColor?: string; /** Stroke colour (hex). */ strokeColor?: string; /** Stroke width in points. */ strokeWidth?: number; /** Text content of the shape. */ text?: string; /** Standard rich-text segments projected from the associated SmartArt node. */ textSegments?: TextSegment[]; /** Font size in points. */ fontSize?: number; /** Font colour (hex). */ fontColor?: string; } /** * Background / outline extracted from `dgm:bg` and `dgm:whole`. * * @example * ```ts * const chrome: PptxSmartArtChrome = { * backgroundColor: "#F0F0F0", * outlineColor: "#333333", * outlineWidth: 1, * }; * // => satisfies PptxSmartArtChrome * ``` */ interface PptxSmartArtChrome { /** Background fill colour (hex). */ backgroundColor?: string; /** Outline stroke colour (hex). */ outlineColor?: string; /** Outline stroke width in points. */ outlineWidth?: number; } /** * Presentation layout variables from `dgm:prSet/dgm:presLayoutVars` (data model) * or `dgm:varLst` (layout definition defaults). * * These drive how the DiagramML layout interpreter arranges points: flow * direction, hierarchy branch style, org-chart mode, and child count limits. * The fallback layout engine can consult them for direction/org-chart hints. * * @example * ```ts * const vars: PptxSmartArtPresLayoutVars = { direction: "rev", orgChart: true }; * // => satisfies PptxSmartArtPresLayoutVars * ``` */ interface PptxSmartArtPresLayoutVars { /** Flow direction (`dgm:dir`): "norm" (default) or "rev" (reversed/RTL). */ direction?: 'norm' | 'rev'; /** Hierarchy branch style (`dgm:hierBranch`): std/init/l/r/hang. */ hierarchyBranch?: 'std' | 'init' | 'l' | 'r' | 'hang'; /** Org-chart mode enabled (`dgm:orgChart`). */ orgChart?: boolean; /** Maximum children per node (`dgm:chMax`, -1 = unbounded). */ childMax?: number; /** Preferred children per node (`dgm:chPref`, -1 = unbounded). */ childPreferred?: number; /** Whether bullets are enabled (`dgm:bulletEnabled`). */ bulletEnabled?: boolean; /** Animation-by-level setting (`dgm:animLvl`). */ animationLevel?: string; /** Animate-one setting (`dgm:animOne`). */ animateOne?: string; /** Allowed resize handles (`dgm:resizeHandles`). */ resizeHandles?: string; } /** * Complete parsed SmartArt data for a {@link SmartArtPptxElement}. * * @example * ```ts * const data: PptxSmartArtData = { * resolvedLayoutType: "hierarchy", * layout: "hierarchy", * colorScheme: "colorful1", * style: "moderate", * nodes: [ * { id: "1", text: "CEO", children: [ * { id: "2", text: "VP Marketing", parentId: "1" }, * ]}, * ], * }; * // => satisfies PptxSmartArtData * ``` */ interface PptxSmartArtData { layoutType?: string; resolvedLayoutType?: SmartArtLayoutType; /** Named layout preset (used when creating new SmartArt). */ layout?: SmartArtLayout; /** Colour scheme for the SmartArt graphic. */ colorScheme?: SmartArtColorScheme; /** Visual style intensity. */ style?: SmartArtStyle; nodes: PptxSmartArtNode[]; /** Connections between data-model nodes. */ connections?: PptxSmartArtConnection[]; /** Pre-computed shapes from `ppt/diagrams/drawing*.xml`. */ drawingShapes?: PptxSmartArtDrawingShape[]; /** Background and outline chrome from `dgm:bg` / `dgm:whole`. */ chrome?: PptxSmartArtChrome; /** Colour transform from `ppt/diagrams/colors*.xml`. */ colorTransform?: PptxSmartArtColorTransform; /** Quick style from `ppt/diagrams/quickStyles*.xml`. */ quickStyle?: PptxSmartArtQuickStyle; /** Editable metadata from the related DiagramML layout definition. */ layoutDefinition?: PptxSmartArtLayoutDefinition; /** * Presentation layout variables (direction, hierarchy branch, org-chart, * child limits, bullets) from `dgm:presLayoutVars` / layout `dgm:varLst`. * Consulted by the fallback layout engine for direction/org-chart hints. */ presLayoutVars?: PptxSmartArtPresLayoutVars; /** Relationship ID for the diagram data part (for round-trip save). */ dataRelId?: string; /** Relationship ID for the diagram layout part. */ layoutRelId?: string; /** Relationship ID for the drawing part. */ drawingRelId?: string; /** Relationship ID for the colours part. */ colorsRelId?: string; /** Relationship ID for the quick-styles part. */ styleRelId?: string; /** Internal save hint: the layout definition changed in the editor. */ layoutDirty?: boolean; /** Internal save hint: typed layout-definition metadata changed. */ layoutDefinitionDirty?: boolean; /** Internal save hint: quick-style definition metadata changed. */ quickStyleDirty?: boolean; /** Internal save hint: color-transform definition metadata changed. */ colorTransformDirty?: boolean; /** Internal save hint: cached drawing geometry or text changed in the editor. */ drawingDirty?: boolean; } //#endregion //#region src/core/types/table.d.ts /** * Table types: cell styling, cell data, rows, table data, and the parsed * table style map from `ppt/tableStyles.xml`. * * @module pptx-types/table */ /** * Per-cell visual style for a table cell. * * All fields are optional - unset values inherit from the table style. * * @example * ```ts * const header: PptxTableCellStyle = { * bold: true, * fontSize: 14, * color: "#FFFFFF", * backgroundColor: "#0055AA", * align: "center", * }; * // => satisfies PptxTableCellStyle * ``` */ interface PptxTableCellStyle { fontSize?: number; bold?: boolean; italic?: boolean; underline?: boolean; color?: string; /** * Font family from the first run's `a:rPr/a:latin@typeface` (falling back to * `a:ea` / `a:cs`). Per-run families live on {@link PptxTableCellTextRun}. */ fontFamily?: string; /** * Raw XML colour-choice node preserved from `a:tc/a:txBody/.../a:rPr/a:solidFill` * for round-trip serialisation. Currently unused by the cell-level writer * (cell text colour falls through `writeCellTextFormatting`), reserved for * future expansion alongside the run-properties round-trip path. */ colorXml?: XmlObject; backgroundColor?: string; /** * Raw XML colour-choice node preserved from cell `a:tcPr/a:solidFill` for * round-trip serialisation. Re-emitted verbatim when the resolved * {@link backgroundColor} still matches the original colour. */ backgroundColorXml?: XmlObject; borderColor?: string; /** Top border width in px. */ borderTopWidth?: number; /** Bottom border width in px. */ borderBottomWidth?: number; /** Left border width in px. */ borderLeftWidth?: number; /** Right border width in px. */ borderRightWidth?: number; /** Top border color as hex. */ borderTopColor?: string; /** Bottom border color as hex. */ borderBottomColor?: string; /** Left border color as hex. */ borderLeftColor?: string; /** Right border color as hex. */ borderRightColor?: string; align?: 'left' | 'center' | 'right' | 'justify'; vAlign?: 'top' | 'middle' | 'bottom'; /** Text direction from `a:tcPr/@vert` (spec values from CT_TextVerticalType). */ textDirection?: 'vert' | 'vert270' | 'eaVert' | 'wordArtVert' | 'wordArtVertRtl' | 'mongolianVert'; /** Cell left margin in px (from a:tcPr > a:tcMar > a:marL). */ marginLeft?: number; /** Cell right margin in px. */ marginRight?: number; /** Cell top margin in px. */ marginTop?: number; /** Cell bottom margin in px. */ marginBottom?: number; /** Diagonal border top-left to bottom-right color. */ borderDiagDownColor?: string; /** Diagonal border top-left to bottom-right width in px. */ borderDiagDownWidth?: number; /** Diagonal border bottom-left to top-right color. */ borderDiagUpColor?: string; /** Diagonal border bottom-left to top-right width in px. */ borderDiagUpWidth?: number; /** Table cell border dash style (legacy single value). */ borderDash?: string; /** Per-edge border dash styles. */ borderTopDash?: string; borderBottomDash?: string; borderLeftDash?: string; borderRightDash?: string; /** Cell text shadow colour. */ textShadowColor?: string; /** Cell text shadow blur radius in px. */ textShadowBlur?: number; /** Cell text shadow horizontal offset in px. */ textShadowOffsetX?: number; /** Cell text shadow vertical offset in px. */ textShadowOffsetY?: number; /** Cell text shadow opacity (0-1). */ textShadowOpacity?: number; /** Cell text glow colour. */ textGlowColor?: string; /** Cell text glow radius in px. */ textGlowRadius?: number; /** Cell text glow opacity (0-1). */ textGlowOpacity?: number; /** Cell fill mode: solid, gradient, pattern, image, or none. */ fillMode?: 'solid' | 'gradient' | 'pattern' | 'image' | 'none'; /** Gradient fill stops (colours with positions). */ gradientFillStops?: Array<{ color: string; position: number; opacity?: number; }>; /** Gradient angle in degrees. */ gradientFillAngle?: number; /** Gradient type: linear or radial. */ gradientFillType?: 'linear' | 'radial'; /** Path gradient sub-type. */ gradientFillPathType?: 'circle' | 'rect' | 'shape'; /** Focal point for radial gradients (0–1 fractions). */ gradientFillFocalPoint?: { x: number; y: number; }; /** Raw fillToRect LTRB values (0–1 fractions) for gradient sizing. */ gradientFillFillToRect?: { l: number; t: number; r: number; b: number; }; /** Pre-computed CSS gradient string for rendering. */ gradientFillCss?: string; /** Pattern fill preset name (e.g. "ltDnDiag"). */ patternFillPreset?: string; /** Pattern fill foreground colour. */ patternFillForeground?: string; /** Pattern fill background colour. */ patternFillBackground?: string; /** * Image fill (`a:tcPr/a:blipFill`, CT_TableCellProperties). Resolved * archive-relative path (or external `http(s):`/`data:` URL) for the * cell's background image, from `a:blipFill/a:blip/@r:embed` (or * `@r:link`). Present when `fillMode` is `'image'`. * * The parser resolves this synchronously (path only, no binary read); * a viewer's load pipeline resolves it further to a displayable * `data:`/`blob:` URL, written back to {@link backgroundImageFillData}. */ backgroundImageFillPath?: string; /** * Displayable image data (`data:` or `blob:` URL) for an image cell * fill, once resolved by the load pipeline. Renderers should prefer * this over {@link backgroundImageFillPath} when both are present. */ backgroundImageFillData?: string; /** * Cell 3D bevel + lighting from `a:tcPr/a:cell3D` (CT_Cell3D, * ECMA-376 §21.1.3.1). Rendered as a CSS bevel treatment. */ cell3D?: PptxTableCell3D; /** * `a:tcPr/@anchorCtr` - centre the text block in the direction * perpendicular to the text flow (horizontal centring for horizontal text). */ anchorCtr?: boolean; /** * `a:tcPr/@horzOverflow` (ST_TextHorzOverflowType): `clip` clips text at * the cell edge, `overflow` (the default) lets it spill. */ horzOverflow?: 'clip' | 'overflow'; } /** * Cell 3D bevel + lighting parsed from `a:tcPr/a:cell3D` (CT_Cell3D). * * Only the fields needed to render a plausible bevel treatment are captured; * verbatim round-trip of the full node is handled separately by the save path. * * @example * ```ts * const c3d: PptxTableCell3D = { * bevelWidth: 8, * bevelHeight: 8, * bevelPreset: 'circle', * material: 'plastic', * }; * // => satisfies PptxTableCell3D * ``` */ interface PptxTableCell3D { /** Bevel width in px (from `a:bevel@w`, EMU converted). */ bevelWidth?: number; /** Bevel height in px (from `a:bevel@h`, EMU converted). */ bevelHeight?: number; /** Bevel preset name (`a:bevel@prst`, e.g. `circle`, `relaxedInset`). */ bevelPreset?: string; /** Preset material (`a:cell3D@prstMaterial`, e.g. `plastic`, `metal`). */ material?: string; /** Light rig type (`a:lightRig@rig`, e.g. `threePt`, `soft`). */ lightRig?: string; /** Light rig direction (`a:lightRig@dir`, e.g. `tl`, `t`, `tr`). */ lightRigDirection?: string; } /** * One styled text run inside a table cell's `a:txBody`. * * `PptxTableCell.text` is a flat string and `PptxTableCell.style` describes * only the FIRST run, so a cell mixing formats ("Revenue **grew 42%** last * year") cannot be represented by those two alone. {@link PptxTableCell.runs} * carries the full sequence, with paragraph and line breaks as marker entries * so a renderer can walk it linearly. * * Structurally identical to `pptx-viewer-shared`'s `CellTextRun`, which every * binding's table renderer already consumes. * * @example * ```ts * const runs: PptxTableCellTextRun[] = [ * { text: "Revenue " }, * { text: "grew 42%", bold: true, color: "#C00000" }, * ]; * // => satisfies PptxTableCellTextRun[] * ``` */ interface PptxTableCellTextRun { /** Run text. Empty for the break markers below. */ text: string; /** This entry starts a new paragraph (`a:p` boundary) rather than carrying text. */ isParagraphBreak?: boolean; /** This entry is a soft line break (`a:br`) rather than carrying text. */ isLineBreak?: boolean; bold?: boolean; italic?: boolean; underline?: boolean; strikethrough?: boolean; /** Resolved run colour as a CSS colour string. */ color?: string; /** Run font size in points (`a:rPr@sz` / 100). */ fontSize?: number; /** Run font family from `a:rPr/a:latin@typeface` (or `a:ea` / `a:cs`). */ fontFamily?: string; } /** * A single table cell with text content, optional style, and merge info. * * @example * ```ts * const cell: PptxTableCell = { * text: "$1.5M", * style: { bold: true, align: "right" }, * gridSpan: 1, * }; * // => satisfies PptxTableCell * ``` */ interface PptxTableCell { text: string; style?: PptxTableCellStyle; /** * Per-run formatting for the cell's text, when it has any beyond what * {@link style} can express. Present only for cells whose `a:txBody` * actually carries runs; renderers fall back to {@link text} when absent. * * Editing a cell's text invalidates these (the editor produces a plain * string), so an edit path must clear them alongside setting `text`. */ textRuns?: PptxTableCellTextRun[]; /** Column span (defaults to 1). */ gridSpan?: number; /** Row span (defaults to 1). */ rowSpan?: number; /** Whether this cell is merged vertically with the cell above. */ vMerge?: boolean; /** Whether this cell is horizontally merged with the cell to the left (gridSpan continuation). */ hMerge?: boolean; /** * Opaque round-trip storage for `a:tcPr` attributes that don't yet have * typed equivalents on {@link PptxTableCellStyle} (e.g. `horzOverflow`, * `anchorCtr`, `headers`, `hideSlicers`, `slicerCacheId`). Keys are the * raw XML attribute names without the `@_` prefix used by * fast-xml-parser. Re-emitted verbatim by the save writer when present. */ extraAttributes?: Record; } /** * A single table row with an optional height and an array of cells. * * @example * ```ts * const row: PptxTableRow = { * height: 40, * cells: [ * { text: "Name" }, * { text: "Score" }, * ], * }; * // => satisfies PptxTableRow * ``` */ interface PptxTableRow { /** Row height in px. */ height?: number; cells: PptxTableCell[]; } /** * Complete parsed table data for a {@link TablePptxElement}. * * Includes row/cell data, column widths, banding flags, and the applied * table style ID. * * @example * ```ts * const data: PptxTableData = { * rows: [ * { cells: [{ text: "Product" }, { text: "Revenue" }] }, * { cells: [{ text: "Widget A" }, { text: "$3.4M" }] }, * ], * columnWidths: [0.6, 0.4], * firstRowHeader: true, * bandedRows: true, * }; * // => satisfies PptxTableData * ``` */ interface PptxTableData { rows: PptxTableRow[]; /** Column widths as proportion of total (summing to 1). */ columnWidths: number[]; /** Whether the table has banded rows. */ bandedRows?: boolean; /** Whether the first row is a header. */ firstRowHeader?: boolean; /** Whether banded columns are enabled. */ bandedColumns?: boolean; /** Whether the last row is styled as a total row. */ lastRow?: boolean; /** Whether the first column is styled as a header column. */ firstCol?: boolean; /** Whether the last column is styled specially. */ lastCol?: boolean; /** Table style ID from `a:tblPr/a:tblStyle@val` or `a:tblPr@tblStyle`. */ tableStyleId?: string; /** Number of rows per banding group (default 1). */ bandRowCycle?: number; /** Number of columns per banding group (default 1). */ bandColCycle?: number; /** Right-to-left table layout from `a:tblPr/@rtl`. */ rtl?: boolean; } /** * A single fill reference within a table style section. * * @example * ```ts * const fill: ParsedTableStyleFill = { * schemeColor: "accent1", * tint: 40000, // 40% tint * }; * // => satisfies ParsedTableStyleFill * ``` */ interface ParsedTableStyleFill { /** * Theme colour key (e.g. `accent1`, `dk1`). Empty string when the fill is a * non-scheme fill (explicit sRGB, gradient, pattern, or none) that carries * no theme colour reference; the renderer then resolves {@link color}, * {@link gradient}, {@link pattern}, or {@link noFill} instead. */ schemeColor: string; /** Tint value (0-100 000). */ tint?: number; /** Shade value (0-100 000). */ shade?: number; /** Explicit sRGB hex colour (e.g. `#FF8800`) from `a:srgbClr`. */ color?: string; /** The fill was `a:noFill`: renders transparent and clears lower layers. */ noFill?: boolean; /** Gradient fill parsed from `a:gradFill`. */ gradient?: ParsedTableStyleGradient; /** Preset pattern fill parsed from `a:pattFill`. */ pattern?: ParsedTableStylePattern; /** Image texture fill parsed from `a:blipFill`. */ image?: ParsedTableStyleImage; } /** * An image texture fill parsed from a table style section's `a:blipFill`. * * `ppt/tableStyles.xml` is a presentation-level part parsed once (not * per-slide), so this mirrors the per-CELL `a:tcPr/a:blipFill` two-field lazy * pattern (`PptxTableCellStyle.backgroundImageFillPath` / * `backgroundImageFillData`): `path` starts out as a raw archive-relative * path (or an already-external `http(s):`/`data:` URL), and a load pipeline * patches it to a displayable URL in `data` once resolved. */ interface ParsedTableStyleImage { /** Archive-relative path, or an already-displayable external/data URL. */ path?: string; /** Displayable URL once a load pipeline has resolved `path`. */ data?: string; } /** A single colour stop within a {@link ParsedTableStyleGradient}. */ interface ParsedTableStyleGradientStop { /** Stop position as a percentage (0-100). */ position: number; /** Stop colour (scheme or explicit sRGB). */ fill: ParsedTableStyleFill; } /** A gradient fill parsed from a table style section's `a:gradFill`. */ interface ParsedTableStyleGradient { /** Ordered colour stops. */ stops: ParsedTableStyleGradientStop[]; /** Linear gradient angle in degrees (from `a:lin@ang`, 60000ths -> deg). */ angle?: number; /** Gradient family: linear (`a:lin`) or radial (`a:path`). */ type: 'linear' | 'radial'; } /** A preset pattern fill parsed from a table style section's `a:pattFill`. */ interface ParsedTableStylePattern { /** OOXML preset name (e.g. `ltDnDiag`) from `a:pattFill@prst`. */ preset: string; /** Foreground colour (`a:fgClr`). */ foreground?: ParsedTableStyleFill; /** Background colour (`a:bgClr`). */ background?: ParsedTableStyleFill; } /** * A single entry in the parsed table style map. * * Contains fill colours for whole-table, banded rows/columns, first/last * row, and first/last column sections. * * @example * ```ts * const entry: ParsedTableStyleEntry = { * styleId: "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}", * styleName: "Medium Style 2 - Accent 1", * accentKey: "accent1", * wholeTblFill: { schemeColor: "accent1", tint: 20000 }, * band1HFill: { schemeColor: "accent1", tint: 40000 }, * firstRowFill: { schemeColor: "accent1" }, * }; * // => satisfies ParsedTableStyleEntry * ``` */ /** Text properties from a:tcTxStyle in a table style section. */ interface ParsedTableStyleText { /** Font bold. */ bold?: boolean; /** Font italic. */ italic?: boolean; /** Font underline (from `a:tcTxStyle@u`, any value other than `none`). */ underline?: boolean; /** Font colour as theme scheme key. */ fontSchemeColor?: string; /** Font colour tint (0-100 000). */ fontTint?: number; /** Font colour shade (0-100 000). */ fontShade?: number; /** Explicit sRGB hex font colour (e.g. `#FF0000`) from `a:srgbClr`. */ fontColor?: string; /** Typeface from `a:font@typeface` (latin font). */ fontFace?: string; /** Font-collection index from `a:fontRef@idx` (`minor`, `major`, `none`). */ fontRefIdx?: string; } /** * A single border side within a table style's `a:tcStyle/a:tcBdr`. * * Corresponds to one of `a:left`, `a:right`, `a:top`, `a:bottom`, * `a:insideH`, `a:insideV`, `a:tl2br`, `a:bl2tr` (each a * `CT_ThemeableLineStyle` wrapping an `a:ln`). * * @example * ```ts * const side: ParsedTableStyleBorder = { * width: 1, * dash: 'solid', * fill: { schemeColor: 'lt1' }, * }; * // => satisfies ParsedTableStyleBorder * ``` */ interface ParsedTableStyleBorder { /** Line width in px (converted from the `a:ln@w` EMU value). */ width?: number; /** OOXML `a:prstDash@val` (e.g. `solid`, `dash`, `sysDot`). */ dash?: string; /** Border colour as a theme scheme fill (from `a:ln/a:solidFill/a:schemeClr`). */ fill?: ParsedTableStyleFill; /** Explicit hex colour when the line used `a:srgbClr` (e.g. `#808080`). */ color?: string; /** The line was `a:noFill` - an explicit "no border" that clears lower layers. */ noFill?: boolean; } /** * The set of border sides parsed from a table style section's * `a:tcStyle/a:tcBdr` element. */ interface ParsedTableStyleBorders { left?: ParsedTableStyleBorder; right?: ParsedTableStyleBorder; top?: ParsedTableStyleBorder; bottom?: ParsedTableStyleBorder; /** Interior horizontal borders between rows in the region. */ insideH?: ParsedTableStyleBorder; /** Interior vertical borders between columns in the region. */ insideV?: ParsedTableStyleBorder; /** Top-left to bottom-right diagonal. */ tl2br?: ParsedTableStyleBorder; /** Bottom-left to top-right diagonal. */ bl2tr?: ParsedTableStyleBorder; } /** * Table background style (CT_TableBackgroundStyle, ECMA-376 §21.1.3.7). * * Corresponds to the `` child of ``. Currently * captures only the resolved scheme-fill colour (verbatim XML for fill * / effect references is preserved separately by the save path). */ interface ParsedTableBackground { /** Solid fill (resolved from `a:fill > a:solidFill > a:schemeClr`). */ fill?: ParsedTableStyleFill; /** Has an `a:effectLst` child that should be round-tripped. */ hasEffectLst?: boolean; } interface ParsedTableStyleEntry { styleId: string; styleName?: string; /** Dominant accent key derived from fills (e.g. `accent1`). */ accentKey?: string; /** Table-level background (``). */ tableBackground?: ParsedTableBackground; wholeTblFill?: ParsedTableStyleFill; band1HFill?: ParsedTableStyleFill; band2HFill?: ParsedTableStyleFill; band1VFill?: ParsedTableStyleFill; band2VFill?: ParsedTableStyleFill; firstRowFill?: ParsedTableStyleFill; lastRowFill?: ParsedTableStyleFill; firstColFill?: ParsedTableStyleFill; lastColFill?: ParsedTableStyleFill; /** Corner cell fills (``, ``, ``, ``). */ seCellFill?: ParsedTableStyleFill; swCellFill?: ParsedTableStyleFill; neCellFill?: ParsedTableStyleFill; nwCellFill?: ParsedTableStyleFill; /** * Per-role border styling from `a:tcStyle/a:tcBdr`. These supply the * gridlines/edges a styled table inherits from its table style when the * cells carry no explicit per-cell `a:lnX` overrides. */ wholeTblBorders?: ParsedTableStyleBorders; firstRowBorders?: ParsedTableStyleBorders; lastRowBorders?: ParsedTableStyleBorders; firstColBorders?: ParsedTableStyleBorders; lastColBorders?: ParsedTableStyleBorders; band1HBorders?: ParsedTableStyleBorders; band2HBorders?: ParsedTableStyleBorders; band1VBorders?: ParsedTableStyleBorders; band2VBorders?: ParsedTableStyleBorders; seCellBorders?: ParsedTableStyleBorders; swCellBorders?: ParsedTableStyleBorders; neCellBorders?: ParsedTableStyleBorders; nwCellBorders?: ParsedTableStyleBorders; /** Per-role text styling from a:tcTxStyle. */ wholeTblText?: ParsedTableStyleText; firstRowText?: ParsedTableStyleText; lastRowText?: ParsedTableStyleText; firstColText?: ParsedTableStyleText; lastColText?: ParsedTableStyleText; band1HText?: ParsedTableStyleText; band2HText?: ParsedTableStyleText; band1VText?: ParsedTableStyleText; band2VText?: ParsedTableStyleText; seCellText?: ParsedTableStyleText; swCellText?: ParsedTableStyleText; neCellText?: ParsedTableStyleText; nwCellText?: ParsedTableStyleText; } /** * Map of GUID → table style entry. * * Parsed from `ppt/tableStyles.xml` and indexed by the style GUID * referenced in `a:tblPr@tblStyle`. * * @example * ```ts * const styles: ParsedTableStyleMap = { * "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}": { * styleId: "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}", * styleName: "Medium Style 2 - Accent 1", * accentKey: "accent1", * }, * }; * // => satisfies ParsedTableStyleMap * ``` */ type ParsedTableStyleMap = Record; //#endregion //#region src/core/types/elements.d.ts /** * A text box — a plain rectangle containing text, typically with no * visible fill or stroke. * * @example * ```ts * const title: TextPptxElement = { * type: "text", * id: "txt_1", x: 50, y: 30, width: 800, height: 60, * text: "Welcome", * textStyle: { fontSize: 36, bold: true }, * }; * // => satisfies TextPptxElement * ``` */ interface TextPptxElement extends PptxElementBase, PptxTextProperties, PptxShapeProperties { type: 'text'; } /** * A shape — may contain text and custom geometry (preset or freeform). * * @example * ```ts * const rect: ShapePptxElement = { * type: "shape", * id: "shp_1", x: 100, y: 200, width: 300, height: 150, * shapeType: "roundRect", * shapeStyle: { fillColor: "#00AA55" }, * text: "OK", * }; * // => satisfies ShapePptxElement * ``` */ interface ShapePptxElement extends PptxElementBase, PptxTextProperties, PptxShapeProperties, PptxCustomPathProperties { type: 'shape'; } /** * A connector (straight, bent, or curved line between shapes). * * Connector endpoints can snap to specific shapes via * `shapeStyle.connectorStartConnection` / `connectorEndConnection`. * * @example * ```ts * const line: ConnectorPptxElement = { * type: "connector", * id: "cxn_1", x: 100, y: 100, width: 200, height: 0, * shapeStyle: { * strokeColor: "#333", * connectorEndArrow: "triangle", * }, * }; * // => satisfies ConnectorPptxElement * ``` */ interface ConnectorPptxElement extends PptxElementBase, PptxTextProperties, PptxShapeProperties { type: 'connector'; } /** * An image element from an OOXML `` node with `type: "image"`. * * @example * ```ts * const img: ImagePptxElement = { * type: "image", * id: "img_1", x: 0, y: 0, width: 960, height: 540, * imagePath: "ppt/media/image1.png", * altText: "Background scenery", * }; * // => satisfies ImagePptxElement * ``` */ interface ImagePptxElement extends PptxElementBase, PptxShapeProperties, PptxCustomPathProperties, PptxImageProperties { type: 'image'; } /** * A picture element from an OOXML `` node with `type: "picture"`. * * Functionally identical to {@link ImagePptxElement} but distinguished by * the `type` discriminant for semantic clarity. */ interface PicturePptxElement extends PptxElementBase, PptxShapeProperties, PptxCustomPathProperties, PptxImageProperties { type: 'picture'; } /** * A single unrecognised `//` extension on a * graphicFrame, captured verbatim so the round-trip can preserve future or * vendor-specific markup that the parser doesn't yet understand. * * The XML is preserved as a fast-xml-parser object tree (the same shape as * `rawXml` on other elements) so the save layer can re-emit it through the * existing builder without lossy string manipulation. */ interface PptxGraphicFrameExtension { /** The `@_uri` attribute identifying the extension (e.g. `{C3CD43...}`). */ uri: string; /** Parsed XML payload of the extension, suitable for re-serialization. */ xml: XmlObject; } /** * A table embedded via a ``. * * @example * ```ts * const tbl: TablePptxElement = { * type: "table", * id: "tbl_1", x: 50, y: 200, width: 860, height: 300, * tableData: { * rows: [ * { cells: [{ text: "Name" }, { text: "Score" }] }, * { cells: [{ text: "Alice" }, { text: "95" }] }, * ], * }, * }; * // => satisfies TablePptxElement * ``` */ interface TablePptxElement extends PptxElementBase { type: 'table'; /** Parsed table cell data for editing. */ tableData?: PptxTableData; /** * Unrecognised extensions captured from `a:graphicData/a:extLst` so they * round-trip losslessly. See {@link PptxGraphicFrameExtension}. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A chart embedded via a ``. * * Chart data is parsed from the related `chartN.xml` / `chartExN.xml` * parts inside the PPTX archive. */ interface ChartPptxElement extends PptxElementBase { type: 'chart'; chartData?: PptxChartData; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A SmartArt diagram embedded via a ``. * * SmartArt data is extracted from `dgm:dataModel` parts. The editor * supports real structural editing (adding, removing and reordering nodes, * editing node text, and switching layout presets) with a lossless * `ptLst` round-trip. When the file carries PowerPoint's own pre-computed * drawing part, that exact layout is used; otherwise an algorithmic layout * engine approximates it, so complex custom layouts may not match * PowerPoint pixel-for-pixel. */ interface SmartArtPptxElement extends PptxElementBase { type: 'smartArt'; smartArtData?: PptxSmartArtData; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * Recognised OLE object application types derived from `progId` / `clsId`. * * Used to show type-specific icons and previews in the editor. */ type OleObjectType = 'excel' | 'word' | 'pdf' | 'visio' | 'mathtype' | 'package' | 'unknown'; /** * An OLE (Object Linking and Embedding) object. * * OLE objects can be embedded Excel sheets, Word documents, PDFs, Visio * diagrams, MathType equations, or generic "packages". They carry a * preview image for display and optional binary data for extraction. * * @example * ```ts * const ole: OlePptxElement = { * type: "ole", * id: "ole_1", x: 100, y: 200, width: 400, height: 300, * oleObjectType: "excel", * oleProgId: "Excel.Sheet.12", * fileName: "budget.xlsx", * }; * // => satisfies OlePptxElement * ``` */ interface OlePptxElement extends PptxElementBase { type: 'ole'; oleTarget?: string; oleProgId?: string; oleName?: string; /** CLSID of the OLE object (from `@_classid`). */ oleClsId?: string; /** Detected application type (excel, word, pdf, etc.). */ oleObjectType?: OleObjectType; /** File extension for the embedded binary (e.g. "xlsx", "docx"). */ oleFileExtension?: string; /** Original file name when available. */ fileName?: string; /** Whether this is a linked (vs. embedded) object. */ isLinked?: boolean; /** External file path for linked OLE objects (TargetMode="External"). */ externalPath?: string; /** Data-URL or path for the OLE preview image. */ previewImage?: string; /** Decoded preview image as a data-URL. */ previewImageData?: string; /** Whether the OLE object is shown as an icon (`p:oleObj/@showAsIcon`). */ oleShowAsIcon?: boolean; /** Authored display width of the OLE object preview, in EMU (`@imgW`). */ oleImgW?: number; /** Authored display height of the OLE object preview, in EMU (`@imgH`). */ oleImgH?: number; /** * The recovered embedded payload as a data-URL (e.g. * `data:application/vnd...;base64,...`), suitable for download or * open-in-new-tab. For a generic "Package" OLE object this is the unwrapped * inner file; for a plain embedded file (e.g. `.xlsx`) it is that file * directly. Undefined when the embedding is missing or unreadable. * * Stored as a data-URL string to mirror how images store decoded bytes * ({@link ImagePptxElement.imageData}) and to stay serialization-safe. */ oleEmbeddedData?: string; /** Original file name of the embedded payload when recoverable. */ oleEmbeddedFileName?: string; /** MIME type of the embedded payload, derived from its extension/ProgID. */ oleEmbeddedMimeType?: string; /** Size of the embedded payload in bytes. */ oleEmbeddedByteSize?: number; /** * `p:link/@followColorScheme` (`ST_OleObjectFollowColorScheme`): whether a * LINKED OLE object's icon recolours to match the presentation theme. * Only meaningful when {@link isLinked} is `true`. ECMA-376 §19.3.1.28. */ oleFollowColorScheme?: 'none' | 'full' | 'textAndBackground'; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * An audio or video media element. * * Media elements reference files inside the PPTX archive * (`mediaPath`) and may include trim points, poster frames, and * playback settings for presentation mode. * * @example * ```ts * const video: MediaPptxElement = { * type: "media", * id: "vid_1", x: 50, y: 100, width: 640, height: 360, * mediaType: "video", * mediaPath: "ppt/media/media1.mp4", * autoPlay: true, * volume: 0.8, * }; * // => satisfies MediaPptxElement * ``` */ interface MediaPptxElement extends PptxElementBase { type: 'media'; mediaType?: PptxMediaType; mediaPath?: string; mediaData?: string; mediaMimeType?: string; mediaReferenceKind?: PptxMediaReferenceKind; mediaReferenceName?: string; /** Explicit DrawingML `audioFile/@contentType` value when present. */ mediaReferenceContentType?: string; audioCdStart?: PptxAudioCdPosition; audioCdEnd?: PptxAudioCdPosition; rawMediaReferenceXml?: XmlObject; /** Trim start in milliseconds (from p:cMediaNode p:cTn @st). */ trimStartMs?: number; /** Trim end in milliseconds (from p:cMediaNode p:cTn @end). */ trimEndMs?: number; /** Path to the poster/preview image inside the ZIP. */ posterFramePath?: string; /** Base64 data-URL for the poster frame image. */ posterFrameData?: string; /** Whether media should play full-screen during presentation. */ fullScreen?: boolean; /** Whether media should loop continuously. */ loop?: boolean; /** Fade-in duration in seconds. */ fadeInDuration?: number; /** Fade-out duration in seconds. */ fadeOutDuration?: number; /** Playback volume (0 to 1). */ volume?: number; /** Whether media auto-plays on slide entry. */ autoPlay?: boolean; /** Whether audio continues playing across slide transitions (presentation mode). */ playAcrossSlides?: boolean; /** Hide the element when media is not actively playing. */ hideWhenNotPlaying?: boolean; /** Named time bookmarks within the clip. */ bookmarks?: MediaBookmark[]; /** Playback speed multiplier (1 = normal, 2 = double, 0.5 = half). */ playbackSpeed?: number; /** Runtime-extracted metadata (duration, resolution, codec). */ metadata?: MediaMetadata; /** Closed caption / subtitle tracks. */ captionTracks?: MediaCaptionTrack[]; /** Whether the media source is missing/broken (file not found in archive). */ mediaMissing?: boolean; /** * Whether the media is linked (external `r:link`) rather than embedded * (`r:embed`). Defaults to embedded when undefined. */ isLinked?: boolean; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A group container that holds child elements. * * Children inherit the group’s transform, so moving/resizing the group * affects all children proportionally. * * @example * ```ts * const group: GroupPptxElement = { * type: "group", * id: "grp_1", x: 0, y: 0, width: 960, height: 540, * children: [textEl, shapeEl], * }; * // => satisfies GroupPptxElement * ``` */ interface GroupPptxElement extends PptxElementBase { type: 'group'; /** Child elements contained within this group. */ children: PptxElement[]; /** Fill style extracted from the group's `p:grpSpPr`, used for `a:grpFill` inheritance. */ groupFill?: ShapeStyle; } /** * A freehand ink / drawing stroke captured with a stylus or mouse. * * Ink strokes are stored as SVG path data strings. Each path may * have independent colour, width, and opacity. */ interface InkPptxElement extends PptxElementBase { type: 'ink'; /** SVG path data for ink strokes. */ inkPaths: string[]; /** Per-path stroke colours. */ inkColors?: string[]; /** Per-path stroke widths. */ inkWidths?: number[]; /** Per-path opacities (0-1). */ inkOpacities?: number[]; /** Drawing tool used: pen, highlighter, or eraser. */ inkTool?: 'pen' | 'highlighter' | 'eraser'; /** * Per-path arrays of per-point pressure values (0-1). * * Each entry corresponds to the path at the same index in `inkPaths`. * Each inner array contains one pressure value per sampled point along * the stroke. When present, the renderer uses these values to produce * variable-width strokes that reflect stylus/pen pressure. */ inkPointPressures?: number[][]; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A single ink stroke within a {@link ContentPartPptxElement}. */ interface ContentPartInkStroke { path: string; color: string; width: number; opacity: number; /** * Per-point pressure values (0-1) for this stroke. * * When present, the renderer uses these values to produce * variable-width strokes that reflect stylus/pen pressure. */ pressures?: number[]; /** * Per-point pen-tilt lean direction (radians), decoded from the source * InkML's `OTx`/`OTy` tilt-offset channels or its `AZIMUTH` channel. * * When present (paired with {@link tiltMagnitudes}), the renderer widens * each point perpendicular to the lean direction, approximating a * calligraphic (chisel-tip) nib. Absent when the source declared no tilt * channel, in which case rendering is unaffected. */ tiltAngles?: number[]; /** * Per-point pen-tilt strength (0 upright, 1 maximally leaned), paired with * {@link tiltAngles}. */ tiltMagnitudes?: number[]; } /** * A content-part element wrapped in `mc:AlternateContent`. * * Typically contains ink strokes from modern PowerPoint pen/highlighter. */ interface ContentPartPptxElement extends PptxElementBase { type: 'contentPart'; /** Ink strokes contained in this content part. */ inkStrokes?: ContentPartInkStroke[]; /** Package path of the related InkML part. */ inkPartPath?: string; /** Parsed InkML root retained for unknown-node preservation on dirty save. */ inkPartRawXml?: XmlObject; } /** * A Slide Zoom or Section Zoom element (PowerPoint Zoom Object). * * Zoom elements display a live thumbnail of the target slide and * navigate to it on click during presentation mode. * * @example * ```ts * const zoom: ZoomPptxElement = { * type: "zoom", * id: "zm_1", x: 300, y: 200, width: 200, height: 120, * zoomType: "slide", * targetSlideIndex: 5, * }; * // => satisfies ZoomPptxElement * ``` */ interface ZoomPptxElement extends PptxElementBase, PptxImageProperties { type: 'zoom'; /** Type of zoom: slide-level, section-level, or a multi-section summary. */ zoomType: 'slide' | 'section' | 'summary'; /** Zero-based index of the target slide. */ targetSlideIndex: number; /** Section ID for section zoom. */ targetSectionId?: string; /** Ordered section tiles in a Summary Zoom container. */ summaryTargets?: SummaryZoomTarget[]; /** Layout mode authored on the Summary Zoom container. */ summaryLayout?: 'grid' | 'fixed'; } /** A single section tile within a PowerPoint Summary Zoom container. */ interface SummaryZoomTarget extends PptxImageProperties { sectionId: string; targetSlideIndex: number; x: number; y: number; width: number; height: number; title?: string; description?: string; offsetFactorX?: number; offsetFactorY?: number; scaleFactorX?: number; scaleFactorY?: number; rawXml?: XmlObject; } /** * A 3D model object embedded via `p16:model3D` inside an * `mc:AlternateContent` block (PowerPoint 365+). * * The element carries the path to the `.glb`/`.gltf` binary inside * the ZIP and a poster/fallback image for rendering in viewers that * do not support interactive 3D. */ interface Model3DPptxElement extends PptxElementBase, PptxImageProperties { type: 'model3d'; /** Path to the 3D model file inside the ZIP. */ modelPath?: string; /** Base64 data URL of the 3D model binary. */ modelData?: string; /** MIME type of the model (e.g. "model/gltf-binary"). */ modelMimeType?: string; /** Poster/preview image shown when 3D rendering is unavailable. */ posterImage?: string; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** An element whose type is not recognised by the parser. */ interface UnknownPptxElement extends PptxElementBase { type: 'unknown'; /** Unrecognised graphicFrame extLst extensions, captured verbatim for round-trip. */ extensionXml?: PptxGraphicFrameExtension[]; } /** * A single element on a PPTX slide. * * This is a **discriminated union**: narrow on `element.type` to access * variant-specific properties like `imageData` (image/picture), `pathData` * (shape), or `textSegments` (text/shape). */ type PptxElement = TextPptxElement | ShapePptxElement | ConnectorPptxElement | ImagePptxElement | PicturePptxElement | TablePptxElement | ChartPptxElement | SmartArtPptxElement | OlePptxElement | MediaPptxElement | GroupPptxElement | InkPptxElement | ContentPartPptxElement | ZoomPptxElement | Model3DPptxElement | UnknownPptxElement; //#endregion //#region src/core/types/masters.d.ts /** * A placeholder slot declared on a master or layout. * * The geometry fields are in CSS pixels (EMU / {@link EMU_PER_PX}) and are only * present when the shape carried an explicit `a:xfrm`. Placeholders that * inherit their frame from the master leave them undefined, so consumers that * draw placeholder outlines (the layout gallery) must skip those entries * rather than assume a zero-sized box at the origin. * * @example * ```ts * const frame: PptxPlaceholderFrame = { type: "body", idx: "1", x: 63, y: 130 }; * // => satisfies PptxPlaceholderFrame * ``` */ interface PptxPlaceholderFrame { /** `p:ph/@type`, lower-cased by the parser; defaults to `body` when omitted. */ type: string; /** `p:ph/@idx`, when present. */ idx?: string; /** Left offset in CSS pixels, when the shape declares `a:off`. */ x?: number; /** Top offset in CSS pixels, when the shape declares `a:off`. */ y?: number; /** Width in CSS pixels, when the shape declares `a:ext`. */ width?: number; /** Height in CSS pixels, when the shape declares `a:ext`. */ height?: number; } /** * Parsed notes master from `ppt/notesMasters/notesMaster1.xml`. * * @example * ```ts * const notes: PptxNotesMaster = { * path: "ppt/notesMasters/notesMaster1.xml", * backgroundColor: "#FFFFFF", * placeholders: [{ type: "body" }, { type: "sldImg" }], * }; * // => satisfies PptxNotesMaster * ``` */ interface PptxNotesMaster { /** File path within the PPTX archive. */ path: string; /** Background colour of the notes master. */ backgroundColor?: string; /** Background image data URL. */ backgroundImage?: string; /** Placeholder shapes found on the notes master. */ placeholders?: PptxPlaceholderFrame[]; /** Editable elements on the notes master (header, footer, date, page number, slide image, notes body). */ elements?: PptxElement[]; /** Header/footer flags from `` on the notes master (P-H3). */ headerFooter?: PptxHeaderFooterFlags; /** Colour map from `` (12 alias attributes). Applied at save time. */ clrMap?: Record; /** * Notes text defaults from `` (`CT_TextListStyle`, ECMA-376 * §19.3.1.34): the same `a:defPPr` + `a:lvl1pPr`..`a:lvl9pPr` shape as * {@link PptxMasterTextStyles}'s per-category styles, keyed 0-8 with the * default at `-1`. Governs the notes body placeholder's font size, indent * levels, and bullet style wherever a notes slide does not override them. * Parsed read-only for the render cascade; the save side preserves the * original `` XML verbatim rather than re-serialising this. */ notesStyle?: PptxTextStyleLevels; } /** * Parsed handout master from `ppt/handoutMasters/handoutMaster1.xml`. * * @example * ```ts * const handout: PptxHandoutMaster = { * path: "ppt/handoutMasters/handoutMaster1.xml", * slidesPerPage: 6, * }; * // => satisfies PptxHandoutMaster * ``` */ interface PptxHandoutMaster { /** File path within the PPTX archive. */ path: string; /** Background colour of the handout master. */ backgroundColor?: string; /** Background image data URL. */ backgroundImage?: string; /** Placeholder shapes found on the handout master. */ placeholders?: PptxPlaceholderFrame[]; /** Editable elements on the handout master (header, footer, date, page number, slide placeholders). */ elements?: PptxElement[]; /** Number of slides per page for handout print layout (1, 2, 3, 4, 6, or 9). */ slidesPerPage?: number; /** Header/footer flags from `` on the handout master (P-H3). */ headerFooter?: PptxHeaderFooterFlags; /** Colour map from `` (12 alias attributes). Applied at save time. */ clrMap?: Record; } /** * Structured slide master data. * * @example * ```ts * const master: PptxSlideMaster = { * path: "ppt/slideMasters/slideMaster1.xml", * name: "Office Theme", * backgroundColor: "#FFFFFF", * themePath: "ppt/theme/theme1.xml", * }; * // => satisfies PptxSlideMaster * ``` */ interface PptxSlideMaster { /** File path within the PPTX archive. */ path: string; /** Human-readable name if available. */ name?: string; /** Background colour of the slide master. */ backgroundColor?: string; /** Background image data URL for the slide master. */ backgroundImage?: string; /** Theme file path this master references. */ themePath?: string; /** Layout paths associated with this master. */ layoutPaths?: string[]; /** Placeholder shapes on the master. */ placeholders?: PptxPlaceholderFrame[]; /** Parsed element shapes on the master slide (for master view rendering). */ elements?: PptxElement[]; /** Parsed slide layout objects associated with this master. */ layouts?: PptxSlideLayout[]; /** Text styles from `p:txStyles` — title, body, and other text defaults. */ txStyles?: PptxMasterTextStyles; /** Header/footer flags from `` on this master (P-H3). */ headerFooter?: PptxHeaderFooterFlags; /** * Colour map from `` (12 alias attributes: bg1/tx1/bg2/tx2, * accent1-6, hlink, folHlink). Applied at save time when present. */ clrMap?: Record; /** * Whether the master is marked as preserved (prevent auto-deletion, * `@preserve`). Mirrors {@link PptxSlideLayout.preserve}: PowerPoint * silently drops an unused master unless this is set. * * ECMA-376 §19.3.1.38 (CT_SlideMaster). */ preserve?: boolean; } /** * Per-level paragraph properties for a text style category. * Each entry maps a 0-based level index to its style defaults. */ type PptxTextStyleLevels = Record; /** * Text styles parsed from `p:txStyles` on a slide master. * Provides cascading defaults for title, body, and other text. */ interface PptxMasterTextStyles { /** Title text style (`p:titleStyle`). */ titleStyle?: PptxTextStyleLevels; /** Body text style (`p:bodyStyle`). */ bodyStyle?: PptxTextStyleLevels; /** Other text style (`p:otherStyle`). */ otherStyle?: PptxTextStyleLevels; } /** * Per-part header/footer flags from `` (CT_HeaderFooter, ECMA-376 * §19.3.1.21). Defaults are "all true" — fields are only set on the typed * model when they were explicitly read, so callers can distinguish "unset" * (preserve original XML) from "false" (override). */ interface PptxHeaderFooterFlags { /** `@hdr` — show header placeholder. Spec default: `true`. */ hasHeader?: boolean; /** `@ftr` — show footer placeholder. Spec default: `true`. */ hasFooter?: boolean; /** `@dt` — show date/time placeholder. Spec default: `true`. */ hasDateTime?: boolean; /** `@sldNum` — show slide-number placeholder. Spec default: `true`. */ hasSlideNumber?: boolean; } /** * A slide layout associated with a slide master. * * @example * ```ts * const layout: PptxSlideLayout = { * path: "ppt/slideLayouts/slideLayout2.xml", * name: "Title and Content", * }; * // => satisfies PptxSlideLayout * ``` */ interface PptxSlideLayout { /** File path within the PPTX archive. */ path: string; /** Human-readable layout name. */ name?: string; /** Background colour of the layout. */ backgroundColor?: string; /** Background image data URL for the layout. */ backgroundImage?: string; /** Parsed element shapes on the layout. */ elements?: PptxElement[]; /** Placeholder shapes on the layout. */ placeholders?: PptxPlaceholderFrame[]; /** Matching name attribute for layout identification (`@matchingName`). */ matchingName?: string; /** Whether the layout is marked as preserved (prevent deletion, `@preserve`). */ preserve?: boolean; /** Whether master placeholder animations should play (`@showMasterPhAnim`). */ showMasterPhAnim?: boolean; /** Whether this layout is user-drawn (`@userDrawn`). */ userDrawn?: boolean; /** Colour map override from `p:clrMapOvr`. */ clrMapOverride?: Record; /** Header/footer flags from `` on this layout (P-H3). */ headerFooter?: PptxHeaderFooterFlags; } /** * Rendered content of a single layout, used to draw gallery thumbnails. * * Produced on demand rather than during load: materialising every layout's * artwork (and decoding its images) up front costs a noticeable amount of time * on decks with many masters, and most sessions never open the layout gallery * at all. * * @example * ```ts * const preview: PptxLayoutPreview = { * path: "ppt/slideLayouts/slideLayout2.xml", * width: 960, * height: 540, * elements: [], * placeholders: [{ type: "title" }], * }; * // => satisfies PptxLayoutPreview * ``` */ interface PptxLayoutPreview { /** ZIP path of the layout this preview belongs to. */ path: string; /** Slide width in CSS pixels, so a thumbnail can compute its own scale. */ width: number; /** Slide height in CSS pixels. */ height: number; /** Background resolved from the layout, falling back to its master's. */ backgroundColor?: string; /** Background image data URL, when the layout or master declares one. */ backgroundImage?: string; /** The layout's own artwork (pictures, shapes and static text). */ elements: PptxElement[]; /** Placeholder slots, drawn as outlined frames in the gallery. */ placeholders: PptxPlaceholderFrame[]; } /** * A theme part available in the presentation package. * * @example * ```ts * const opt: PptxThemeOption = { * path: "ppt/theme/theme1.xml", * name: "Office Theme", * }; * // => satisfies PptxThemeOption * ``` */ interface PptxThemeOption { /** File path within the PPTX archive (e.g. `ppt/theme/theme2.xml`). */ path: string; /** Human-readable theme name from `a:theme/@name`, when present. */ name?: string; } //#endregion //#region src/core/types/animation.d.ts /** * Built-in animation preset names used for entrance, exit, and emphasis effects. * * @example * ```ts * const preset: PptxAnimationPreset = "fadeIn"; * // => "fadeIn" — one of: none | fadeIn | flyIn | zoomIn | fadeOut | flyOut | zoomOut | spin | pulse | ... * ``` */ type PptxAnimationPreset = 'none' | 'appear' | 'fadeIn' | 'flyIn' | 'zoomIn' | 'bounceIn' | 'wipeIn' | 'splitIn' | 'dissolveIn' | 'wheelIn' | 'blindsIn' | 'boxIn' | 'floatIn' | 'riseUp' | 'swivel' | 'expandIn' | 'checkerboardIn' | 'flashIn' | 'peekIn' | 'randomBarsIn' | 'spinnerIn' | 'growTurnIn' | 'fadeOut' | 'flyOut' | 'zoomOut' | 'bounceOut' | 'wipeOut' | 'shrinkOut' | 'dissolveOut' | 'disappear' | 'spin' | 'pulse' | 'colorWave' | 'bounce' | 'flash' | 'growShrink' | 'teeter' | 'transparency' | 'boldFlash' | 'wave'; /** Animation timing curve. */ type PptxAnimationTimingCurve = 'ease' | 'ease-in' | 'ease-out' | 'linear'; /** Repeat mode for animations. */ type PptxAnimationRepeatMode = 'untilNextClick' | 'untilEndOfSlide'; /** Animation trigger type from OOXML `p:cTn`. */ type PptxAnimationTrigger = 'onClick' | 'onShapeClick' | 'onHover' | 'afterPrevious' | 'withPrevious' | 'afterDelay'; /** * Native animation kind. The historic shape-targeted preset animations are * implicitly the default kind (`undefined`). Media animations (`p:audio`, * `p:video`) emit dedicated entries so playback order on the slide timeline * is preserved alongside other animations. */ type PptxNativeAnimationKind = 'media'; /** A target selected by `p:tgtEl` in the PresentationML timing model. */ type PptxAnimationTarget = { type: 'shape'; shapeId: string; rawXml?: XmlObject; } | { type: 'slide'; rawXml?: XmlObject; } | { type: 'sound'; relationshipId: string; name?: string; rawXml?: XmlObject; } | { type: 'ink'; shapeId: string; rawXml?: XmlObject; } | { type: 'unknown'; rawXml: XmlObject; }; /** Nested build choice carried by `p:bldGraphic`. */ type PptxGraphicBuild = { mode: 'asOne'; rawXml?: XmlObject; } | { mode: 'sub'; kind: 'diagram'; build: string; reverse: boolean; rawXml?: XmlObject; } | { mode: 'sub'; kind: 'chart'; build: string; animateBackground: boolean; rawXml?: XmlObject; }; /** * A single `p:tmpl` timing template parsed from a TEXT `p:bldP/p:tmplLst` * (CT_TLTemplate, ECMA-376 §19.5.85; the list itself is CT_TLTemplateList, * §19.5.84). * * PowerPoint writes these as the timing PowerPoint would apply to a build * level that does not yet have an instantiated effect, so that promoting or * demoting an outline paragraph, or adding a new bullet at a level with no * prior animation, has a default to clone. They are not consulted at * playback: the animation actually shown for every paragraph level already * visible on the slide is the real, instantiated `p:tnLst` under * `p:timing/p:tnLst`, which the rest of this parser already models in full. * * The nested time-node tree under each template's own `p:tnLst` is kept as * a preserved `XmlObject` rather than deep-parsed into * {@link PptxNativeAnimation} records: it is schema-identical to the * top-level timing tree but scoped to a template that is never itself * executed, so structurally modelling it would stand up a second, unused * parallel animation model. Parsing stops at typed round-trip; see * `docs/guide/limitations.md`. */ interface PptxTimingTemplate { /** Build level this template targets, from `p:tmpl/@lvl` (ST_TLLevel, default 0). */ level: number; /** Preserved `p:tnLst` (CT_TimeNodeList) subtree, verbatim. */ timeNodeList: XmlObject; /** Preserved `p:tmpl` XML node (its attributes plus any unmodelled children). */ rawXml?: XmlObject; } /** * Parsed native animation record from `p:timing / p:tnLst`. * * Represents a single animation node in the OOXML timing tree, * including motion paths, scale transforms, and text build settings. * * @example * ```ts * const anim: PptxNativeAnimation = { * targetId: "shape_1", * presetClass: "entr", * presetId: 10, * trigger: "afterPrevious", * durationMs: 500, * }; * // => { targetId: "shape_1", presetClass: "entr", presetId: 10, trigger: "afterPrevious", durationMs: 500 } * ``` */ interface PptxNativeAnimation { /** Target element/shape ID. */ targetId?: string; /** Full timing target, including sound and ink target variants. */ target?: PptxAnimationTarget; /** Trigger type. */ trigger?: PptxAnimationTrigger; /** Shape ID that triggers this animation when clicked (interactive sequence). */ triggerShapeId?: string; /** Effect preset class (entr, exit, emph, path). */ presetClass?: 'entr' | 'exit' | 'emph' | 'path'; /** Effect preset sub-type identifier. */ presetId?: number; /** * Effect preset direction/variant code from `p:cTn/@presetSubtype` * (ECMA-376 CT_TLCommonTimeNodeData). For Fly In/Out this encodes the * edge/corner the object travels from as a bitmask (1=top, 2=right, * 4=bottom, 8=left; corners combine bits). Absent means the preset default. */ presetSubtype?: number; /** Duration in milliseconds. */ durationMs?: number; /** Delay in milliseconds. */ delayMs?: number; /** * Acceleration fraction in the range 0..1, parsed from `p:cTn/@accel` * (ST_PositiveFixedPercentage, stored as 1000ths of a percent). A non-zero * value means the effect eases in (starts slow). Absent means no easing-in. */ accel?: number; /** * Deceleration fraction in the range 0..1, parsed from `p:cTn/@decel`. * A non-zero value means the effect eases out (ends slow). Absent means no * easing-out. When both {@link accel} and {@link decel} are set, the effect * eases in and out. */ decel?: number; /** Trigger delay in milliseconds (for afterDelay). */ triggerDelayMs?: number; /** SVG path string for motion path animations (`p:animMotion/@path`). */ motionPath?: string; /** Motion origin: "layout" or "parent". */ motionOrigin?: string; /** * Whether the element auto-rotates to follow the motion path tangent. * Viewer-authoring-only hint: OOXML has no such flag (`p:animMotion/@rAng` * is a plain rotation angle that PowerPoint writes as "0" on every path), * so the parser never sets this. */ motionPathRotateAuto?: boolean; /** Path edit mode from `p:animMotion/@pathEditMode` (e.g. "relative", "fixed"). */ motionPathEditMode?: string; /** Comma-separated point-types string from `p:animMotion/@ptsTypes`. */ motionPtsTypes?: string; /** Rotation angle in degrees for `p:animRot/@by` (converted from 60000ths). */ rotationBy?: number; /** Starting rotation angle in degrees for `p:animRot/@from` (converted from 60000ths). */ rotationFrom?: number; /** Ending rotation angle in degrees for `p:animRot/@to` (converted from 60000ths). */ rotationTo?: number; /** X scale factor (percentage / 100) for `p:animScale/p:by/@x`. */ scaleByX?: number; /** Y scale factor (percentage / 100) for `p:animScale/p:by/@y`. */ scaleByY?: number; /** Starting X scale factor for `p:animScale/p:from/@x`. */ scaleFromX?: number; /** Starting Y scale factor for `p:animScale/p:from/@y`. */ scaleFromY?: number; /** Ending X scale factor for `p:animScale/p:to/@x`. */ scaleToX?: number; /** Ending Y scale factor for `p:animScale/p:to/@y`. */ scaleToY?: number; /** Whether `p:animScale/@zoomContents` was set ("1"/"true"). */ scaleZoomContents?: boolean; /** Parsed `p:tav` keyframes from `p:tavLst` (CT_TLAnimVariantList). */ keyframes?: PptxAnimationKeyframe[]; /** * The attribute {@link keyframes} drives, from the SAME node's * `p:cBhvr/p:attrNameLst/p:attrName` (ECMA-376 S19.5.4). `p:tavLst` is * schema-generic: without this, playback could see a numeric ramp but not * know whether it targeted opacity, position, colour, or something with * no CSS mapping. Lowercased and trimmed; common values seen in the wild * (and written by this codebase's own animation writer) include * `"style.opacity"`, `"style.color"`, `"style.visibility"`, `"fillcolor"`, * `"stroke.color"`, `"r"` (rotation, only meaningful on `p:animRot`), and * `"ppt_x"` / `"ppt_y"` (position, normally driven via `p:animMotion` * instead). Absent when the behaviour carries no `p:attrNameLst`. */ attrName?: string; /** Repeat count (e.g. `2`, `Infinity` for indefinite). */ repeatCount?: number; /** Whether the animation plays in reverse after completion. */ autoReverse?: boolean; /** Text build type from `p:bldP/@build` in `p:bldLst`. */ buildType?: PptxTextBuildType; /** Build level for multi-level lists from `p:bldP/@bldLvl`. */ buildLevel?: number; /** Group ID linking a `p:bldP` entry to its timing animation node. */ groupId?: string; /** Sound relationship ID to play when animation triggers (`p:stSnd`). */ soundRId?: string; /** Resolved sound file path from relationship. */ soundPath?: string; /** Whether to stop any currently playing sound (`p:endSnd`). */ stopSound?: boolean; /** * End-state behaviour from `p:cTn/@fill` (ST_TLTimeNodeFillType, ECMA-376 * §19.5.27). `hold`/`freeze` mean the effect's final frame persists after * it finishes; `remove` (the default when absent) means the target reverts * to its pre-effect appearance. `transition` behaves like `hold` until the * next time node starts. Absent means the OOXML default (`remove`). */ fill?: 'remove' | 'freeze' | 'hold' | 'transition'; /** * Restart behaviour from `p:cTn/@restart` (ST_TLTimeNodeRestartType). * Absent means the OOXML default (`always`). */ restart?: 'always' | 'whenNotActive' | 'never'; /** * Repeat duration in milliseconds from `p:cTn/@repeatDur`. `Infinity` * represents the literal `"indefinite"` token. */ repeatDurMs?: number; /** * Playback speed multiplier from `p:cTn/@spd` (ST_Percentage, normalized * from OOXML's 1000ths-of-a-percent storage to a plain percentage, e.g. * `150` for 150% / double speed). Absent means normal (100%) speed. */ speedPct?: number; /** * Reverse the paragraph build order from `p:bldP/@rev` (TEXT build only). * Not to be confused with {@link PptxGraphicBuild}'s `reverse` field, which * carries the unrelated `p:bldDgm`/`@rev` DIAGRAM-build reverse flag. */ buildReverse?: boolean; /** * Auto-advance time in milliseconds from `p:bldP/@advAuto`. `Infinity` * represents the literal `"indefinite"` token. Absent means the build * step waits for a click. */ buildAdvAutoMs?: number; /** * Per-build-level timing templates from a TEXT `p:bldP/p:tmplLst` * (ECMA-376 §19.5.84 CT_TLTemplateList). Parsed for round-trip only; see * {@link PptxTimingTemplate} for why they are not consulted at playback. */ buildTemplates?: PptxTimingTemplate[]; /** * Whether the enclosing `p:seq` allows concurrent play with its siblings, * from `p:seq/@concurrent`. Parsed for round-trip; not yet honoured by * playback (see `docs/guide/limitations.md`). */ seqConcurrent?: boolean; /** Next-action behaviour from `p:seq/@nextAc` (ST_TLNextActionType). */ seqNextAction?: 'none' | 'seek'; /** Previous-action behaviour from `p:seq/@prevAc` (ST_TLPreviousActionType). */ seqPrevAction?: 'none' | 'skipTimeNode'; /** * Whether the enclosing click-level group (a direct `p:par` child of the * `mainSeq`) begins automatically when the slide appears, rather than waiting * for a click. * * PowerPoint gates a click step with a lone ``; * a group that also carries a time-node condition (`onBegin`/`onEnd` with a * `@tn`) or a finite delay starts on slide entry ("With/After Previous" as the * first effect on the slide). The flat animation list cannot express that on * its own, so the parse layer stamps it here. */ groupAutoStart?: boolean; /** * Index of the enclosing effect-wrapper `p:par` inside the click-level group. * * Effects that share a wrapper are OOXML siblings: they all start when that * wrapper starts, and each `p:cond/@delay` is measured from the wrapper's * start, NOT chained off the effect before it. Playback uses this to place * simultaneous effects at their true offsets instead of accumulating delays. */ parGroupIndex?: number; /** Structured start conditions parsed from `p:stCondLst`. */ startConditions?: AnimationCondition[]; /** Structured end conditions parsed from `p:endCondLst`. */ endConditions?: AnimationCondition[]; /** Preserved raw `p:endCondLst` XML node for lossless round-trip. */ rawEndCondLst?: XmlObject; /** Color animation data from `p:animClr`. */ colorAnimation?: PptxColorAnimation; /** Text-level target: character range or paragraph range from `p:txEl`. */ textTarget?: PptxTextAnimationTarget; /** Whether this animation is inside an exclusive container (`p:excl`). */ exclusive?: boolean; /** * Identifies which `p:excl` container this animation belongs to, when * {@link exclusive} is set. ECMA-376 S19.5.24 CT_TLExclusiveTimeNode: at * most one direct child of an exclusive container may be active at a * time, so starting one child stops any other currently-playing child of * the SAME container. Two different `p:excl` containers on the same slide * are independent groups; this id (assigned per container encountered * during parsing, stable only within one parse's animation list) lets * playback tell them apart. Absent when {@link exclusive} is unset. */ exclGroupId?: number; /** Command type from `p:cmd` (@_type: call/evt/verb). */ commandType?: string; /** Command string from `p:cmd` (@_cmd). */ commandString?: string; /** Iteration configuration from `p:iterate`. */ iterate?: PptxAnimationIterate; /** * Discriminator for non-preset animation kinds. When `undefined`, the * entry represents the default shape-effect animation. The `'media'` * kind represents a `p:audio` / `p:video` timing node, captured here so * playback order in the timeline is preserved alongside other animations. */ kind?: PptxNativeAnimationKind; /** * For `kind === 'media'`, identifies whether this is an audio or video * media node so writers know which OOXML element to re-emit. */ mediaType?: 'audio' | 'video'; /** * SmartArt build attribute (`p:bldDgm/@bld`) when this animation is * associated with a SmartArt diagram build. Common values include * `whole`, `one`, `lvlOne`, `lvlAtOnce`. */ smartArtBuild?: string; /** * Graphic-frame build attribute (`p:bldGraphic/@bld`) when this animation * is associated with a generic graphic frame build (charts, tables, etc. * that aren't OLE charts). */ graphicBuild?: string; /** * OLE-embedded chart build attribute (`p:bldOleChart/@bld`) when this * animation stages an OLE chart graphic frame. Values follow * ST_TLOleChartBuildType: `allAtOnce`, `series`, `category`, `seriesEl`, * `categoryEl`. Lets a staged-reveal renderer build the chart by series / * category / element to match PowerPoint, rather than as one whole element. */ oleChartBuild?: string; /** Schema-accurate `p:bldGraphic/p:bldAsOne|p:bldSub` representation. */ graphicBuildProperties?: PptxGraphicBuild; /** * Opaque map of `p:cTn` attributes that don't have a typed home on this * interface but must round-trip through parse → save. Keys are stored * verbatim including the `@_` prefix used by the underlying XML parser * (e.g. `@_evtFilter`, `@_display`, `@_masterRel`, `@_nodePh`, * `@_endSync`, `@_progress`). The `subTnLst` child element is also * preserved here under the literal key `p:subTnLst`. The `afterEffect` * attribute is surfaced separately as a typed boolean ({@link afterEffect}) * because it changes write semantics for subsequent timing nodes. */ cTnAttributes?: Record; /** * Whether the OOXML `p:cTn/@afterEffect` flag is set. Indicates this node * runs after the parent effect's main body has completed; affects how * subsequent peer nodes are sequenced when serialised back to OOXML. */ afterEffect?: boolean; /** * "After animation" end-state behaviour carried over from the matching * {@link PptxElementAnimation.afterAnimation} entry for this effect's * element. Not populated by the native-timing parser itself (there is no * single `p:cTn` attribute for it): `applyAfterAnimationFromEditorList` in * `pptx-viewer-shared` merges it in from the editor's per-element * animation list before playback, since that is the model the animation * panel writes `afterAnimation` into. */ afterAnimationAction?: PptxAfterAnimationAction; /** Dim-to color hex, present when {@link afterAnimationAction} is `dimToColor`. */ afterAnimationColor?: string; /** * Parsed `p:animEffect` filter descriptor. `presetId`/`presetClass` remain * the primary effect selector (see `resolveEffect` in `pptx-viewer-shared`); * this is the fallback used when a preset table lookup misses (unmapped or * absent `presetId`), which happens for decks authored by tools other than * PowerPoint that only emit the SMIL-style filter string. */ effectFilter?: PptxAnimationEffectFilter; } /** * Parsed `p:animEffect/@filter` (+ `@transition`) descriptor. ECMA-376 * describes `@filter` as a free-form string of the form `family(subtype)`, * optionally followed by `;`-separated fallback candidates (only the first * is honoured, per ECMA-376 S19.5.3's "first supported filter wins" rule). * * @example * ```ts * const f: PptxAnimationEffectFilter = { family: 'wipe', subtype: 'up', transition: 'in', raw: 'wipe(up)' }; * ``` */ interface PptxAnimationEffectFilter { /** Filter family name (e.g. `"wipe"`, `"barn"`, `"checkerboard"`), lowercased. */ family: string; /** * Parenthesised subtype/direction token verbatim (e.g. `"up"`, * `"inVertical"`, `"across"`, `"4"`). Absent when the filter has no * subtype (e.g. bare `"dissolve"`). */ subtype?: string; /** * `p:animEffect/@transition`: `"in"` reveals the target (the OOXML * default when the attribute is omitted), `"out"` conceals it, `"none"` * applies the filter without a visibility change (a static filter pass). */ transition?: 'in' | 'out' | 'none'; /** Raw filter string exactly as authored, for round-trip/debugging. */ raw: string; } /** * Single keyframe parsed from a `p:tav` element (CT_TLTimeAnimateValue). * * Each entry in a `p:tavLst` has a time fraction (`@_tm`, in 1000ths of the * total duration; or the literal "indefinite" / "large") and a typed value * child under `p:val/p:strVal|p:boolVal|p:intVal|p:fltVal|p:clrVal`. * * @see ECMA-376 §19.5.30 CT_TLAnimVariantList / §19.5.92 CT_TLTimeAnimateValue */ interface PptxAnimationKeyframe { /** * Time fraction. A finite number is the OOXML `@_tm` integer (0–100000 * for percentage, where 100000 = 100% of duration). A string preserves * special tokens ("indefinite", "large"). */ tm: number | string; /** Decoded keyframe value. */ value: string | boolean | number; /** Discriminant indicating which `p:val` child carried the value. */ valueType: 'str' | 'bool' | 'int' | 'flt' | 'clr'; /** * Optional formula carried on `p:tav/@_fmla`. Preserved for round-trip * fidelity; consumers may use it to drive computed animation values. */ fmla?: string; } /** Color animation data parsed from `p:animClr`. */ interface PptxColorAnimation { /** Color interpolation space: "hsl" or "rgb". */ colorSpace: 'hsl' | 'rgb'; /** Direction for HSL interpolation: "cw" (clockwise) or "ccw". */ direction?: 'cw' | 'ccw'; /** * Optional `p:animClr/@path` value preserved for round-trip. When set, * the colour sweep follows a path-based interpolation rather than the * straight cw/ccw arc. ECMA-376 §19.5.13 documents this attribute as a * companion to `@dir` for HSL colour-space animations. */ path?: string; /** Starting color as hex string. */ fromColor?: string; /** Ending color as hex string. */ toColor?: string; /** * Color delta (for "by" animations) as hex string. For HSL colour-space * animations the value encodes a delta over hue/sat/lum and is preserved * verbatim from the source. */ byColor?: string; /** * Target attribute from `p:attrNameLst` (e.g. "fillcolor", "style.color", * "stroke.color"). Used to determine which CSS property to animate. */ targetAttribute?: string; } /** Text-level animation target from `p:txEl`. */ interface PptxTextAnimationTarget { /** Target type: character range or paragraph range. */ type: 'charRg' | 'pRg'; /** Start index (0-based). */ start: number; /** End index (exclusive). */ end: number; } /** * Event types for animation conditions from `p:cond/@evt`. * * These map directly to OOXML condition event attribute values * (ISO/IEC 29500-1 S19.5.28 CT_TLTimeCondition). */ type AnimationConditionEvent = 'onBegin' | 'onEnd' | 'begin' | 'end' | 'onClick' | 'onMouseOver' | 'onMouseOut' | 'onNext' | 'onPrev' | 'onStopAudio'; /** * Structured representation of a single OOXML animation condition * from `p:cond` elements inside `p:stCondLst` or `p:endCondLst`. * * Conditions control when an animation starts or ends, and can reference * events, time delays, and target time node IDs. * * @example * ```ts * const cond: AnimationCondition = { * event: "onClick", * delay: 0, * targetShapeId: "shape_5", * }; * ``` */ interface AnimationCondition { /** Event that triggers the condition. */ event?: AnimationConditionEvent; /** Delay in milliseconds (from `@_delay`). "indefinite" is represented as -1. */ delay?: number; /** Target time node ID reference (from `@_tn`). */ targetTimeNodeId?: number; /** Target shape ID from `p:tgtEl/p:spTgt/@spid`. */ targetShapeId?: string; /** Whether the condition targets a slide (from `p:tgtEl/p:sldTgt`). */ targetSlide?: boolean; /** Full target choice, including `p:sndTgt` and `p:inkTgt`. */ target?: PptxAnimationTarget; } /** Iteration configuration from `p:iterate`. */ interface PptxAnimationIterate { /** Iteration type: el (element), lt (letter), wd (word). */ type: 'el' | 'lt' | 'wd'; /** Whether to iterate backwards. */ backwards?: boolean; /** Timing interval (percentage of total duration, in 1000ths). */ tmPct?: number; /** Absolute timing interval in ms. */ tmAbs?: number; } /** Build type for text build (paragraph/word/letter) animations from `p:bldP/@build`. */ type PptxTextBuildType = 'allAtOnce' | 'byParagraph' | 'byWord' | 'byChar'; /** Direction for fly-in / fly-out / wipe effects. */ type PptxAnimationDirection = 'fromLeft' | 'fromRight' | 'fromTop' | 'fromBottom' | 'fromTopLeft' | 'fromTopRight' | 'fromBottomLeft' | 'fromBottomRight'; /** Sequence mode for paragraph-level animations. */ type PptxAnimationSequence = 'asOne' | 'byParagraph' | 'byWord' | 'byLetter'; /** Behavior after animation finishes. */ type PptxAfterAnimationAction = 'none' | 'hideOnNextClick' | 'hideAfterAnimation' | 'dimToColor'; /** * High-level animation data associated with a slide element. * * Combines entrance, exit, and emphasis presets with timing and * trigger configuration. Used by the editor’s animation panel * and the `setPptxElementAnimation` tool. * * @example * ```ts * const anim: PptxElementAnimation = { * elementId: "title_1", * entrance: "fadeIn", * durationMs: 600, * order: 1, * trigger: "afterPrevious", * }; * // => { elementId: "title_1", entrance: "fadeIn", durationMs: 600, order: 1, trigger: "afterPrevious" } * ``` */ interface PptxElementAnimation { elementId: string; entrance?: PptxAnimationPreset; exit?: PptxAnimationPreset; emphasis?: PptxAnimationPreset; durationMs?: number; delayMs?: number; order?: number; trigger?: PptxAnimationTrigger; /** Shape ID that triggers this animation when clicked (interactive sequence). */ triggerShapeId?: string; timingCurve?: PptxAnimationTimingCurve; repeatCount?: number; repeatMode?: PptxAnimationRepeatMode; /** Direction for directional effects (fly in/out, wipe, etc.). */ direction?: PptxAnimationDirection; /** Sequence mode — animate as one object or by paragraph/word/letter. */ sequence?: PptxAnimationSequence; /** What happens after the animation finishes playing. */ afterAnimation?: PptxAfterAnimationAction; /** Dim-to color hex (used when afterAnimation is "dimToColor"). */ afterAnimationColor?: string; /** SVG motion path string for custom motion path animations. */ motionPath?: string; /** * Path edit mode for `p:animMotion/@pathEditMode`. Defaults to "relative" * when emitted without an explicit value. */ motionPathEditMode?: string; /** Comma-separated point-types string for `p:animMotion/@ptsTypes`. */ motionPtsTypes?: string; /** Sound relationship ID to play when animation triggers (`p:stSnd`). */ soundRId?: string; /** Resolved sound file path from relationship. */ soundPath?: string; /** Whether to stop any currently playing sound (`p:endSnd`). */ stopSound?: boolean; /** * Pending, not-yet-embedded sound chosen in the authoring UI, as a * `data:audio/...;base64,...` URL. Mirrors the `imageData` / * `mediaData` pending-embed convention used elsewhere in the typed model: * on save, the writer converts this to real bytes under `ppt/media/`, * mints a relationship, and replaces this field with the resolved * {@link soundRId} / {@link soundPath}. Cleared once embedded. */ soundData?: string; /** * Display name for the chosen sound (e.g. the uploaded file's name), * shown by the authoring UI's sound picker. Purely cosmetic; has no * OOXML equivalent and is not required for playback. */ soundFileName?: string; } /** * A read-only anchor representing one of the deck's own effect groups: a * top-level click group (`p:par` under `p:timing`'s main sequence) that this * app did not author, so it is never exposed as an editable * {@link PptxElementAnimation}. * * The authoring UI merges these anchors alongside `PptxSlide.animations` to * render the FULL animation sequence (editor-authored and deck-native * effects together) and lets an editor-authored entry be dragged to any * position relative to them. `order` is the anchor's position among ALL * top-level click groups (editor-owned and native) at load time, in the same * numbering space as {@link PptxElementAnimation.order}, so the two * populations sort into one coherent timeline. * * Anchors are never written back: on save, an untouched anchor's own click * group is repositioned (if an editor-authored effect was dragged past it) * but never mutated, so the deck's own effect stays byte-identical apart * from its position in the sequence. */ interface PptxAnimationTimelineAnchor { /** * Position of this group among all top-level click groups in the main * animation sequence at load time (dense, shared with editor entries' * `order`). */ order: number; /** Shape id(s) this group's effects target, for a readable UI label. */ targetIds: string[]; /** Effect preset classes present in the group (entr/exit/emph/path). */ presetClasses: Array<'entr' | 'exit' | 'emph' | 'path'>; } //#endregion //#region src/core/types/embedded-font.d.ts interface PptxEmbeddedFontDataId { /** Required relationship identifier from `r:id`. */ relationshipId?: string | null; /** Original leaf retained for unknown attribute preservation. */ rawXml?: XmlObject; } interface PptxEmbeddedFontDescriptor { typeface?: string | null; panose?: string | null; pitchFamily?: string | null; charset?: string | null; rawXml?: XmlObject; } interface PptxEmbeddedFontListEntry { font: PptxEmbeddedFontDescriptor; regular?: PptxEmbeddedFontDataId | null; bold?: PptxEmbeddedFontDataId | null; italic?: PptxEmbeddedFontDataId | null; boldItalic?: PptxEmbeddedFontDataId | null; rawXml?: XmlObject; } interface PptxEmbeddedFontList { fonts: PptxEmbeddedFontListEntry[]; /** Original list retained for unknown attribute and child preservation. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/comment-mentions.d.ts /** * A single `@`-mention inside a modern comment body. * * Offsets index into the comment's FLATTENED plain text: every `a:t` value * below `p188:txBody` concatenated, with paragraphs joined by `\n`. That is the * same string `PptxComment.text` carries, so an edit to `text` invalidates * every offset after the edit point and the serializer re-bases them. * * The markup Office uses for a mention is `CT_Mention` (documented for the * SpreadsheetML `2018/threadedcomments` part): `mentionpersonId`, `mentionId`, * `startIndex` and `length`. The PowerPoint `2018/8/main` schema does not * publish a mention element at all, so `rawXml` is retained and re-emitted * attribute-for-attribute: a producer that spells the attributes differently * still round-trips. * * @example * ```ts * const mention: PptxCommentMention = { * personId: "{2CB2E9D0-D392-EB21-5D46-FBA34C1295E6}", * authorName: "Bob Example", * startIndex: 3, * length: 11, * }; * // => "Hi Bob Example can you check this".slice(3, 14) === "Bob Example" * ``` */ interface PptxCommentMention { /** `mentionId`: GUID identifying this mention instance. */ id?: string; /** `mentionpersonId`: the `p188:author` id of the mentioned person. */ personId: string; /** Display name resolved from the author list at parse time, when known. */ authorName?: string; /** Character offset of the mentioned span in the flattened plain text. */ startIndex: number; /** Character length of the mentioned span. */ length: number; /** * `uri` of the `p188:ext` this mention list was read from. Undefined means * the list is a direct child of `p188:cm`, which is where it is written for * newly authored mentions. */ containerUri?: string; /** Original `p188:mention` node, retained for unknown-attribute preservation. */ rawXml?: XmlObject; } //#endregion //#region src/core/types/metadata.d.ts /** * A slide comment — may be a legacy positional comment or a modern * threaded comment with replies. * * @example * ```ts * const comment: PptxComment = { * id: "c1", * text: "Please update this chart.", * author: "Alice", * createdAt: "2024-06-01T10:00:00Z", * resolved: false, * }; * // => satisfies PptxComment * ``` */ interface PptxComment { id: string; text: string; /** Storage vocabulary used by this comment. Omitted means legacy PresentationML. */ format?: 'legacy' | 'modern'; /** Stable GUID author identifier used by Office 2021 modern comments. */ authorId?: string; /** Optional parent comment id for reply threading metadata. */ parentId?: string; author?: string; createdAt?: string; x?: number; y?: number; /** Whether this comment has been resolved/marked done. */ resolved?: boolean; /** Native p188 status token. */ status?: 'active' | 'resolved' | 'closed'; /** Modern comment classification tags and author IDs that liked the comment. */ tags?: string[]; likes?: string[]; startDate?: string; dueDate?: string; assignedTo?: string[]; /** Task completion in thousandths of a percent, from 0 through 100000. */ complete?: number; priority?: number; title?: string; /** Modern threaded comment support (p15:threadingInfo). */ threadId?: string; /** `@`-mentions, indexed into `text` (see {@link PptxCommentMention}). */ mentions?: PptxCommentMention[]; /** Replies to this comment (for modern threaded comments). */ replies?: PptxComment[]; /** ID of the element this comment is associated with (if any). */ elementId?: string; /** Original `p:cm` subtree, retained for unknown child and extension preservation. */ rawXml?: XmlObject; } /** Office 2021 comment author from the p188 Author part. */ interface PptxModernCommentAuthor { id: string; name: string; initials?: string; userId: string; providerId: string; rawXml?: XmlObject; } /** * A comment author from `ppt/commentAuthors.xml`. * * Stores all attributes needed for lossless round-trip serialization * of the `p:cmAuthor` element (id, name, initials, lastIdx, clrIdx). * * @see ECMA-376 Part 1, §19.4.2 (cmAuthor) * * @example * ```ts * const author: PptxCommentAuthor = { * id: "0", * name: "John Doe", * initials: "JD", * lastIdx: 3, * clrIdx: 0, * }; * // => satisfies PptxCommentAuthor * ``` */ interface PptxCommentAuthor { /** Unique numeric author identifier (`@_id`). */ id: string; /** Author display name (`@_name`). */ name: string; /** Author initials (`@_initials`). */ initials: string; /** Last comment index used by this author (`@_lastIdx`). */ lastIdx: number; /** Colour index assigned to this author (`@_clrIdx`). */ clrIdx: number; /** Original `p:cmAuthor` subtree, retained for unknown attribute preservation. */ rawXml?: XmlObject; } /** * A compatibility warning generated during parse or save when the * file uses features not fully supported by the editor. * * @example * ```ts * const warning: PptxCompatibilityWarning = { * code: "UNSUPPORTED_3D", * message: "3D rotation effects may not render accurately.", * severity: "warning", * scope: "element", * slideId: "slide-1", * elementId: "elem-42", * }; * // => satisfies PptxCompatibilityWarning * ``` */ interface PptxCompatibilityWarning { code: string; message: string; severity: 'info' | 'warning'; scope: 'presentation' | 'slide' | 'element' | 'save'; slideId?: string; elementId?: string; xmlPath?: string; } /** * A single name–value tag from `ppt/tags/*.xml`. * * @example * ```ts * const tag: PptxTag = { name: "CUSTOM_ID", value: "12345" }; * // => satisfies PptxTag * ``` */ interface PptxTag { name: string; value: string; } /** * A collection of tags from a single tags XML part. * * @example * ```ts * const coll: PptxTagCollection = { * path: "ppt/tags/tag1.xml", * tags: [{ name: "CUSTOM_ID", value: "12345" }], * }; * // => satisfies PptxTagCollection * ``` */ interface PptxTagCollection { /** File path within the PPTX archive. */ path?: string; /** Package owner of the tags relationship. New collections default to presentation. */ owner?: 'presentation' | 'slide' | 'part'; /** Source OPC part that owns the relationship, e.g. ppt/slides/slide1.xml. */ sourcePartPath?: string; /** Durable relationship identifier from the owning part. */ relationshipId?: string; /** Tags in this collection. */ tags: PptxTag[]; /** Parsed tag-list XML retained for unknown-node preservation. */ rawXml?: XmlObject; } /** * A custom document property from `docProps/custom.xml`. * * @example * ```ts * const prop: PptxCustomProperty = { * name: "Project", * value: "pptx", * type: "lpwstr", * }; * // => satisfies PptxCustomProperty * ``` */ interface PptxCustomProperty { /** Property name. */ name: string; /** Property value (always stringified). */ value: string; /** Original VT type (e.g. "lpwstr", "i4", "bool", "filetime"). */ type: string; } /** * Core document properties from `docProps/core.xml` (Dublin Core + OOXML). * * @example * ```ts * const core: PptxCoreProperties = { * title: "Q4 Business Review", * creator: "Alice", * created: "2024-01-15T08:00:00Z", * modified: "2024-06-01T12:30:00Z", * lastModifiedBy: "Bob", * }; * // => satisfies PptxCoreProperties * ``` */ interface PptxCoreProperties { /** dc:title */ title?: string; /** dc:subject */ subject?: string; /** dc:creator */ creator?: string; /** cp:keywords */ keywords?: string; /** dc:description */ description?: string; /** cp:lastModifiedBy */ lastModifiedBy?: string; /** cp:revision */ revision?: string; /** dcterms:created (ISO 8601) */ created?: string; /** dcterms:modified (ISO 8601) */ modified?: string; /** cp:category */ category?: string; /** cp:contentStatus */ contentStatus?: string; } /** * Extended (application) properties from `docProps/app.xml`. * * @example * ```ts * const app: PptxAppProperties = { * application: "Microsoft Office PowerPoint", * appVersion: "16.0000", * slides: 24, * words: 1500, * company: "Acme Corp", * }; * // => satisfies PptxAppProperties * ``` */ interface PptxAppProperties { /** Application name (e.g. "Microsoft Office PowerPoint"). */ application?: string; /** Application version string. */ appVersion?: string; /** Presentation format (e.g. "On-screen Show (16:9)"). */ presentationFormat?: string; /** Total number of slides. */ slides?: number; /** Number of hidden slides. */ hiddenSlides?: number; /** Number of notes slides. */ notes?: number; /** Total editing time in minutes. */ totalTime?: number; /** Number of words. */ words?: number; /** Number of paragraphs. */ paragraphs?: number; /** Company name. */ company?: string; /** Manager name. */ manager?: string; /** Template name. */ template?: string; /** Hyperlink base URL. */ hyperlinkBase?: string; /** Document security bitmask (``). */ docSecurity?: number; /** Number of multimedia clips (``). */ mmClips?: number; /** Whether thumbnail images were scaled to fit (``). */ scaleCrop?: boolean; /** Whether hyperlinks are current (``). */ linksUpToDate?: boolean; /** Whether the document is shared (``). */ sharedDoc?: boolean; /** Whether hyperlinks changed since last save (``). */ hyperlinksChanged?: boolean; } //#endregion //#region src/core/types/presentation-print-properties.d.ts type PptxPrintOutput = 'slides' | 'handouts1' | 'handouts2' | 'handouts3' | 'handouts4' | 'handouts6' | 'handouts9' | 'notes' | 'outline'; type PptxPrintColorMode = 'bw' | 'gray' | 'clr'; /** PresentationML `CT_PrintProperties` (`p:prnPr`). */ interface PptxPresentationPrintProperties { printWhat?: PptxPrintOutput | null; colorMode?: PptxPrintColorMode | null; hiddenSlides?: boolean | null; scaleToFitPaper?: boolean | null; frameSlides?: boolean | null; /** Original subtree retained for unknown attributes and `p:extLst`. */ rawXml?: XmlObject; } /** * Resolved hex values for the 12 theme colour slots. * * @example * ```ts * const scheme: PptxThemeColorScheme = { * dk1: "#000000", lt1: "#FFFFFF", * dk2: "#1F497D", lt2: "#EEECE1", * accent1: "#4F81BD", accent2: "#C0504D", * accent3: "#9BBB59", accent4: "#8064A2", * accent5: "#4BACC6", accent6: "#F79646", * hlink: "#0000FF", folHlink: "#800080", * }; * // => satisfies PptxThemeColorScheme * ``` */ interface PptxThemeColorScheme { dk1: string; lt1: string; dk2: string; lt2: string; accent1: string; accent2: string; accent3: string; accent4: string; accent5: string; accent6: string; hlink: string; folHlink: string; } /** * A font-family triplet for major or minor theme fonts. * * Supports Latin, East Asian, and Complex Script font families. * * @example * ```ts * const fonts: PptxThemeFontGroup = { * latin: "Calibri Light", * eastAsia: "MS PGothic", * complexScript: "Arial", * }; * // => satisfies PptxThemeFontGroup * ``` */ interface PptxThemeFontGroup { latin?: string; eastAsia?: string; complexScript?: string; /** * Per-script typeface overrides (`` per ECMA-376 §20.1.4.1.16). Keyed by * the four-letter ISO 15924 script tag from the `script` attribute. * * Phase 4 Stream A / M4. */ byScript?: Record; } /** * Theme font scheme — major (headings) and minor (body) font families. * * @example * ```ts * const scheme: PptxThemeFontScheme = { * majorFont: { latin: "Calibri Light" }, * minorFont: { latin: "Calibri", eastAsia: "MS PGothic" }, * }; * // => satisfies PptxThemeFontScheme * ``` */ interface PptxThemeFontScheme { majorFont?: PptxThemeFontGroup; minorFont?: PptxThemeFontGroup; } /** * A single fill style entry from the theme format scheme. * Each entry is one of: solid, gradient, pattern, or no fill. * The raw XML node is also stored so that `phClr` substitution can happen * at resolution time. * * @example * ```ts * const solidFill: PptxThemeFillStyle = { * kind: "solid", * color: "#4F81BD", * opacity: 1, * }; * * const gradientFill: PptxThemeFillStyle = { * kind: "gradient", * gradientAngle: 90, * gradientType: "linear", * gradientStops: [ * { color: "#4F81BD", position: 0 }, * { color: "#1F497D", position: 1 }, * ], * }; * // => satisfies PptxThemeFillStyle * ``` */ interface PptxThemeFillStyle { /** * Discriminator for the fill type. * * `'group'` corresponds to `` — a fill that inherits the * containing group shape's fill at render time. Captured for round-trip * preservation in `fmtScheme/fillStyleLst`. */ kind: 'solid' | 'gradient' | 'pattern' | 'none' | 'group'; /** Pre-resolved colour (may be `undefined` when `phClr`-dependent). */ color?: string; opacity?: number; /** Gradient-specific fields (only present when `kind === "gradient"`). */ gradientStops?: Array<{ color: string; position: number; opacity?: number; }>; gradientAngle?: number; gradientType?: 'linear' | 'radial'; gradientCss?: string; /** Pattern-specific fields (only present when `kind === "pattern"`). */ patternPreset?: string; patternBackgroundColor?: string; /** Raw XML node preserved for `phClr` re-resolution. */ rawNode?: unknown; } /** * A single line style entry from `a:lnStyleLst`. * Provides width, dash, join, cap, and optional fill colour. * * @example * ```ts * const line: PptxThemeLineStyle = { * width: 1.5, * color: "#4F81BD", * dash: "solid", * lineJoin: "round", * lineCap: "flat", * }; * // => satisfies PptxThemeLineStyle * ``` */ interface PptxThemeLineStyle { /** Line width in pixels (converted from EMU). */ width?: number; color?: string; opacity?: number; dash?: string; lineJoin?: 'round' | 'bevel' | 'miter'; lineCap?: 'flat' | 'rnd' | 'sq'; compoundLine?: 'sng' | 'dbl' | 'thickThin' | 'thinThick' | 'tri'; /** Raw XML node preserved for `phClr` re-resolution. */ rawNode?: unknown; } /** * A single effect style entry from `a:effectStyleLst`. * Each entry may define shadow, glow, soft-edge, reflection, blur, * and optionally a 3-D scene/shape. * * @example * ```ts * const dropShadow: PptxThemeEffectStyle = { * shadowColor: "#000000", * shadowBlur: 4, * shadowOffsetX: 2, * shadowOffsetY: 3, * shadowOpacity: 0.4, * }; * // => satisfies PptxThemeEffectStyle * ``` */ interface PptxThemeEffectStyle { shadowColor?: string; shadowBlur?: number; shadowOffsetX?: number; shadowOffsetY?: number; shadowOpacity?: number; glowColor?: string; glowRadius?: number; glowOpacity?: number; softEdgeRadius?: number; innerShadowColor?: string; innerShadowOpacity?: number; innerShadowBlur?: number; innerShadowOffsetX?: number; innerShadowOffsetY?: number; reflectionBlurRadius?: number; reflectionStartOpacity?: number; reflectionEndOpacity?: number; reflectionEndPosition?: number; reflectionDirection?: number; reflectionRotation?: number; reflectionDistance?: number; /** 3D scene/camera from `a:scene3d` on the effect style (idx 3 typically). */ scene3d?: Pptx3DScene; /** 3D shape extrusion/bevel from `a:sp3d` on the effect style (idx 3 typically). */ shape3d?: Pptx3DShape; /** Raw XML node preserved for `phClr` re-resolution. */ rawNode?: unknown; } /** * The full parsed format scheme from `a:fmtScheme` inside `a:themeElements`. * Contains three fill style lists and one line/effect style list each, * at three intensity levels: subtle (idx 1), moderate (idx 2), intense (idx 3). * * OOXML reference indices: * - fillStyleLst: idx 1-3 (used by `a:fillRef @idx` 1-3) * - lnStyleLst: idx 1-3 (used by `a:lnRef @idx` 1-3) * - effectStyleLst: idx 1-3 (used by `a:effectRef @idx` 1-3) * - bgFillStyleLst: idx 1-3 (used by `a:fillRef @idx` 1001-1003) * * @example * ```ts * const fmt: PptxThemeFormatScheme = { * name: "Office", * fillStyles: [solidFill, gradientFill, intenseFill], * lineStyles: [thinLine, mediumLine, thickLine], * effectStyles: [subtle, moderate, intense], * backgroundFillStyles: [solidBg, gradientBg, intenseBg], * }; * // => satisfies PptxThemeFormatScheme * ``` */ interface PptxThemeFormatScheme { /** The `@name` attribute of the format scheme. */ name?: string; /** Fill styles at indices 1-3 (subtle, moderate, intense). */ fillStyles: PptxThemeFillStyle[]; /** Line styles at indices 1-3. */ lineStyles: PptxThemeLineStyle[]; /** Effect styles at indices 1-3. */ effectStyles: PptxThemeEffectStyle[]; /** Background fill styles at indices 1-3 (referenced via idx 1001-1003). */ backgroundFillStyles: PptxThemeFillStyle[]; } /** * Full parsed theme object available to renderers. * * @example * ```ts * const theme: PptxTheme = { * name: "Office Theme", * colorScheme: { dk1: "#000", lt1: "#FFF", /* … *\/ }, * fontScheme: { * majorFont: { latin: "Calibri Light" }, * minorFont: { latin: "Calibri" }, * }, * }; * // => satisfies PptxTheme * ``` */ interface PptxTheme { /** Theme name from `a:theme @name`. */ name?: string; /** Resolved colour scheme. */ colorScheme?: PptxThemeColorScheme; /** Resolved font scheme. */ fontScheme?: PptxThemeFontScheme; /** Format scheme — fill, line, effect and background fill style matrices. */ formatScheme?: PptxThemeFormatScheme; } //#endregion //#region src/core/types/transition.d.ts /** * Available slide transition effects. * * Maps to the OOXML child element names under `` / ``. * * @example * ```ts * const t: PptxTransitionType = "morph"; * // => "morph" — one of 40+ transition effects * ``` */ type PptxTransitionType = 'none' | 'cut' | 'fade' | 'push' | 'wipe' | 'split' | 'randomBar' | 'blinds' | 'checker' | 'circle' | 'comb' | 'cover' | 'diamond' | 'dissolve' | 'plus' | 'pull' | 'random' | 'strips' | 'uncover' | 'wedge' | 'wheel' | 'zoom' | 'newsflash' | 'morph' | 'conveyor' | 'doors' | 'ferris' | 'flash' | 'flythrough' | 'gallery' | 'glitter' | 'honeycomb' | 'pan' | 'prism' | 'reveal' | 'ripple' | 'shred' | 'switch' | 'vortex' | 'warp' | 'wheelReverse' | 'window' | 'cube' | 'flip' | 'rotate' | 'box' | 'orbit' | 'fallOver' | 'drape' | 'curtains' | 'wind' | 'prestige' | 'fracture' | 'crush' | 'peelOff' | 'pageCurlDouble' | 'pageCurlSingle' | 'airplane' | 'origami'; /** Split orientation from OOXML `@_orient`. */ type PptxSplitOrientation = 'horz' | 'vert'; /** Schema-defined `ST_TransitionSpeed` values. */ type PptxTransitionSpeed = 'slow' | 'med' | 'fast'; /** * Slide transition configuration. * * @example * ```ts * const transition: PptxSlideTransition = { * type: "fade", * durationMs: 700, * advanceOnClick: true, * advanceAfterMs: 5000, * }; * // => { type: "fade", durationMs: 700, advanceOnClick: true, advanceAfterMs: 5000 } * ``` */ /** * Morph granularity (``). * * - `byObject` - match whole shapes (PowerPoint's default) * - `byWord` - additionally morph text word by word * - `byChar` - additionally morph text character by character */ type PptxMorphOption = 'byObject' | 'byWord' | 'byChar'; interface PptxSlideTransition { type: PptxTransitionType; /** Schema-defined transition speed. Defaults to `fast` when omitted. */ speed?: PptxTransitionSpeed; durationMs?: number; direction?: string; advanceOnClick?: boolean; advanceAfterMs?: number; /** Number of spokes for wheel transition (1-8). */ spokes?: number; /** Pattern type for shred transition. */ pattern?: string; /** Through-black flag for blinds/checker (OOXML `@_thruBlk`). */ thruBlk?: boolean; /** Split orientation (horz/vert) parsed from `@_orient`. */ orient?: PptxSplitOrientation; /** * Morph granularity from ``: how finely PowerPoint * matches content between the two slides. Only meaningful when * {@link type} is `morph`; defaults to `byObject` when the attribute is * absent, matching PowerPoint's own default. */ morphOption?: PptxMorphOption; /** Relationship ID of transition sound from `p:sndAc/p:stSnd/@r:embed` when present. */ soundRId?: string; /** Embedded WAV display name from `p:stSnd/p:snd/@name`. */ soundName?: string; /** Whether the transition sound repeats until another sound starts. */ soundLoop?: boolean; /** Resolved transition sound media path within the package. */ soundPath?: string; /** Human-readable sound file name (extracted from soundPath, or set by the * UI when a new file is picked, before it has a soundPath at all). */ soundFileName?: string; /** * A newly-picked local sound file awaiting embedding, as a `data:` URL. * Set by the transitions ribbon's Sound picker (`applyTransitionSoundFile` * in `pptx-viewer-shared`) when the user chooses a file that is not yet * part of the package; mirrors `imageData`/`mediaData` on picture and media * elements. The save pipeline (`embedTransitionSound`) writes the bytes to * `ppt/media/`, mints a relationship, sets `soundRId`/`soundPath`, and * clears this field so a later save does not re-embed the same bytes. */ soundData?: string; /** * When true, the transition stops the currently-playing sound (OOXML `p:sndAc/p:endSnd`). * Mutually exclusive with `soundRId`/`soundPath` (which use `p:stSnd`). */ stopSound?: boolean; /** Preserved sound-action XML node from `p:sndAc` for lossless round-trip. */ rawSoundAction?: XmlObject; /** Preserved extension-list XML node from `p:extLst` within the transition for lossless round-trip. */ rawExtLst?: XmlObject; /** Original transition node, retained to preserve unknown attributes and children. */ rawTransition?: XmlObject; } //#endregion //#region src/core/types/view-properties.d.ts /** * View properties types parsed from `ppt/viewProps.xml`. * * Models the `p:viewPr` element and its child views: * normalViewPr, slideViewPr, outlineViewPr, notesTextViewPr, * sorterViewPr, notesViewPr. * * @module pptx-types/view-properties */ /** * Scale factor for a view (numerator / denominator percentage). */ interface PptxViewScale { /** Numerator of the scale percentage (e.g. 100 for 100%). */ n: number; /** Denominator of the scale percentage (e.g. 100 for 100%). */ d: number; /** Optional independent vertical scale. When absent, the X scale is used. */ sy?: { n: number; d: number; }; } /** * Origin point for a view (x, y in twips or EMU). */ interface PptxViewOrigin { x: number; y: number; } /** A horizontal or vertical drawing guide in slide coordinates. */ interface PptxViewGuide { orientation?: 'horz' | 'vert'; position?: number; } /** Positive grid spacing from `p:gridSpacing`. */ interface PptxGridSpacing { cx: number; cy: number; } /** * Restored region dimensions for normal view splitter. * Represents `p:restoredLeft` or `p:restoredTop`. */ interface PptxRestoredRegion { /** Size as a percentage of the available space (thousandths of a percent). */ sz: number; /** Whether auto-adjust is enabled. */ autoAdjust?: boolean; } /** * Normal view properties (`p:normalViewPr`). * Controls the splitter positions in normal (editing) view. */ interface PptxNormalViewProperties { /** Whether to show outline icons in the slide panel. */ showOutlineIcons?: boolean; /** Whether the outline/slide panel is snapped closed. */ snapVertSplitter?: boolean; /** Vertical splitter bar state: 'minimized' | 'maximized' | 'restored'. */ vertBarState?: string; /** Horizontal splitter bar state. */ horzBarState?: string; /** Whether to prefer single-slide view in the panel. */ preferSingleView?: boolean; /** Restored left region (slide panel width). */ restoredLeft?: PptxRestoredRegion; /** Restored top region (notes panel height). */ restoredTop?: PptxRestoredRegion; } /** * Common slide view properties shared by slideViewPr, outlineViewPr, * notesTextViewPr, and notesViewPr. */ interface PptxCommonSlideViewProperties { /** Whether snap-to-grid is enabled. */ snapToGrid?: boolean; /** Whether snap-to-objects is enabled. */ snapToObjects?: boolean; /** Whether drawing guides are shown. */ showGuides?: boolean; /** Whether the application may vary the scale automatically. */ variableScale?: boolean; /** Drawing guides shown in this slide view. */ guides?: PptxViewGuide[]; /** View origin (scroll position). */ origin?: PptxViewOrigin; /** View scale. */ scale?: PptxViewScale; } /** * Full view properties from `ppt/viewProps.xml`. */ interface PptxViewProperties { /** Last used view type (`p:viewPr/@lastView`). */ lastView?: string; /** Whether comments are shown (`p:viewPr/@showComments`). */ showComments?: boolean; /** Normal view properties (splitter positions). */ normalViewPr?: PptxNormalViewProperties; /** Slide view properties. */ slideViewPr?: PptxCommonSlideViewProperties; /** Outline view properties. */ outlineViewPr?: PptxCommonSlideViewProperties; /** Notes text view properties. */ notesTextViewPr?: PptxCommonSlideViewProperties; /** Sorter view scale. */ sorterViewPr?: { scale?: PptxViewScale; }; /** Notes view properties. */ notesViewPr?: PptxCommonSlideViewProperties; /** Grid spacing in positive DrawingML coordinates. */ gridSpacing?: PptxGridSpacing; /** Raw XML preserved for lossless round-trip of unparsed attributes. */ rawXml?: Record; } //#endregion //#region src/core/types/presentation.d.ts /** * A customer data reference from `p:custDataLst / p:custData`. * * Enterprise add-ins and integrations store custom data parts in the * package and reference them via relationship IDs in the slide or * presentation XML. * * @see ECMA-376 Part 1, §19.2.1.3 (custDataLst), §19.3.1.6 (custData) */ interface PptxCustomerData { /** Resolved part path inside the package (e.g. `customXml/item1.xml`). */ id?: string; /** Relationship ID referencing the custom data part. */ relId?: string; /** Raw string content of the custom data part (if resolvable). */ data?: string; /** OPC content type for the custom data part. */ contentType?: string; /** Raw `p:custData` XML retained for unknown-node preservation. */ rawXml?: XmlObject; } /** * An ActiveX control reference from `p:controls / p:control`. * * ActiveX form controls (buttons, text boxes, check boxes, combo boxes, etc.) * are embedded via OLE parts and referenced by relationship ID in the slide XML. * * @see ECMA-376 Part 1, §19.3.1.3 (controls), §19.3.1.2 (control) */ interface PptxActiveXControl { /** Relationship ID referencing the ActiveX binary part. */ relId: string; /** Control name from @name attribute. */ name?: string; /** Shape ID this control is linked to (from @spid). */ shapeId?: string; /** X position (px) of the control's fallback picture, if present. */ x?: number; /** Y position (px) of the control's fallback picture, if present. */ y?: number; /** Width (px) of the control's fallback picture, if present. */ width?: number; /** Height (px) of the control's fallback picture, if present. */ height?: number; /** * Relationship ID of the control's static fallback picture * (`mc:AlternateContent > mc:Fallback > p:pic > p:blipFill > a:blip@r:embed`). * Renderers resolve this to an image so a control shows its last static * frame instead of a blank area (the live ActiveX cannot run in a viewer). */ fallbackImageRelId?: string; /** Raw XML for round-trip preservation. */ rawXml?: XmlObject; } /** * Pattern fill on a slide background. * * Mirrors the `` choice inside ``. Renderers should * draw a 2-colour preset pattern (e.g. `dkDnDiag`, `pct50`). * * ECMA-376 §20.1.8.47. * * @example * ```ts * const pattern: PptxSlideBackgroundPattern = { * preset: "ltDnDiag", * fgColor: "#4472C4", * bgColor: "#FFFFFF", * }; * // => satisfies PptxSlideBackgroundPattern * ``` */ interface PptxSlideBackgroundPattern { /** DrawingML preset pattern token (`@_prst`). */ preset: string; /** Foreground colour resolved to `#RRGGBB`. */ fgColor?: string; /** Background colour resolved to `#RRGGBB`. */ bgColor?: string; } /** * A single slide in a parsed PPTX presentation. * * Contains the element tree, background settings, notes, comments, * transition / animation data, and metadata like layout path and section. * * @example * ```ts * const slide: PptxSlide = { * id: "slide1", * rId: "rId2", * slideNumber: 1, * elements: [titleTextBox, subtitleTextBox], * backgroundColor: "#FFFFFF", * notes: "Remember to mention quarterly goals.", * }; * // => satisfies PptxSlide * ``` */ interface PptxSlide { id: string; rId: string; /** * `p:sldIdLst/p:sldId/@id` (ST_SlideId, 256..2147483647): the numeric key * that sections (`p14:sldIdLst/p14:sldId/@id`) and section/summary zooms * name slides by. * * It lives in `presentation.xml`, NOT in the slide part, so it cannot be * recovered from `rawXml`. Without it on the model, code that writes a * section's membership has nothing correct to write and falls back to the * slide NUMBER, which is 1-based and therefore never matches a real deck's * ids: the section reloads with no slides in it. */ slideId?: string; sourceSlideId?: string; /** Optional author-supplied slide name (set via `SlideBuilder.setName`). */ name?: string; layoutPath?: string; layoutName?: string; slideNumber: number; hidden?: boolean; sectionName?: string; sectionId?: string; elements: PptxElement[]; backgroundColor?: string; backgroundImage?: string; backgroundGradient?: string; /** * Pattern fill on the slide background (`` inside ``). * * When present, renderers should draw a real two-colour pattern using * the named DrawingML preset (e.g. `"ltDnDiag"`, `"pct50"`). The flat * `backgroundColor` field is left set to the foreground colour for * fallback rendering paths that don't understand patterns. * * ECMA-376 §20.1.8.47. */ backgroundPattern?: PptxSlideBackgroundPattern; /** * ``: boolean flag instructing the renderer to * shade the background gradient toward the title placeholder's text * colour. Parsed and round-tripped here on the core model; the actual * visual effect is applied by `pptx-viewer-shared`'s * `getSlideBackgroundStyle` (see `render/background-shade-to-title.ts`), * consumed by all five bindings, not by core itself. That module's * docstring explains the approximation: no published ECMA-376 or * MS-ODRAWXML text documents the exact legacy blend. Legacy PowerPoint * 97-2003 hint, not observed in any real-world corpus file this project * has collected and not settable from any modern PowerPoint UI; see * `docs/guide/limitations.md`. * * ECMA-376 §19.3.1.2 (CT_BackgroundProperties). */ backgroundShadeToTitle?: boolean; transition?: PptxSlideTransition; animations?: PptxElementAnimation[]; /** * Read-only anchors for the deck's own (non-editor-authored) effect * groups, merged with `animations` by the authoring UI so drag-to-reorder * can target any position in the full sequence. See * {@link PptxAnimationTimelineAnchor}. */ animationTimelineAnchors?: PptxAnimationTimelineAnchor[]; /** Native OOXML animation data parsed from `p:timing`. */ nativeAnimations?: PptxNativeAnimation[]; /** Preserved raw `p:timing` XML for lossless round-trip of native animations. */ rawTiming?: XmlObject; notes?: string; /** Rich text segments for the slide notes (preserves formatting). */ notesSegments?: TextSegment[]; /** * Parsed shapes from the notes slide's `/` so the full * notes-page shape tree can be inspected and mutated, not just the body * placeholder text. When undefined, the existing notes XML is left * untouched on save and only `notes` / `notesSegments` are written. */ notesShapes?: PptxElement[]; /** * Per-notes-slide colour map override parsed from `/`. * Captured for lossless round-trip of the notes-slide's colour scheme. */ notesClrMapOverride?: Record; /** Optional `` value of the notes slide, for round-trip. */ notesCSldName?: string; comments?: PptxComment[]; /** Source package metadata for an Office 2021 p188 comment part. */ modernCommentPart?: PptxModernCommentPart; warnings?: PptxCompatibilityWarning[]; rawXml?: XmlObject; /** Per-slide colour map override parsed from `p:clrMapOvr`. */ clrMapOverride?: Record; /** Whether background animations should play (`p:bg/@showAnimation`). */ backgroundShowAnimation?: boolean; /** Whether master slide shapes should be shown on this slide (`p:sld/@showMasterSp`). */ showMasterShapes?: boolean; /** * Whether inherited master placeholder animations should replay on this * slide (`p:sld/@showMasterPhAnim`). Distinct from {@link showMasterShapes}: * this governs animation timing, not shape visibility. Mirrors * `p:sldLayout/@showMasterPhAnim`, ECMA-376 §19.3.1.38. */ showMasterPhAnim?: boolean; /** Drawing guides parsed from slide extension list. */ guides?: PptxDrawingGuide[]; /** When explicitly `false`, the slide is unmodified and save can skip re-serialization. */ isDirty?: boolean; /** Customer data references from `p:custDataLst` on this slide. */ customerData?: PptxCustomerData[]; /** ActiveX control references from `p:controls` on this slide. */ activeXControls?: PptxActiveXControl[]; /** * Shapes parsed from a referenced legacy VML drawing part * (`ppt/drawings/vmlDrawing*.vml`, linked via a `legacyDrawing` * relationship). These are read-only render hints: the VML part itself is * preserved verbatim on save, so this field is not re-serialized. */ legacyVmlElements?: PptxElement[]; /** Per-slide header/footer flags from `` (P-H3). */ headerFooterFlags?: PptxHeaderFooterFlags; /** Server-backed slide synchronization metadata stored in a related OPC part. */ slideSynchronization?: PptxSlideSyncProperties; } interface PptxModernCommentPart { path: string; relationshipId: string; /** Original p188:cmLst root, including unknown attributes and extensions. */ rawXml?: XmlObject; } /** Metadata from a `p:sldSyncPr` slide synchronization data part. */ interface PptxSlideSyncProperties { serverSlideId: string; serverSlideModifiedTime: string; clientInsertedTime: string; extensionList?: XmlObject; rawXml?: XmlObject; partPath?: string; relationshipId?: string; } /** * A slide layout available in the loaded presentation. * * Each entry maps to a `` inside `ppt/slideLayouts/`. * * @example * ```ts * const layout: PptxLayoutOption = { * path: "ppt/slideLayouts/slideLayout2.xml", * name: "Title and Content", * }; * // => satisfies PptxLayoutOption * ``` */ interface PptxLayoutOption { path: string; name: string; /** Standard layout type from `p:sldLayout/@type` (e.g. "obj", "twoColTx", "blank"). */ type?: string; /** ZIP path of the slide master this layout belongs to. */ masterPath?: string; } /** * Header, footer, date-time, and slide-number placeholders. * * Parsed from `ppt/presProps.xml` and individual slide layouts. * * @example * ```ts * const hf: PptxHeaderFooter = { * hasFooter: true, * footerText: "Confidential", * hasSlideNumber: true, * }; * // => satisfies PptxHeaderFooter * ``` */ interface PptxHeaderFooter { hasHeader?: boolean; headerText?: string; hasFooter?: boolean; footerText?: string; hasDateTime?: boolean; dateTimeText?: string; dateTimeAuto?: boolean; /** OOXML date format pattern (e.g. "M/d/yyyy", "dddd, MMMM dd, yyyy"). */ dateFormat?: string; hasSlideNumber?: boolean; } /** * Presentation-level properties parsed from `presentationPr.xml`. * * Controls slideshow behaviour, print settings, custom colours, and grid. * * @example * ```ts * const props: PptxPresentationProperties = { * showType: "presented", * loopContinuously: false, * advanceMode: "useTimings", * }; * // => satisfies PptxPresentationProperties * ``` */ interface PptxPresentationProperties { /** Show type: presented, browsed, kiosk. */ showType?: 'presented' | 'browsed' | 'kiosk'; /** Whether to loop the slideshow continuously. */ loopContinuously?: boolean; /** Whether to show without narration. */ showWithNarration?: boolean; /** Whether to show without animation. */ showWithAnimation?: boolean; /** Advance slides mode: manual click or use stored timings. */ advanceMode?: 'manual' | 'useTimings'; /** Show slides: 'all', a custom show id, or a from-to range. */ showSlidesMode?: 'all' | 'customShow' | 'range'; /** Custom show id to use when showSlidesMode is 'customShow'. */ showSlidesCustomShowId?: string; /** Slide range start (1-based) when showSlidesMode is 'range'. */ showSlidesFrom?: number; /** Slide range end (1-based) when showSlidesMode is 'range'. */ showSlidesTo?: number; /** Whether to show subtitles/captions during presentation mode. */ showSubtitles?: boolean; /** Typed `p:prnPr` settings. Set to null during save to remove the element. */ printProperties?: PptxPresentationPrintProperties | null; /** Most-recently-used colours from the presentation palette. */ mruColors?: string[]; /** Pen colour for presentation mode annotations (from `p:showPr/p:penClr`). */ penColor?: string; /** Kiosk auto-restart interval in milliseconds (from `p:kiosk/@restart`). Only meaningful when showType is "kiosk". */ kioskRestartTime?: number; } /** * Slide dimensions from `p:sldSz` (CT_SlideSize, ECMA-376 §19.2.1.39). * * @example * ```ts * const size: PptxSlideSize = { widthEmu: 9144000, heightEmu: 6858000, type: 'screen4x3' }; * // => satisfies PptxSlideSize * ``` */ interface PptxSlideSize { /** `@cx` in EMU. Omitted or non-positive values leave the loaded width alone. */ widthEmu?: number; /** `@cy` in EMU. Omitted or non-positive values leave the loaded height alone. */ heightEmu?: number; /** * `@type` (ST_SlideSizeType). The schema default is `custom`, which is * why PowerPoint omits the attribute for a non-preset size. */ type?: string; } /** * A named custom slide show (`p:custShowLst / p:custShow`). * * Custom shows define ordered subsets of slides that can be presented * independently of the full deck. * * @example * ```ts * const show: PptxCustomShow = { * name: "Executive Summary", * id: "0", * slideRIds: ["rId2", "rId5", "rId8"], * }; * // => satisfies PptxCustomShow * ``` */ interface PptxCustomShow { /** Custom show name. */ name: string; /** Custom show id. */ id: string; /** Ordered list of slide relationship IDs included in this custom show. */ slideRIds: string[]; /** Original `p:custShow` subtree used to preserve unmodelled attributes and extensions. */ rawXml?: XmlObject; } /** * An ordered section in the presentation (from `p:sectionLst` / `p14:sectionLst`). * * Sections group consecutive slides under a named heading (visible * in the PowerPoint slide sorter). * * @example * ```ts * const section: PptxSection = { * id: "sec_1", * name: "Introduction", * slideIds: ["256", "257"], * }; * // => satisfies PptxSection * ``` */ interface PptxSection { /** Section unique identifier (GUID or synthetic). */ id: string; /** Human-readable section name. */ name: string; /** Ordered list of numeric slide IDs that belong to this section. */ slideIds: string[]; /** Whether the section is collapsed in the slide sorter (from p15:sectionPr). */ collapsed?: boolean; /** Section highlight color hex (from p15:sectionPr/@clr). */ color?: string; /** Original section subtree used to preserve unmodelled attributes and extensions. */ rawXml?: XmlObject; } /** * Write-protection hash data parsed from `p:modifyVerifier` in `presentation.xml`. * * When present, the presentation is marked as "read-only recommended" or * write-protected with a password hash. The hash parameters follow the * ECMA-376 Part 1, section 19.2.1.22 specification. * * @example * ```ts * const verifier: PptxModifyVerifier = { * algorithmName: "SHA-512", * hashData: "base64EncodedHash==", * saltData: "base64EncodedSalt==", * spinValue: 100000, * }; * // => satisfies PptxModifyVerifier * ``` */ interface PptxModifyVerifier { /** Hash algorithm name (e.g. "SHA-512", "SHA-1"). */ algorithmName?: string; /** Base64-encoded hash value. */ hashData?: string; /** Base64-encoded salt value. */ saltData?: string; /** Number of hash iterations (spin count). */ spinValue?: number; /** Legacy algorithm ID extension. */ algIdExt?: string; /** Legacy algorithm ID. */ cryptAlgorithmSid?: number; /** Cryptographic algorithm type (e.g. "typeAny"). */ cryptAlgorithmType?: string; /** Cryptographic provider name. */ cryptProvider?: string; /** Cryptographic provider type (e.g. "providerTypeRsaFull"). */ cryptProviderType?: string; /** Cryptographic algorithm class (e.g. "hash"). */ cryptAlgorithmClass?: string; } /** * Photo album metadata from `p:photoAlbum` in `presentation.xml`. * * Stores settings for presentations created via Insert > Photo Album. * * @see ECMA-376 Part 1, §19.2.1.27 */ interface PptxPhotoAlbum { /** Whether photos are displayed in black-and-white. */ bw?: boolean; /** Whether captions are shown below each photo. */ showCaptions?: boolean; /** Photo album layout (e.g. "1pic", "2pic", "4pic", "fitToSlide"). */ layout?: string; /** Frame style applied to each photo (e.g. "frameStyle1"). */ frame?: string; } /** * East Asian line-break (kinsoku) settings from `p:kinsoku` in `presentation.xml`. * * Defines forbidden start/end characters for a given language so that * line-breaking follows East Asian typographic rules. * * @see ECMA-376 Part 1, §19.2.1.17 */ interface PptxKinsoku { /** Language code (e.g. "ja-JP", "zh-CN"). */ lang?: string | null; /** Characters that cannot begin a line. */ invalStChars?: string; /** Characters that cannot end a line. */ invalEndChars?: string; /** Original leaf retained for unknown attribute preservation. */ rawXml?: XmlObject; } /** * Root data structure returned by {@link PptxHandlerCore.load}. * * Contains every slide, canvas dimensions, theme data, layout options, * metadata, and optional features (custom shows, sections, macros, * digital signatures, embedded fonts). * * @example * ```ts * const data: PptxData = await handler.load(buffer); * console.log(`${data.slides.length} slides, ${data.width}×${data.height}`); * // => e.g. "24 slides, 960×540" * ``` */ interface PptxData { slides: PptxSlide[]; width: number; height: number; /** Slide width in EMU (for save round-trip). */ widthEmu?: number; /** Slide height in EMU (for save round-trip). */ heightEmu?: number; /** Slide size type from `p:sldSz/@type` (e.g. "screen4x3", "screen16x9", "custom"). */ slideSizeType?: string; /** Notes page width in EMU (from `p:notesSz`). */ notesWidthEmu?: number; /** Notes page height in EMU (from `p:notesSz`). */ notesHeightEmu?: number; layoutOptions?: PptxLayoutOption[]; headerFooter?: PptxHeaderFooter; /** Presentation-level properties parsed from `presentationPr.xml`. */ presentationProperties?: PptxPresentationProperties; /** Named custom slide shows from `p:custShowLst`. */ customShows?: PptxCustomShow[]; /** Ordered presentation sections from `p:sectionLst` / `p14:sectionLst`. */ sections?: PptxSection[]; warnings?: PptxCompatibilityWarning[]; /** Map of theme colour scheme keys to resolved hex values. */ themeColorMap?: Record; /** Full parsed theme object with colours, fonts, and name. */ theme?: PptxTheme; /** Available theme parts discovered in `ppt/theme/`. */ themeOptions?: PptxThemeOption[]; /** Parsed table style definitions from `ppt/tableStyles.xml`. */ tableStyleMap?: ParsedTableStyleMap; /** Whether the presentation is password-protected. */ isPasswordProtected?: boolean; /** Embedded font data (name + binary data URL) extracted from the presentation. */ embeddedFonts?: PptxEmbeddedFont[]; /** Typed `p:embeddedFontLst` package metadata, including unresolved variants. */ embeddedFontList?: PptxEmbeddedFontList; /** * `p:presentation/@embedTrueTypeFonts` (ECMA-376 §19.2.1.26): the author's * saved preference that TrueType fonts referenced by the deck be embedded. * `undefined` when the attribute is absent (spec default `false`). * * This is purely declarative in this library: fonts are only ever embedded * when the caller explicitly supplies `embeddedFontList`/`embeddedFonts` * (there is no automatic embed-on-save), so toggling this flag does not * gate any embedding behaviour of its own here - it only round-trips the * author's stated preference, the same way real PowerPoint reads it back * as a checkbox state rather than a trigger. See `@saveSubsetFonts`, * which is a separate, deliberately unimplemented flag (no glyph * subsetting) that does not interact with this one. */ embedTrueTypeFonts?: boolean; /** * Presentation-level default text style (`p:defaultTextStyle`): the * last-resort paragraph/run-property fallback for every shape (placeholder * or not) whose local and inherited cascade leaves a field undefined. * Keyed the same way as {@link PptxMasterTextStyles} categories: `-1` is * `a:defPPr`, `0`-`8` are `a:lvl1pPr`-`a:lvl9pPr`. */ defaultTextStyle?: PptxTextStyleLevels; /** Most-recently-used colour list from presentation properties. */ mruColors?: string[]; /** Parsed notes master data if present in the PPTX. */ notesMaster?: PptxNotesMaster; /** Parsed handout master data if present in the PPTX. */ handoutMaster?: PptxHandoutMaster; /** Structured slide master data for each master in the presentation. */ slideMasters?: PptxSlideMaster[]; /** Parsed tag collections attached to the presentation or slides. */ tags?: PptxTagCollection[]; /** Custom document properties from `docProps/custom.xml`. */ customProperties?: PptxCustomProperty[]; /** Core document properties from `docProps/core.xml`. */ coreProperties?: PptxCoreProperties; /** Extended (application) properties from `docProps/app.xml`. */ appProperties?: PptxAppProperties; /** Whether the presentation contains VBA macros (is a .pptm file). */ hasMacros?: boolean; /** Whether the presentation contains digital signatures (`_xmlsignatures/` parts). */ hasDigitalSignatures?: boolean; /** Number of digital signatures found. */ digitalSignatureCount?: number; /** Presentation-level drawing guides from `p:extLst`. */ presentationGuides?: PptxDrawingGuide[]; /** View properties from `ppt/viewProps.xml`. */ viewProperties?: PptxViewProperties; /** Write-protection verifier from `p:modifyVerifier` in `presentation.xml`. */ modifyVerifier?: PptxModifyVerifier; /** Photo album metadata from `p:photoAlbum` in `presentation.xml`. */ photoAlbum?: PptxPhotoAlbum; /** East Asian line-break settings from `p:kinsoku` in `presentation.xml`. */ kinsoku?: PptxKinsoku; /** Custom XML data parts from `customXml/` in the OPC package. */ customXmlParts?: PptxCustomXmlPart[]; /** Customer data references from `p:custDataLst` in `presentation.xml`. */ customerData?: PptxCustomerData[]; /** Thumbnail image binary data from `docProps/thumbnail.{jpeg,png}`. */ thumbnailData?: Uint8Array; /** Comment authors parsed from `ppt/commentAuthors.xml` for round-trip preservation. */ commentAuthors?: PptxCommentAuthor[]; /** Office 2021 p188 authors from the modern Author part. */ modernCommentAuthors?: PptxModernCommentAuthor[]; /** * OOXML conformance class of the loaded file. * - `'strict'` -- ISO/IEC 29500 Strict (uses `purl.oclc.org` namespace URIs) * - `'transitional'` -- ECMA-376 Transitional (uses `schemas.openxmlformats.org` URIs) * * When saving, if the save option `conformance` is `'preserve'` (default), * the file will be saved using the same conformance class as the original. */ conformance?: 'strict' | 'transitional'; } /** * Target format for slide export. * * @see {@link PptxExportOptions} */ type PptxExportFormat = 'pdf' | 'png' | 'svg'; /** * Options controlling slide export to raster or vector formats. * * @example * ```ts * const opts: PptxExportOptions = { * format: "png", * slideIndices: [0, 2, 4], * dpi: 300, * }; * // => satisfies PptxExportOptions * ``` */ interface PptxExportOptions { /** Target format. */ format: PptxExportFormat; /** Slide indices to export (0-based). If omitted, all slides are exported. */ slideIndices?: number[]; /** Output width in pixels (for PNG). Height is derived from aspect ratio. */ width?: number; /** DPI for raster export (default 150). */ dpi?: number; /** Whether to include hidden slides. */ includeHidden?: boolean; } /** * Embedded font data extracted from a PPTX file. * * Used to register `@font-face` rules so the renderer can display * the correct typeface even when the system font is missing. * * @example * ```ts * const font: PptxEmbeddedFont = { * name: "CustomSans", * dataUrl: "data:font/truetype;base64,AAEAK...", * format: "truetype", * }; * // => satisfies PptxEmbeddedFont * ``` */ /** * A single Custom XML Data Part stored in `customXml/` within the OPC package. * * These parts are used by add-ins, data-binding, and enterprise templates * to store structured data alongside the presentation. * * @see ECMA-376 Part 1, §15.2.5 */ interface PptxCustomXmlPart { /** Item number (e.g. "1" for `customXml/item1.xml`). */ id: string; /** Raw XML string content of the custom XML item. */ data: string; /** Schema target namespace URI from `itemProps` (ds:schemaRef/@ds:uri). */ schemaUri?: string; /** Raw XML string content of the associated `itemProps` file. */ properties?: string; /** Raw XML string content of the OPC relationship file (`customXml/_rels/item{id}.xml.rels`). */ rels?: string; } interface PptxEmbeddedFont { name: string; dataUrl: string; bold?: boolean; italic?: boolean; /** CSS font format hint (e.g. "truetype", "opentype"). */ format?: 'truetype' | 'opentype' | 'woff' | 'woff2'; /** * Deobfuscated (clear-text) font binary data preserved from load * for round-trip re-embedding on save. When present, the save * pipeline will re-obfuscate and write this data back into the ZIP. */ rawFontData?: Uint8Array; /** * Original ZIP path of the font part (e.g. `ppt/fonts/{GUID}.fntdata`). * Preserved from load for round-trip. */ partPath?: string; /** * The GUID used for obfuscation, either from the `fontKey` attribute * or extracted from the part path. Preserved from load for round-trip. */ fontGuid?: string; /** * Relationship ID (e.g. `rId21`) of the font part in * `ppt/_rels/presentation.xml.rels`. Preserved from load so the save * pipeline can reuse the original part/rel instead of minting a new * GUID-named copy alongside the stale original. */ originalRId?: string; /** * Raw bytes of the original obfuscated font part exactly as they were * stored in the source ZIP. When the loader could not determine a * usable GUID (e.g. EOT extraction path), the save pipeline preserves * these bytes verbatim under the original path/rel. */ originalPartBytes?: Uint8Array; } //#region src/core/types/theme-presets.d.ts /** * A complete theme preset that can be applied to a presentation. * * @example * ```ts * import { THEME_PRESETS } from "pptx-viewer-core"; * * const office = THEME_PRESETS.find(p => p.id === "office"); * await handler.switchTheme(office.colorScheme, office.fontScheme, office.name); * ``` */ interface PptxThemePreset { /** Unique identifier for the preset. */ id: string; /** Human-readable display name. */ name: string; /** The 12-colour scheme. */ colorScheme: PptxThemeColorScheme; /** Heading and body font families. */ fontScheme: PptxThemeFontScheme; } //#endregion //#region src/core/builders/sdk/types.d.ts /** Position and size in pixels. Converted to EMU internally when needed. */ interface ElementPosition { x: number; y: number; width: number; height: number; rotation?: number; } type FillInput = { type: 'solid'; color: string; opacity?: number; } | { type: 'gradient'; /** * Gradient direction in the OOXML `a:lin/@ang` convention: degrees * clockwise from the positive x-axis, pointing from the first stop * towards the last (`0` = left to right, `90` = top to bottom). This is * what lands in `ShapeStyle.fillGradientAngle` and what is written back * to the file, NOT a CSS `linear-gradient()` angle. */ angle?: number; gradientType?: 'linear' | 'radial'; stops: Array<{ color: string; position: number; opacity?: number; }>; } | { type: 'pattern'; preset: string; foreground?: string; background?: string; } | { type: 'image'; url: string; mode?: 'stretch' | 'tile'; } | { type: 'none'; }; interface StrokeInput { color?: string; width?: number; dash?: StrokeDashType; opacity?: number; join?: 'round' | 'bevel' | 'miter'; cap?: 'flat' | 'rnd' | 'sq'; } interface ShadowInput { color?: string; blur?: number; offsetX?: number; offsetY?: number; opacity?: number; } interface TextStyleInput { fontSize?: number; fontFamily?: string; bold?: boolean; italic?: boolean; underline?: boolean; strikethrough?: boolean; color?: string; alignment?: 'left' | 'center' | 'right' | 'justify'; verticalAlignment?: 'top' | 'middle' | 'bottom'; lineSpacing?: number; spaceBefore?: number; spaceAfter?: number; } interface TextSegmentInput { text: string; style?: Partial; } interface TextOptions extends Partial { fontSize?: number; fontFamily?: string; bold?: boolean; italic?: boolean; underline?: boolean; strikethrough?: boolean; color?: string; alignment?: 'left' | 'center' | 'right' | 'justify'; verticalAlignment?: 'top' | 'middle' | 'bottom'; lineSpacing?: number; fill?: FillInput; stroke?: StrokeInput; shadow?: ShadowInput; opacity?: number; } interface ShapeOptions extends Partial { fill?: FillInput; stroke?: StrokeInput; text?: string; textStyle?: Partial; adjustments?: Record; shadow?: ShadowInput; opacity?: number; } interface ImageOptions extends Partial { altText?: string; cropLeft?: number; cropTop?: number; cropRight?: number; cropBottom?: number; opacity?: number; } interface TableInput { rows: TableRowInput[]; columnWidths?: number[]; style?: string; bandRows?: boolean; bandColumns?: boolean; firstRow?: boolean; lastRow?: boolean; firstCol?: boolean; lastCol?: boolean; } interface TableRowInput { cells: TableCellInput[]; height?: number; } interface TableCellInput { text: string; style?: Partial; fill?: FillInput; gridSpan?: number; rowSpan?: number; } interface TableOptions extends Partial {} interface ChartSeriesInput { name: string; values: number[]; color?: string; boxWhiskerOptions?: PptxChartBoxWhiskerOptions; histogramOptions?: PptxChartHistogramOptions; waterfallOptions?: PptxChartWaterfallOptions; regionMapOptions?: PptxChartRegionMapOptions; treemapOptions?: PptxChartTreemapOptions; } interface ChartInput { series: ChartSeriesInput[]; categories: string[]; /** ChartEx hierarchy levels in leaf-to-root XML order. */ categoryLevels?: string[][]; title?: string; hasLegend?: boolean; legendPosition?: 't' | 'b' | 'l' | 'r' | 'tr'; grouping?: 'clustered' | 'stacked' | 'percentStacked'; /** Bar series direction (`c:barDir`): vertical columns (default) or horizontal bars. */ barDirection?: 'col' | 'bar'; } interface ChartOptions extends Partial {} interface ConnectorOptions extends Partial { type?: 'straight' | 'bent' | 'curved'; stroke?: StrokeInput; startArrow?: ConnectorArrowType; endArrow?: ConnectorArrowType; from?: { elementId: string; site: number; }; to?: { elementId: string; site: number; }; } interface MediaOptions extends Partial { autoPlay?: boolean; loop?: boolean; volume?: number; trimStartMs?: number; trimEndMs?: number; posterFrame?: string; } interface GroupOptions extends Partial {} type BackgroundInput = { type: 'solid'; color: string; } | { type: 'gradient'; /** * Slide backgrounds are stored as a ready-made CSS gradient string * (`PptxSlide.backgroundGradient`), so this is a CSS * `linear-gradient()` angle: degrees clockwise from "to top" * (`90` = left to right, `180` = top to bottom). Defaults to `180`. */ angle?: number; stops: Array<{ color: string; position: number; }>; } | { type: 'image'; source: string; }; interface TransitionInput { type: PptxTransitionType; duration?: number; direction?: string; advanceAfterMs?: number; } interface AnimationInput { preset: PptxAnimationPreset; trigger?: PptxAnimationTrigger; duration?: number; delay?: number; } interface PresentationOptions { /** Slide width in EMU. Default: 12192000 (16:9 widescreen). */ width?: number; /** Slide height in EMU. Default: 6858000 (16:9 widescreen). */ height?: number; /** Theme configuration. */ theme?: PresentationThemeInput; /** Presentation title (stored in docProps/core.xml). */ title?: string; /** Presentation author. */ creator?: string; /** * Number of blank slides to include in the initial presentation. * Default: 0 (no slides). Slides use the "Blank" layout. */ initialSlideCount?: number; } interface PresentationThemeInput { name?: string; colors?: { dk1?: string; lt1?: string; dk2?: string; lt2?: string; accent1?: string; accent2?: string; accent3?: string; accent4?: string; accent5?: string; accent6?: string; hlink?: string; folHlink?: string; }; fonts?: { majorFont?: string; minorFont?: string; }; } //#endregion //#region src/core/builders/sdk/SlideBuilder.d.ts /** * Fluent builder for a single slide. * * @example * ```ts * const slide = new SlideBuilder(1) * .addText("Hello World", { fontSize: 36, bold: true, x: 100, y: 50 }) * .addShape("roundRect", { fill: { type: "solid", color: "#4472C4" } }) * .setNotes("Remember to mention key points") * .setBackground({ type: "solid", color: "#F5F5F5" }) * .build(); * ``` */ declare class SlideBuilder { private readonly slide; /** * @param slideNumber - 1-based slide number. * @param layoutPath - Optional layout archive path. * @param layoutName - Optional layout display name. */ constructor(slideNumber: number, layoutPath?: string, layoutName?: string); /** Add a text box to the slide. */ addText(text: string | TextSegmentInput[], options?: TextOptions): this; /** Add a shape to the slide. */ addShape(shapeType: string, options?: ShapeOptions): this; /** Add a connector (line) to the slide. */ addConnector(options?: ConnectorOptions): this; /** Add an image to the slide. */ addImage(source: string, options?: ImageOptions): this; /** Add a table to the slide. */ addTable(input: TableInput, options?: TableOptions): this; /** Add a chart to the slide. */ addChart(chartType: PptxChartType, input: ChartInput, options?: ChartOptions): this; /** Add a media element (video or audio) to the slide. */ addMedia(mediaType: 'video' | 'audio', source: string, options?: MediaOptions): this; /** Add a group of elements to the slide. */ addGroup(children: PptxElement[], options?: GroupOptions): this; /** Add a pre-built element directly. */ addElement(element: PptxElement): this; /** Set slide background. */ setBackground(bg: BackgroundInput): this; /** Set slide transition. */ setTransition(input: TransitionInput): this; /** Add an animation to an element on this slide. */ addAnimation(elementId: string, input: AnimationInput): this; /** Set speaker notes. */ setNotes(text: string): this; /** Mark the slide as hidden. */ setHidden(hidden: boolean): this; /** Assign the slide to a section. */ setSection(name: string, id?: string): this; /** * Add a freeform shape from SVG path data. * * Creates a custom-geometry shape element using the provided SVG path * string and appends it to the slide's element list. * * @param pathData - An SVG path data string (e.g. `"M 0 0 L 100 50 L 50 100 Z"`). * @param options - Optional position, styling, and size overrides. * @returns The builder instance for chaining. * * @example * ```ts * new SlideBuilder(1) * .addFreeform("M 0 0 C 33 0 66 100 100 100", { * stroke: { color: "#FF0000", width: 2 }, * }) * .build(); * ``` */ addFreeform(pathData: string, options?: ShapeOptions): this; /** * Add a pre-built element from any element builder (calls `.build()` for you). * * Accepts any object with a `build()` method that returns a {@link PptxElement}, * such as {@link TextBuilder}, {@link ShapeBuilder}, {@link ImageBuilder}, etc. * * @param builder - An element builder with a `.build()` method. * @returns The builder instance for chaining. * * @example * ```ts * const title = TextBuilder.create("Hello").fontSize(36).bold(); * new SlideBuilder(1).addBuilderElement(title).build(); * ``` */ addBuilderElement(builder: { build(): PptxElement; }): this; /** * Remove an element by its ID. * * Filters out the element with the given ID from the slide's element list. * If no element matches, the slide is left unchanged. * * @param elementId - The unique ID of the element to remove. * @returns The builder instance for chaining. * * @example * ```ts * const slide = new SlideBuilder(1) * .addText("temp", { x: 0, y: 0 }) * .removeElement("txt_abc123_1") * .build(); * ``` */ removeElement(elementId: string): this; /** * Get the current list of elements on this slide. * * Returns a readonly view of the elements array. Useful for inspecting * what has been added so far during the build process. * * @returns A readonly array of the slide's current elements. * * @example * ```ts * const builder = new SlideBuilder(1).addText("Hi"); * console.log(builder.getElements().length); // 1 * ``` */ getElements(): readonly PptxElement[]; /** * Get the number of elements on this slide. * * @returns The count of elements currently added to the slide. * * @example * ```ts * const builder = new SlideBuilder(1) * .addText("A").addText("B"); * console.log(builder.elementCount); // 2 * ``` */ get elementCount(): number; /** * Get the last added element (useful for getting its ID for animations). * * Returns `undefined` if the slide has no elements yet. * * @returns The most recently added element, or `undefined`. * * @example * ```ts * const builder = new SlideBuilder(1).addShape("rect"); * const shape = builder.getLastElement(); * if (shape) { * builder.addAnimation(shape.id, { preset: "fadeIn" }); * } * ``` */ getLastElement(): PptxElement | undefined; /** * Set the slide name/title for organizational purposes. * * Stores an arbitrary name string on the slide object. This is useful * for labeling slides in tooling or custom workflows. * * @param name - The display name to assign to the slide. * @returns The builder instance for chaining. * * @example * ```ts * new SlideBuilder(1) * .setName("Introduction") * .addText("Welcome!") * .build(); * ``` */ setName(name: string): this; /** Return the built {@link PptxSlide}. */ build(): PptxSlide; } //#endregion //#region src/core/builders/sdk/PresentationBuilder.d.ts /** Result returned by {@link PresentationBuilder.create}. */ interface PresentationBuilderResult { /** Initialized handler ready for editing and saving. */ handler: PptxHandler; /** Parsed presentation data. */ data: PptxData; /** Convenience slide builder factory. */ createSlide: (layoutName?: string) => SlideBuilder; } //#endregion //#region src/core/builders/fluent/PptxXmlBuilder.d.ts /** * Fluent interface for navigating and mutating a {@link PptxData} structure. * Provides method-chaining access to slides, elements, and notes. */ interface IPptxXmlBuilder { /** Navigate to a slide by zero-based index (Pascal-case alias). */ Slides(index: number): PptxSlideBuilder; /** Navigate to a slide by zero-based index. */ slide(index: number): PptxSlideBuilder; /** Navigate to a slide by zero-based index (plural alias). */ slides(index: number): PptxSlideBuilder; /** Return the underlying presentation data. */ project(): PptxData; } /** * Root builder of the fluent PPTX editing API. * * Wraps a {@link PptxData} object and provides chainable accessors * to navigate into slides, elements, and notes for in-place mutation. */ declare class PptxXmlBuilder implements IPptxXmlBuilder { /** The presentation data being mutated. */ private readonly data; /** @param data - The presentation data to wrap. */ constructor(data: PptxData); /** * Factory method to create a builder from presentation data. * @param data - The presentation data to wrap. * @returns A new {@link PptxXmlBuilder} instance. */ static from(data: PptxData): PptxXmlBuilder; /** @inheritdoc */ Slides(index: number): PptxSlideBuilder; /** * Navigate to a slide by zero-based index. * @param index - Zero-based slide index. * @returns A {@link PptxSlideBuilder} for the requested slide. * @throws Error if index is not an integer or is out of range. */ slide(index: number): PptxSlideBuilder; /** @inheritdoc */ slides(index: number): PptxSlideBuilder; /** Return the underlying {@link PptxData}. */ project(): PptxData; /** Pascal-case alias for {@link project}. */ Project(): PptxData; } /** * Fluent builder scoped to a single slide. * Provides navigation to the slide's elements and notes. */ declare class PptxSlideBuilder { /** The slide being operated on. */ private readonly slideValue; /** Reference back to the root builder for chaining. */ private readonly rootBuilder; /** * @param slideValue - The slide data. * @param rootBuilder - The parent builder. */ constructor(slideValue: PptxSlide, rootBuilder: PptxXmlBuilder); /** Navigate to the slide's notes builder (getter). */ get Notes(): PptxSlideNotesBuilder; /** Navigate to the slide's notes builder. */ notes(): PptxSlideNotesBuilder; /** Navigate to the slide's elements builder. */ elements(): PptxSlideElementsBuilder; /** Return the underlying slide data. */ project(): PptxSlide; /** Pascal-case alias for {@link project}. */ Project(): PptxSlide; /** Navigate back to the root builder. */ done(): PptxXmlBuilder; /** Pascal-case alias for {@link done}. */ Done(): PptxXmlBuilder; } /** * Fluent builder for manipulating the elements array of a single slide. * Supports adding, removing, and updating elements by ID. */ declare class PptxSlideElementsBuilder { private readonly slideValue; private readonly slideBuilder; /** * @param slideValue - The slide whose elements are being modified. * @param slideBuilder - The parent slide builder for chaining. */ constructor(slideValue: PptxSlide, slideBuilder: PptxSlideBuilder); /** * Append an element to the slide's element list. * @param element - The element to add. * @returns This builder for chaining. */ add(element: PptxElement): this; /** * Remove an element from the slide by its ID. * @param elementId - The ID of the element to remove. * @returns This builder for chaining. */ removeById(elementId: string): this; /** * Update an element in-place by ID using a transform function. * @param elementId - The ID of the element to update. * @param updater - A function that receives the current element and returns the replacement. * @returns This builder for chaining. */ updateById(elementId: string, updater: (current: PptxElement) => PptxElement): this; /** Return the current elements array. */ project(): PptxElement[]; /** Navigate back to the slide builder. */ done(): PptxSlideBuilder; } /** * Fluent builder for manipulating speaker notes on a single slide. * Supports adding, setting, clearing, and retrieving notes text. */ declare class PptxSlideNotesBuilder { private readonly slideValue; private readonly slideBuilder; /** * @param slideValue - The slide whose notes are being modified. * @param slideBuilder - The parent slide builder for chaining. */ constructor(slideValue: PptxSlide, slideBuilder: PptxSlideBuilder); /** * Append text to existing notes (separated by newline). * @param text - The text to append. * @returns This builder for chaining. */ add(text: string): this; /** Pascal-case alias for {@link add}. */ Add(text: string): this; /** * Replace all notes with the given text. * @param text - The replacement notes text. Empty string clears notes. * @returns This builder for chaining. */ set(text: string): this; /** Pascal-case alias for {@link set}. */ Set(text: string): this; /** Remove all notes from the slide. */ clear(): this; /** Pascal-case alias for {@link clear}. */ Clear(): this; /** Return the current notes text, or `undefined` if none. */ get(): string | undefined; /** Pascal-case alias for {@link get}. */ Get(): string | undefined; /** Navigate back to the slide builder. */ done(): PptxSlideBuilder; /** Pascal-case alias for {@link done}. */ Done(): PptxSlideBuilder; /** * Synchronize the `notesSegments` array from the plain-text notes string. * Splits text on newlines and creates corresponding {@link TextSegment} entries * with paragraph break markers between lines. */ private syncSegmentsFromNotes; } //#endregion //#region src/core/core/types.d.ts interface PptxHandlerLoadOptions { eagerDecodeImages?: boolean; password?: string; /** * Maximum total uncompressed bytes accepted from the input ZIP archive. * Defaults to 500 MiB. When the sum of `_data.uncompressedSize` across * all archive entries exceeds this cap, `load()` rejects with a * {@link ZipBombError}. A hard cap of 65 536 archive entries also * applies. */ maxUncompressedBytes?: number; /** * When `false` (default), relationship targets that resolve to * `http://` or `https://` URLs are dropped from rendered slides * (image, picture, background). Set to `true` to allow external image * URLs to flow through to ``. * * Disabled by default to mitigate SSRF / privacy-leak vectors in * server-side rendering and headless export pipelines. */ allowExternalImages?: boolean; } /** Output format for the save pipeline. */ type PptxSaveFormat = 'pptx' | 'ppsx' | 'pptm'; interface PptxHandlerSaveOptions { headerFooter?: PptxHeaderFooter; presentationProperties?: PptxPresentationProperties; customShows?: PptxCustomShow[]; sections?: PptxSection[]; coreProperties?: PptxCoreProperties; appProperties?: PptxAppProperties; customProperties?: PptxCustomProperty[]; /** Updated notes master data to save back to notesMaster1.xml. */ notesMaster?: PptxNotesMaster; /** Updated handout master data to save back to handoutMaster1.xml. */ handoutMaster?: PptxHandoutMaster; /** * Updated slide masters to save back to ppt/slideMasters/slideMaster*.xml. * Each entry in the array applies typed mutations (clrMap, hf flags, * background) to the master at its `path`. Masters not listed here pass * through verbatim from the original load. */ slideMasters?: PptxSlideMaster[]; /** * Updated slide layouts to save back to ppt/slideLayouts/slideLayout*.xml. * Each entry applies typed mutations (clrMapOverride, attrs, hf flags, * background) to the layout at its `path`. Layouts not listed here pass * through verbatim from the original load. */ slideLayouts?: PptxSlideLayout[]; /** Updated tag collections to save back to ppt/tags/tag*.xml. */ tags?: PptxTagCollection[]; /** Presentation-level customer data references to author or update. */ customerData?: PptxCustomerData[]; /** Photo album metadata to save back to `p:photoAlbum`. */ photoAlbum?: PptxPhotoAlbum; /** * Slide dimensions to write back to `p:sldSz`. * * Omitting the option preserves the load-time dimensions verbatim, which * is why an edit made through a viewer's Slide Size control has to reach * the save call: nothing else in the pipeline can observe it. * * PowerPoint derives `Presentation.PageSetup.SlideSize` from `@cx`/`@cy` * alone (verified by COM: an A4-typed `p:sldSz` carrying 4:3 dimensions * still reports `ppSlideSizeCustom`), so `type` is written for fidelity * but the dimensions are what actually decide the reported preset. */ slideSize?: PptxSlideSize; /** East Asian line-break settings to save back to `p:kinsoku`. */ kinsoku?: PptxKinsoku | null; /** Write-protection verifier. Set to `null` to remove, `undefined` to preserve existing. */ modifyVerifier?: PptxModifyVerifier | null; /** * `p:presentation/@embedTrueTypeFonts` to write. `undefined` preserves * whatever was loaded (or omits the attribute for a brand-new deck); * purely declarative here, see {@link PptxData.embedTrueTypeFonts}. */ embedTrueTypeFonts?: boolean; /** * Presentation-level default text style edits to save back to * `p:defaultTextStyle`. Only the levels present in the map are touched; * omitted levels and any unmodelled XML on an edited level survive * untouched. See {@link PptxData.defaultTextStyle}. */ defaultTextStyle?: PptxTextStyleLevels; /** View properties to save back to ppt/viewProps.xml. */ viewProperties?: PptxViewProperties; /** * Table style edits to save back to `ppt/tableStyles.xml`. Pass the * `tableStyleMap` from `PptxData` (optionally with edited entries) * to persist user edits. The `def` GUID and any unmodelled XML are * preserved verbatim. Omitting the option round-trips the original * part untouched. */ tableStyles?: ParsedTableStyleMap; /** * Target output format. * - `'pptx'` (default): Standard presentation. * - `'ppsx'`: Slide-show file (opens in presentation mode). * - `'pptm'`: Macro-enabled presentation (requires VBA data). */ outputFormat?: PptxSaveFormat; /** * Embedded fonts to write back (or add) to the saved PPTX. * * Pass the `embeddedFonts` array from `PptxData` to preserve existing * embedded fonts during save. You can also add new fonts by including * entries with `rawFontData` populated. * * When omitted, the save pipeline will automatically re-embed any * fonts that were loaded from the original PPTX and have `rawFontData` * preserved (i.e. the default is lossless round-trip). */ embeddedFonts?: PptxEmbeddedFont[]; /** Typed embedded-font list metadata. Set to null to remove fonts and relationships. */ embeddedFontList?: PptxEmbeddedFontList | null; /** * OOXML conformance class for the saved output. * - `'preserve'` (default): use the same conformance as the loaded file. * - `'strict'`: force Strict Open XML (ISO/IEC 29500) namespace URIs. * - `'transitional'`: force Transitional (ECMA-376) namespace URIs. */ conformance?: 'strict' | 'transitional' | 'preserve'; } interface IPptxHandlerRuntime { /** * Release all resources held by this runtime (Blob URLs, caches, ZIP). * After calling, the runtime cannot be used further. */ dispose(): void; /** * Revoke all Blob URLs created during image loading. */ revokeBlobUrls(): void; getCompatibilityWarnings(): PptxCompatibilityWarning[]; getLayoutOptions(): PptxLayoutOption[]; getLayoutPreview(layoutPath: string): Promise; getLayoutPreviews(layoutPaths?: readonly string[]): Promise; createXmlBuilder(data: PptxData): PptxXmlBuilder; Builder(data: PptxData): PptxXmlBuilder; setTemplateBackground(path: string, backgroundColor: string | undefined): void; setPresentationTheme(themePath: string, applyToAllMasters?: boolean): Promise; getTemplateBackgroundColor(path: string): string | undefined; updateThemeColorScheme(colorScheme: PptxThemeColorScheme): Promise; updateThemeFontScheme(fontScheme: PptxThemeFontScheme): Promise; updateThemeName(name: string): Promise; applyTheme(colorScheme: PptxThemeColorScheme, fontScheme: PptxThemeFontScheme, themeName?: string): Promise; load(data: ArrayBuffer, options?: PptxHandlerLoadOptions): Promise; getChartDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; getSmartArtDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; getImageData(imagePath: string): Promise; /** * Extract a media file from the PPTX archive as an ArrayBuffer. * Returns undefined if the file is not found. */ getMediaArrayBuffer(mediaPath: string): Promise; save(slides: PptxSlide[], options?: PptxHandlerSaveOptions): Promise; exportSlides(slides: PptxSlide[], options: PptxExportOptions): Promise>; /** * Get the available slide layouts for a specific slide, based on the * slide's master. Scans the slide master's relationships to find all * layouts that belong to it. * * @param slideIndex - Zero-based slide index. * @param slides - Current slides array. * @returns Array of layout options belonging to the same slide master. */ getAvailableLayoutsForSlide(slideIndex: number, slides: PptxSlide[]): Promise; /** * Resolve the editable template (master + layout) elements a slide * inherits, each carrying a `master-` / `layout-` prefixed id. Excludes * placeholders; returns only decorative shapes/pictures/graphic frames. * * @param slideId - The slide's archive path (`PptxSlide.id`). */ getTemplateElementsForSlide(slideId: string): Promise; /** * Scan the loaded PPTX archive for all theme parts. */ getAvailableThemes(): Promise>; /** * Apply a different layout to an existing slide by updating the slide's * relationship to point to the new layout and re-parsing layout * placeholders / background. * * @param slideIndex - Zero-based slide index. * @param layoutPath - Archive path of the target layout * (e.g. `ppt/slideLayouts/slideLayout2.xml`). * @param slides - Current slides array. * @returns The updated slide with new layout path, name, and background. */ applyLayoutToSlide(slideIndex: number, layoutPath: string, slides: PptxSlide[]): Promise; } //#endregion //#region src/core/core/PptxHandlerRuntimeFactory.d.ts /** * Abstract factory contract for creating {@link IPptxHandlerRuntime} * instances. * * Implement this interface to supply a custom runtime (e.g. a * WASM-backed or test-double runtime) to {@link PptxHandlerCore}. */ interface IPptxHandlerRuntimeFactory { /** Instantiate and return a new runtime implementation. */ createRuntime(): IPptxHandlerRuntime; } //#endregion //#region src/core/utils/ooxml-crypto-types.d.ts /** * Type definitions for OOXML encryption and decryption. * * Contains all interfaces and type aliases used by the OOXML crypto modules. * * @module ooxml-crypto-types */ /** Supported encryption algorithms. */ type EncryptionAlgorithm = 'AES128' | 'AES256'; /** * Which OOXML encryption scheme to write when creating a password-protected * file. Real PowerPoint can write and open either scheme; this library * defaults to 'agile' (Office 2010+), matching PowerPoint's own default. */ type EncryptionScheme = 'agile' | 'standard'; /** Encryption options for creating encrypted files. */ interface EncryptionOptions { /** The encryption algorithm to use (defaults to AES256). */ algorithm?: EncryptionAlgorithm; /** Number of hash iterations for key derivation (defaults to 100000). Lower values speed up tests. */ spinCount?: number; /** * Which encryption scheme to write (defaults to 'agile'). 'standard' * writes the ECMA-376 Standard scheme (Office 2007-compatible: a single * password-derived AES-CBC key with a zero IV over the whole package), * mirroring the scheme this library already knows how to decrypt. */ encryptionScheme?: EncryptionScheme; } //#endregion //#region src/core/PptxHandlerCore.d.ts /** * Dependency injection options for {@link PptxHandlerCore}. * * Provide either `runtime` (an already-constructed runtime) or * `runtimeFactory` (a factory that will be called once). When neither * is supplied the default runtime is created automatically. * * @example * ```ts * // Use the default runtime: * const core = new PptxHandlerCore(); * * // Inject a custom runtime: * const core = new PptxHandlerCore({ runtime: myRuntime }); * * // Supply a factory for lazy creation: * const core = new PptxHandlerCore({ runtimeFactory: myFactory }); * // => PptxHandlerCore instance with injected runtime * ``` */ interface PptxHandlerCoreDependencies { runtime?: IPptxHandlerRuntime; runtimeFactory?: IPptxHandlerRuntimeFactory; } /** * Thin facade over the PPTX runtime implementation. * * All heavy parsing, serialisation, and XML manipulation is delegated to an * {@link IPptxHandlerRuntime}. This surface stays stable and small so that * callers remain decoupled from the runtime internals and host-specific * runtime swaps (e.g. WASM vs Node) can be done transparently. * * @remarks * - Constructed once per open document. * - Errors from encrypted files are caught at `load()` time via * {@link EncryptedFileError}. * - `PptxXmlBuilder` instances returned by `createXmlBuilder()` / `Builder()` * operate directly on the runtime’s in-memory ZIP. * * @example * ```ts * const handler = new PptxHandlerCore(); * const data = await handler.load(arrayBuffer); * // ... mutate slides ... * const out = await handler.save(data.slides); * // => Uint8Array of the modified .pptx file * ``` */ declare class PptxHandlerCore { private readonly runtime; /** * Create a new handler, optionally injecting a custom runtime. * * Resolution order: * 1. `dependencies.runtime` — use as-is. * 2. `dependencies.runtimeFactory` — call `createRuntime()` once. * 3. Fall back to {@link createDefaultPptxHandlerRuntime}. * * @param dependencies - Optional runtime or factory override. * * @example * ```ts * const core = new PptxHandlerCore(); * // => PptxHandlerCore instance with default runtime * ``` */ constructor(dependencies?: PptxHandlerCoreDependencies); /** * Release all resources held by this handler instance. * * Revokes every Blob URL created for images/media, clears all * in-memory caches, and releases the in-memory ZIP archive. * * Call this when the handler is no longer needed (e.g. component * unmount) to free memory immediately rather than waiting for GC. * * After calling `dispose()`, do not call any other methods — create * a new `PptxHandler` instance instead. */ dispose(): void; /** * Return any compatibility warnings detected during the most recent load. * * Warnings indicate features the editor cannot fully represent (e.g. * SmartArt, 3-D effects, embedded OLE objects). * * @returns Array of {@link PptxCompatibilityWarning} objects. */ getCompatibilityWarnings(): PptxCompatibilityWarning[]; /** * Get the slide layout options available in the loaded presentation. * * Each option maps to a `` inside the PPTX archive. * * @returns Array of {@link PptxLayoutOption} entries. */ getLayoutOptions(): PptxLayoutOption[]; /** * Build the artwork thumbnails backing the New Slide / Layout galleries. * * Parsing happens on first request and is memoised afterwards, so opening * the gallery costs one pass over the layout parts and reopening it costs * nothing. Callers that only need one entry should prefer * {@link getLayoutPreview}. * * @param layoutPaths - Restrict the result to these layouts; defaults to * every layout in the presentation. * @returns One {@link PptxLayoutPreview} per resolvable layout. */ getLayoutPreviews(layoutPaths?: readonly string[]): Promise; /** * Build the artwork thumbnail for a single layout. * * @param layoutPath - Archive path of the `p:sldLayout` part. * @returns The preview, or `null` when the presentation has no such layout. */ getLayoutPreview(layoutPath: string): Promise; /** * Create a fluent XML builder scoped to the given presentation data. * * The builder provides a chainable API for constructing and inserting * OpenXML nodes directly into the runtime’s in-memory ZIP. * * @param data - The parsed {@link PptxData} to bind the builder to. * @returns A new {@link PptxXmlBuilder} instance. */ createXmlBuilder(data: PptxData): PptxXmlBuilder; /** * Shorthand alias for {@link createXmlBuilder}. * * @param data - Parsed presentation data. * @returns A {@link PptxXmlBuilder} instance. */ Builder(data: PptxData): PptxXmlBuilder; /** * Register a background image for a specific template layout path. * * @param path - The internal PPTX path (e.g. `ppt/slideLayouts/slideLayout1.xml`). * @param backgroundColor - Optional hex colour to render behind the image. */ setTemplateBackground(path: string, backgroundColor: string | undefined): void; /** * Retrieve the background colour previously set for a template layout. * * @param path - The internal PPTX layout path. * @returns Hex colour string, or `undefined` if none was set. */ getTemplateBackgroundColor(path: string): string | undefined; /** * Replace the presentation’s theme by loading an external `.thmx` file. * * @param themePath - Absolute or relative path to the `.thmx` file. * @param applyToAllMasters - Apply to every slide master (default `true`). * * @example * ```ts * await handler.setPresentationTheme("./themes/corporate.thmx"); * // => void — theme XML replaced in the in-memory ZIP * ``` */ setPresentationTheme(themePath: string, applyToAllMasters?: boolean): Promise; /** * Modify the theme’s colour scheme (accent colours, background, text, etc.). * * @param colorScheme - A {@link PptxThemeColorScheme} with hex colour values. * * @example * ```ts * await handler.updateThemeColorScheme({ * dk1: "#1A1A2E", dk2: "#16213E", * lt1: "#FFFFFF", lt2: "#E8E8E8", * accent1: "#0F3460", accent2: "#533483", * accent3: "#E94560", accent4: "#F0A500", * }); * // => void — colour scheme updated in the in-memory theme XML * ``` */ updateThemeColorScheme(colorScheme: PptxThemeColorScheme): Promise; /** * Update the theme’s font scheme (heading + body typefaces). * * @param fontScheme - A {@link PptxThemeFontScheme} with font family names. * * @example * ```ts * await handler.updateThemeFontScheme({ * majorFont: "Montserrat", * minorFont: "Open Sans", * }); * // => void — font scheme updated in the in-memory theme XML * ``` */ updateThemeFontScheme(fontScheme: PptxThemeFontScheme): Promise; /** * Rename the presentation theme. * * @param name - New display name for the theme. */ updateThemeName(name: string): Promise; /** * Apply a complete theme in one call (colour scheme + font scheme + optional name). * * This is a convenience wrapper over {@link updateThemeColorScheme}, * {@link updateThemeFontScheme}, and {@link updateThemeName}. * * @param colorScheme - Colour definitions. * @param fontScheme - Font definitions. * @param themeName - Optional theme display name. * * @example * ```ts * await handler.applyTheme( * { dk1: "#000", lt1: "#FFF", accent1: "#0066CC", /* … *\/ }, * { majorFont: "Helvetica", minorFont: "Arial" }, * "Corporate 2025", * ); * // => void — colour scheme, font scheme, and name applied atomically * ``` */ applyTheme(colorScheme: PptxThemeColorScheme, fontScheme: PptxThemeFontScheme, themeName?: string): Promise; /** * Switch the presentation's theme, updating both the underlying XML and * re-resolving all element colours in-place. * * This is the high-level API for theme switching: it updates the theme * data in the ZIP, then patches all resolved colours in the provided * `PptxData` so that elements immediately reflect the new colour scheme * without requiring a re-parse. * * @param data - The current parsed presentation data (mutated in-place for * convenience, but a new `PptxData` object is also returned). * @param colorScheme - New colour scheme (12 colours). * @param fontScheme - Optional new font scheme. * @param themeName - Optional theme display name. * @returns The updated PptxData with re-resolved colours. * * @example * ```ts * import { THEME_PRESETS } from "pptx-viewer-core"; * * const ion = THEME_PRESETS.find(p => p.id === "ion")!; * const newData = await handler.switchTheme( * data, * ion.colorScheme, * ion.fontScheme, * ion.name, * ); * // => PptxData with all colours updated to the Ion theme * ``` */ switchTheme(data: PptxData, colorScheme: PptxThemeColorScheme, fontScheme?: PptxThemeFontScheme, themeName?: string): Promise; /** * Apply a built-in theme preset to the presentation. * * Convenience wrapper around {@link switchTheme} that accepts a * {@link PptxThemePreset} directly. * * @param data - The current parsed presentation data. * @param preset - One of the built-in presets from {@link THEME_PRESETS}. * @returns The updated PptxData. * * @example * ```ts * import { THEME_PRESETS } from "pptx-viewer-core"; * * const preset = THEME_PRESETS.find(p => p.id === "facet")!; * const newData = await handler.switchThemePreset(data, preset); * ``` */ switchThemePreset(data: PptxData, preset: PptxThemePreset): Promise; /** * Parse a PPTX file from an `ArrayBuffer` and return structured data. * * If the file is encrypted and a `password` is provided in `options`, * the file will be decrypted before parsing. If no password is provided * for an encrypted file, throws {@link EncryptedFileError}. * * @param data - Raw bytes of the `.pptx` file (may be encrypted OLE2). * @param options - Optional load-time settings, including `password`. * @returns Parsed {@link PptxData} containing slides, theme, layouts, etc. * * @example * ```ts * // Load an unencrypted file: * const pptx = await handler.load(buf.buffer); * * // Load a password-protected file: * const pptx = await handler.load(buf.buffer, { password: "secret" }); * console.log(`${pptx.slides.length} slides loaded`); * ``` */ load(data: ArrayBuffer, options?: PptxHandlerLoadOptions): Promise; /** * Extract chart data from a graphic-frame XML node. * * @param slidePath - Internal archive path of the slide (e.g. `ppt/slides/slide1.xml`). * @param graphicFrame - Parsed XML object for the `` node. * @returns Chart data, or `undefined` if the frame is not a chart. */ getChartDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; /** * Extract SmartArt data from a graphic-frame XML node. * * @param slidePath - Internal archive path of the slide. * @param graphicFrame - Parsed XML object for the `` node. * @returns SmartArt data, or `undefined` if the frame is not SmartArt. */ getSmartArtDataForGraphicFrame(slidePath: string, graphicFrame: XmlObject | undefined): Promise; /** * Get the base64-encoded data URL for an embedded image. * * @param imagePath - Archive-relative path (e.g. `ppt/media/image1.png`). * @returns A `data:image/...;base64,...` string, or `undefined` if not found. */ getImageData(imagePath: string): Promise; /** * Extract a media file from the PPTX archive as an ArrayBuffer. * Avoids the 33% base64 overhead of getImageData — prefer this for * audio/video media that will be played via Blob URLs. */ getMediaArrayBuffer(mediaPath: string): Promise; /** * Serialise current slides back into a PPTX byte array. * * @param slides - The (possibly mutated) slide array. * @param options - Optional save-time settings (e.g. thumbnail generation). * @returns `Uint8Array` of the complete `.pptx` file. * * @example * ```ts * const bytes = await handler.save(data.slides); * await fs.writeFile("output.pptx", Buffer.from(bytes)); * // => Uint8Array written to disk as a valid .pptx file * ``` */ save(slides: PptxSlide[], options?: PptxHandlerSaveOptions): Promise; /** * Serialise slides and then encrypt the output with a password. * * This is a convenience method that calls {@link save} followed by * {@link encryptPptx}. The result is an OLE2 container suitable for * opening in Microsoft PowerPoint with a password prompt. * * @param slides - The (possibly mutated) slide array. * @param password - The password to encrypt with. * @param options - Optional save-time and encryption settings. * @returns `Uint8Array` of the encrypted OLE2 file. * * @example * ```ts * const bytes = await handler.saveEncrypted(data.slides, "secret"); * await fs.writeFile("protected.pptx", Buffer.from(bytes)); * // => Encrypted OLE2 file requiring password to open * ``` */ saveEncrypted(slides: PptxSlide[], password: string, options?: PptxHandlerSaveOptions & { encryption?: EncryptionOptions; }): Promise; /** * Get the slide layouts available for a specific slide. * * Returns layouts belonging to the same slide master as the given slide. * This is useful for building a layout picker UI scoped to the current * slide's master. * * @param slideIndex - Zero-based slide index. * @param slides - Current slides array. * @returns Array of {@link PptxLayoutOption} entries for the slide's master. * * @example * ```ts * const layouts = await handler.getAvailableLayoutsForSlide(0, data.slides); * console.log(layouts.map(l => l.name)); * // => ["Title Slide", "Title and Content", "Blank", ...] * ``` */ getAvailableLayoutsForSlide(slideIndex: number, slides: PptxSlide[]): Promise; /** * Resolve the editable template (master + layout) elements a slide * inherits, each carrying a `master-` / `layout-` prefixed id. * * This is the foundation for an "edit template/master" feature. The * returned elements are the decorative master/layout shapes the loader * already merges behind slide-authored content (master shapes behind, * layout shapes on top); placeholders are excluded. The same elements are * shared by every slide inheriting the layout/master, so editing one and * saving updates the shared part. * * To persist an edit, keep the mutated template element inside the * `slide.elements` array passed to {@link save}; the save writer reads * template elements from there and writes their shape XML back into the * owning layout/master `p:spTree`. * * @param slideId - The slide's archive path (the `PptxSlide.id`). * @returns Master + layout elements with prefixed ids (may be empty). * * @example * ```ts * const templateEls = await handler.getTemplateElementsForSlide(slide.id); * const logo = templateEls.find((e) => e.id.startsWith("master-")); * if (logo) { * logo.x += 10; * slide.elements = [...slide.elements, logo]; * await handler.save(data.slides); * } * ``` */ getTemplateElementsForSlide(slideId: string): Promise; /** * Apply a different layout to an existing slide. * * Updates the slide's relationship to point to the new layout and * refreshes layout-derived properties (background, layout name). * The slide's own content elements are preserved. * * @param slideIndex - Zero-based slide index. * @param layoutPath - Archive path of the target layout * (e.g. `ppt/slideLayouts/slideLayout2.xml`). * @param slides - Current slides array (the slide at `slideIndex` * is replaced in-place). * @returns The updated {@link PptxSlide} with new layout metadata. * * @example * ```ts * const updated = await handler.applyLayoutToSlide( * 0, * "ppt/slideLayouts/slideLayout3.xml", * data.slides, * ); * console.log(updated.layoutName); * // => "Two Content" * ``` */ applyLayoutToSlide(slideIndex: number, layoutPath: string, slides: PptxSlide[]): Promise; /** * Scan the loaded PPTX archive for all theme parts (`ppt/theme/theme*.xml`) * and return their paths and display names. */ getAvailableThemes(): Promise>; /** * Export selected slides to a vector or raster format, keyed by slide index. * * **This does not produce PPTX files.** The previous version of this comment * said each entry was "a standalone PPTX with only that slide", named the * option `slideIndexes` (the real field is `slideIndices`), and wrote the * bytes to `slide_N.pptx`. None of that was ever true: the runtime has * always taken a `format` of `svg` / `png` / `pdf`. Per-slide PPTX * extraction is a different operation and is not implemented here. * * Only `svg` works without a host-supplied backend, and it works fully: * the headless {@link SvgExporter} renders it with no DOM. `png` and `pdf` * THROW, because this package carries no rasteriser; use a viewer binding's * browser export pipeline, or override `exportSlides` on the runtime with * your own backend. * * @param slides - Full slide array. * @param options - Export options (`format`, `slideIndices`, `width`, ...). * @returns A `Map` of exported files. Hidden slides * are omitted unless `options.includeHidden` is set, so the map can be * smaller than `options.slideIndices`. * @throws {Error} when `options.format` is `png` or `pdf`. * * @example * ```ts * const exports = await handler.exportSlides(data.slides, { * format: 'svg', * slideIndices: [0, 2], * }); * for (const [idx, bytes] of exports) { * await fs.writeFile(`slide_${idx}.svg`, Buffer.from(bytes)); * } * // => Map: one SVG document per exported slide * ``` */ exportSlides(slides: PptxSlide[], options: PptxExportOptions): Promise>; } //#endregion //#region src/core/PptxHandler.d.ts /** * Public facade for the PPTX editor handler. * * The implementation lives in `PptxHandlerCore` so this surface can stay small, * stable, and easy to replace with alternate implementations in the future. */ declare class PptxHandler extends PptxHandlerCore { /** * Create a new blank PPTX presentation from scratch. * * This is a convenience static method that delegates to * {@link PresentationBuilder.create}. The returned handler is fully * initialized and ready for editing, adding slides, and saving. * * @param options - Optional slide dimensions, theme, and metadata. * @returns Handler, parsed data, and a slide builder factory. * * @example * ```ts * const { handler, data, createSlide } = await PptxHandler.createBlank({ * title: "My Deck", * theme: { colors: { accent1: "#FF6B6B" } }, * }); * * data.slides.push( * createSlide("Blank") * .addText("Hello", { fontSize: 36 }) * .build() * ); * * const bytes = await handler.save(data.slides); * ``` */ static createBlank(options?: PresentationOptions): Promise; /** * Create a new PPTX presentation from scratch. * * Alias for {@link createBlank}. Generates a valid minimal OpenXML * package and returns a fully initialized handler ready for editing, * adding slides, and saving. * * @param options - Optional slide dimensions, theme, metadata, * and initial slide count. * @returns Handler, parsed data, and a slide builder factory. * * @example * ```ts * const { handler, data, createSlide } = await PptxHandler.create({ * title: "Q4 Report", * initialSlideCount: 3, * theme: { colors: { accent1: "#FF6B6B" } }, * }); * * // The presentation already has 3 blank slides * console.log(data.slides.length); // => 3 * * // Add more slides with content * data.slides.push( * createSlide("Blank") * .addText("Hello", { fontSize: 36 }) * .build() * ); * * const bytes = await handler.save(data.slides); * ``` */ static create(options?: PresentationOptions): Promise; /** * Parse a presentation from an `ArrayBuffer`, accepting both `.pptx` * archives and portable `pptx-viewer-json` documents. * * The buffer is sniffed first: JSON documents (leading `{` plus the * `"pptx-viewer-json"` format marker) are routed to {@link loadFromJson}; * everything else goes through the regular ZIP/OLE2 pipeline. */ load(data: ArrayBuffer, options?: PptxHandlerLoadOptions): Promise; /** * Load a presentation from `pptx-viewer-json` text. * * A minimal blank package is generated and loaded first so that the * handler keeps a valid in-memory archive (editing and {@link save} keep * working), then the imported model is overlaid on top: imported * presentation fields win, and the slide array is replaced wholesale. */ loadFromJson(text: string, options?: PptxHandlerLoadOptions): Promise; } //#endregion //#region src/converter/SvgExporter.d.ts /** * Options controlling SVG export behaviour. */ interface SvgExportOptions { /** Include hidden slides when exporting all. Default `false`. */ includeHidden?: boolean; /** Slide indices to export (0-based). If omitted, all slides are exported. */ slideIndices?: number[]; /** Default font family when the element does not specify one. */ defaultFontFamily?: string; /** Default font size in points when the element does not specify one. */ defaultFontSize?: number; } //#region src/theme/types.d.ts /** * Theme configuration types for the PowerPoint viewer. * * All color values accept any valid CSS color string: * hex (`#6366f1`), rgb (`rgb(99 102 241)`), hsl (`hsl(239 84% 67%)`), * oklch (`oklch(0.585 0.233 277)`), named colors, etc. * * Framework-agnostic — shared by the React, Vue, and Angular bindings. */ /** * Semantic color tokens for the viewer UI. * * These map to CSS custom properties (`--pptx-`) and drive all * UI component colors. The naming follows the shadcn/ui convention so * that Tailwind + shadcn users get a familiar experience. */ interface ViewerThemeColors { /** Page / root background */ background: string; /** Default text color */ foreground: string; /** Card / panel surface */ card: string; /** Text on card surfaces */ cardForeground: string; /** Popover / dropdown surface */ popover: string; /** Text inside popovers */ popoverForeground: string; /** Primary action color (buttons, active indicators) */ primary: string; /** Text on primary-colored backgrounds */ primaryForeground: string; /** Secondary / subdued action color */ secondary: string; /** Text on secondary backgrounds */ secondaryForeground: string; /** Muted / disabled surface */ muted: string; /** Text on muted surfaces (also used for secondary text) */ mutedForeground: string; /** Accent / hover-highlight surface */ accent: string; /** Text on accent surfaces */ accentForeground: string; /** Destructive / danger action color */ destructive: string; /** Text on destructive backgrounds */ destructiveForeground: string; /** Default border color */ border: string; /** Input field border color */ input: string; /** Focus ring color */ ring: string; } /** * Full viewer theme configuration. * * Every property is optional — unset values fall back to the built-in * dark theme defaults. */ interface ViewerTheme { /** Semantic UI colors. Each key maps to a `--pptx-` CSS custom property. */ colors?: Partial; /** Base border-radius value (e.g. `"0.5rem"`, `"8px"`). */ radius?: string; /** * Escape hatch: arbitrary CSS custom properties to set on the viewer * root element. Keys should include the `--` prefix. * * @example * ```ts * { "--my-custom-shadow": "0 4px 12px rgba(0,0,0,0.5)" } * ``` */ cssVars?: Record; } //#endregion //#region src/theme/defaults.d.ts /** * Default dark-theme color values. * * These correspond to the built-in dark UI of the PowerPoint viewer and * use Tailwind's gray palette as the neutral scale with indigo as the * primary accent. */ declare const defaultThemeColors: ViewerThemeColors; /** Default border-radius. */ declare const defaultRadius = "0.5rem"; //#endregion //#region src/theme/css-vars.d.ts /** * Convert a `ViewerTheme` into a flat `Record` of CSS * custom properties (including the `--` prefix) ready to be spread onto * a `style` attribute. * * Only properties that differ from the built-in defaults are emitted when * `omitDefaults` is true (the default). */ declare function themeToCssVars(theme: ViewerTheme | undefined, omitDefaults?: boolean): Record; /** * Build the complete set of CSS custom properties with all defaults. * Useful for generating a full fallback stylesheet. */ declare function defaultCssVars(): Record; //#endregion //#region src/theme/presets.d.ts /** * Built-in "vermilion" theme presets. * * These mirror the pptx-viewer brand used on the documentation site: * a warm paper canvas in light mode, a dimmed presenter room in dark * mode, and the vermilion accent in both. Pass one to the viewer's * `theme` prop (React/Vue) or `provideViewerTheme` (Angular), or spread * the color objects to derive your own variant. */ /** Light "paper" palette: a projection screen in a bright room. */ declare const vermilionLightColors: ViewerThemeColors; /** Dark "presenter" palette: the presenter room with the lights down. */ declare const vermilionDarkColors: ViewerThemeColors; /** Shared border-radius for the vermilion presets (slightly sharper than the default). */ declare const vermilionRadius = "0.375rem"; /** Light vermilion theme, ready for the viewer's `theme` prop. */ declare const vermilionLightTheme: ViewerTheme; /** Dark vermilion theme, ready for the viewer's `theme` prop. */ declare const vermilionDarkTheme: ViewerTheme; //#endregion //#region src/theme/theme-catalog.d.ts /** One selectable entry in the viewer chrome's built-in theme picker (File > Options > Appearance). */ interface ThemeCatalogEntry { /** Stable identifier persisted to storage and passed to `onThemeChange`. */ key: string; /** `pptx.*` translation key for the entry's display label. */ labelKey: string; /** The theme to apply, or `undefined` to reset to the built-in default. */ theme: ViewerTheme | undefined; } //#region src/types.d.ts /** Canvas dimensions in pixels. */ interface CanvasSize { width: number; height: number; } /** Viewer interaction mode: read-only, edit, presentation, or master-view. */ type ViewerMode = 'preview' | 'edit' | 'present' | 'master'; /** * Framework-agnostic imperative API contract for the PowerPoint viewer. * * Each binding (React `forwardRef` handle, Vue `defineExpose`, Angular public * methods) implements this interface so consumers get a consistent progressive * API regardless of framework. */ interface PowerPointViewerAPI { /** Serialise the current presentation to `.pptx` bytes. */ getContent: () => Promise; /** Navigate to a specific slide by zero-based index. */ goTo: (slideIndex: number) => void; /** Navigate to the previous slide. */ goPrev: () => void; /** Navigate to the next slide. */ goNext: () => void; /** Undo the last editing action. No-op when nothing to undo. */ undo: () => void; /** Redo the last undone action. No-op when nothing to redo. */ redo: () => void; /** Whether an undo action is available. */ canUndo: () => boolean; /** Whether a redo action is available. */ canRedo: () => boolean; /** Get the current zoom level (1 = 100%). */ getZoom: () => number; /** Set the zoom level (clamped to min/max bounds). */ setZoom: (level: number) => void; /** Zoom in by one step. */ zoomIn: () => void; /** Zoom out by one step. */ zoomOut: () => void; /** Reset zoom to 100%. */ zoomReset: () => void; /** Get the current viewer mode. */ getMode: () => ViewerMode; /** Switch the viewer mode (e.g. 'edit', 'preview', 'present'). */ setMode: (mode: ViewerMode) => void; /** Get the zero-based active slide index. */ getActiveSlideIndex: () => number; /** Set the active slide by zero-based index (alias of goTo). */ setActiveSlideIndex: (index: number) => void; /** Get the total number of slides. */ getSlideCount: () => number; /** Whether the document has unsaved changes. */ isDirty: () => boolean; /** * Get the full slide array. Returns the actual `PptxSlide[]` from the * internal model with full type information (elements, notes, transitions, * animations, etc.). The returned reference is a snapshot; mutations are * not reflected back unless done via the manipulation methods. */ getSlides: () => readonly PptxSlide[]; /** Get a single slide by zero-based index, or undefined if out of range. */ getSlide: (index: number) => PptxSlide | undefined; /** Get the currently active slide. */ getActiveSlide: () => PptxSlide | undefined; /** Add a blank slide after the given index (or at end if omitted). */ addSlide: (afterIndex?: number) => void; /** Delete slides at the given zero-based indexes. At least one slide is kept. */ deleteSlides: (indexes: number[]) => void; /** Duplicate slides at the given zero-based indexes. */ duplicateSlides: (indexes: number[]) => void; /** Move a slide from one position to another. */ moveSlide: (fromIndex: number, toIndex: number) => void; /** Toggle the hidden flag on slides at the given indexes. */ toggleHideSlides: (indexes: number[]) => void; /** * Get the elements on a slide. Defaults to the active slide when * `slideIndex` is omitted. Returns the full `PptxElement[]` with * all type-specific properties intact. */ getElements: (slideIndex?: number) => readonly PptxElement[]; /** Get a single element by ID from the active slide (or a specified slide). */ getElementById: (elementId: string, slideIndex?: number) => PptxElement | undefined; /** * Update one or more properties of an element by ID on the active slide. * Accepts a `Partial` patch (e.g. `{ x: 100, width: 300 }`). */ updateElement: (elementId: string, updates: Partial) => void; /** Delete elements by their IDs from the active slide. */ deleteElements: (elementIds: string[]) => void; /** * Duplicate an element on the active slide. * Returns the new element's ID, or undefined if the source was not found. */ duplicateElement: (elementId: string) => string | undefined; /** Get the IDs of currently selected elements. */ getSelectedElementIds: () => string[]; /** Programmatically select elements by their IDs. */ selectElements: (ids: string[]) => void; /** Clear the current selection. */ clearSelection: () => void; } /** Collaboration role within a session. */ type CollaborationRole = 'owner' | 'collaborator' | 'viewer'; /** * Collaboration transport. * * - `'websocket'` (default): y-websocket against `serverUrl`. * - `'webrtc'`: y-webrtc peer-to-peer; needs no document server. Peers meet * through the `signaling` servers (WebRTC signaling only, no document data) * and same-browser tabs additionally sync via BroadcastChannel even without * any signaling server, which makes this mode usable from static hosting. */ type CollaborationTransport = 'websocket' | 'webrtc'; /** How the local user entered a collaboration session. */ type CollaborationSessionIntent = 'create' | 'join'; /** * Real-time collaboration configuration. * * The same shape is accepted by every framework binding. */ interface CollaborationConfig { /** Unique identifier for the collaboration room (alphanumeric, hyphens, underscores). */ roomId: string; /** * WebSocket server URL for the Yjs provider (e.g. "wss://collab.example.com"). * Ignored (may be empty) when `transport` is `'webrtc'`. */ serverUrl: string; /** Transport to use. Defaults to `'websocket'`. */ transport?: CollaborationTransport; /** * WebRTC signaling server URLs (only used when `transport` is `'webrtc'`). * Defaults to y-webrtc's built-in public signaling list. Same-browser tabs * sync via BroadcastChannel regardless of signaling availability. */ signaling?: string[]; /** Display name for the local user. */ userName: string; /** Avatar URL for the local user (optional). */ userAvatar?: string; /** Hex colour for the local user's cursor/presence indicator. */ userColor?: string; /** Optional authentication token sent with the WebSocket handshake. */ authToken?: string; /** Role in the session; defaults to `'collaborator'`. */ role?: CollaborationRole; /** * Whether this client created the room or joined an existing room. Providers * do not use this value, but hosts can use it to avoid publishing local file * bytes when handling a join request. Omitted values retain the legacy * create-session behaviour. */ sessionIntent?: CollaborationSessionIntent; /** * Elected-writer write-back callback (Area 3 of the C3 hardening plan). * * When the local user has `role: 'owner'`, the binding debounces changes and * serializes the current Y.Doc state to a PPTX byte array, then calls this * callback so the host can persist the snapshot. Only one writer (the owner) * does this; other collaborators never trigger write-back, eliminating the * last-save-wins problem. */ onWriteBack?: (bytes: Uint8Array) => void; /** * Debounce delay (ms) between the last Y.Doc change and the write-back * invocation. Defaults to 5000 ms. Set to 0 to write back on every change * (not recommended for large documents). */ writeBackDebounceMs?: number; } /** * A font supplied by the host application. The package never ships fonts: * applications provide a licensed URL, data URL, or blob URL for their users. */ interface ViewerFontSource { family: string; src: string; format?: 'truetype' | 'opentype' | 'woff' | 'woff2'; weight?: string | number; style?: 'normal' | 'italic'; } //#endregion //#region src/render/presentation-file-kinds.d.ts /** * presentation-file-kinds: the one place that answers "can the viewer open * this file?" and "what should the saved copy be called?". * * ## Why this is a shared decision and not five allow-lists * * The loader reads more formats than any single UI advertises. Legacy binary * `.ppt` (PowerPoint 97-2003) is the sharp example: `PptxHandler.load()` has * detected the OLE compound-file container and converted the binary deck * through the regular pptx pipeline for some time, but the product kept saying * it was unsupported, and a picker that filters the extension out makes a * working loader unreachable in practice. Whenever the loader learns a format, * exactly one list has to change. * * ## Read many, write one * * Input is a superset of output. We READ `.pptx`, `.ppsx`, `.pptm`, `.potx`, * legacy binary `.ppt` and portable `pptx-viewer-json`; we WRITE only the * OpenXML family. That asymmetry is deliberate (PowerPoint itself does the * same: open a 97-2003 deck and Save As offers `.pptx`), and it is why * {@link savedPresentationFileName} always REPLACES the source extension * rather than keeping it. A deck opened as `report.ppt` and saved as * `report.ppt` would be a file whose bytes and whose name disagree, which is * the kind of thing PowerPoint refuses to open. * * This module deliberately imports nothing, so any layer (render, export, a * binding, a host app) can depend on it without risking an import cycle. * * @module render/presentation-file-kinds */ /** * Extensions the built-in file picker offers, in the order it offers them. * * `.ppt` is in the list because the loader genuinely handles it, not as a * courtesy: see `packages/core/src/core/ppt/` and the `ppt-import` integration * suite, which asserts a `.ppt` loads to the same model as the `.pptx` it was * exported from. */ declare const PRESENTATION_OPEN_EXTENSIONS: readonly ['.pptx', '.ppsx', '.pptm', '.potx', '.ppt', '.json']; /** Comma-separated `accept` attribute for a presentation file input. */ declare const PPTX_OPEN_ACCEPT: string; /** * True when a picked / dropped file's name looks like something the loader can * open. Use this instead of a hand-rolled `endsWith` chain: a drop handler that * disagrees with the picker's `accept` list is a format that is supported by * mouse but not by drag, which is how `.ppt` stayed invisible. * * Extension-only, by design. The real answer comes from the container sniff in * `PptxHandler.load()`; this is only the cheap pre-filter a drop target needs * before it hands bytes to the loader. */ declare function isSupportedPresentationFile(name: string | null | undefined): boolean; /** True for the binary PowerPoint 97-2003 family, which we read but never write. */ declare function isLegacyBinaryPresentation(name: string | null | undefined): boolean; /** The formats the save path can produce. Binary `.ppt` is deliberately absent. */ type SavedPresentationFormat = 'pptx' | 'ppsx' | 'pptm'; /** * The stem of a presentation file name: directories and any loadable extension * removed. `C:\decks\report.ppt` becomes `report`; a name with no recognised * extension is kept whole, so `Untitled Presentation` survives intact rather * than losing everything after its last dot. */ declare function presentationBaseName(sourceName: string | null | undefined, fallback?: string): string; /** * The name a saved copy should be offered under: the source stem plus the * extension of the format actually being written. * * This is what turns `report.ppt` into `report.pptx` on Save As. Output is * always an OpenXML package, so keeping the source extension would mislabel * the bytes. */ declare function savedPresentationFileName(sourceName: string | null | undefined, format?: SavedPresentationFormat): string; //#endregion //#region src/render/session-restore.d.ts /** * session-restore: keep the deck a host app has open across a page refresh. * * A host (the demo apps, or any embedder) owns the bytes it hands to the * viewer, so a plain reload drops them and the user lands back on the file * dropzone with their presentation gone. This store remembers the open deck in * IndexedDB and hands it back on the next load. * * Scope is deliberately per-tab: the record is keyed by an id kept in * `sessionStorage`, which survives a reload but NOT a new tab. Refreshing * restores the deck this tab had open, while a second tab opened on the same * origin still starts on the landing page, and two tabs holding different decks * never steal each other's content. * * `restoreSessionDeck` additionally prefers a NEWER autosave snapshot for the * same file (see `./autosave-store`), so a refresh mid-edit comes back with the * edited deck rather than the pristine bytes that were first opened. * * Every operation is best-effort: a blocked IndexedDB, a partitioned * `sessionStorage`, or an exhausted quota degrades to "no restore", never to a * thrown error in the host. */ /** A deck remembered for the current tab. */ interface SessionDeck { /** File name the deck was opened under, used as the autosave key. */ fileName: string; /** The presentation bytes. */ data: Uint8Array; /** When these bytes were remembered (epoch ms). */ timestamp: number; } /** * This tab's session id, or `null` when `sessionStorage` is unavailable (a * sandboxed iframe, or a browser with storage disabled). * * @param create Mint and persist an id when the tab does not have one yet. * Reads pass `false` so a fresh tab never claims another tab's record. */ declare function getSessionTabId(create?: boolean): string | null; /** * Remember `data` as the deck this tab has open, so the next load can restore * it. Resolves `false` when the browser refused to store it; callers treat that * as "no restore available later", never as an error. */ declare function rememberSessionDeck(fileName: string, data: Uint8Array): Promise; /** The deck remembered for this tab, or `null` when there is nothing to restore. */ declare function loadSessionDeck(): Promise; /** Forget this tab's deck (the host closed it, or handed the tab to another flow). */ declare function forgetSessionDeck(): Promise; /** * The deck to reopen on load: this tab's remembered bytes, upgraded to a newer * autosave snapshot of the same file when the viewer wrote one after they were * remembered. Without that upgrade a refresh mid-edit would silently roll the * presentation back to the state it was opened in. */ declare function restoreSessionDeck(): Promise; declare function parsePresentationSessionId(hash: string): string | null; declare function loadPresentationDeck(sessionId: string): Promise; interface AutosaveRecord { key: string; data: Uint8Array; timestamp: number; size: number; } /** * Retrieve a single autosave snapshot by file path. * Returns undefined when no snapshot exists. */ declare function getAutosaveSnapshot(filePath: string): Promise; /** * List all autosave snapshots (without the heavy `data` blob). * Useful for showing a recovery picker on app start. */ declare function listAutosaveSnapshots(): Promise>; /** * Delete an autosave snapshot by file path. */ declare function deleteAutosaveSnapshot(filePath: string): Promise; /** * Optional hook point for hosts that want to wire a real sign-in flow into * File > Account. Disabled by default: the Account page renders nothing * extra unless a host explicitly opts in by passing `enabled: true`. * See docs/guide for wiring instructions. */ interface AccountAuthConfig { enabled: boolean; onSignIn: () => void; signedInUser?: { name: string; email?: string; avatarUrl?: string; }; } //#endregion //#region src/render/toolbar-actions.d.ts /** * Toolbar action / ribbon-tab visibility: a single, framework-agnostic * catalogue of every top-level toolbar button and ribbon tab a host app can * independently hide. Each binding exposes a `hiddenActions?: ToolbarActionId[]` * prop, threads it down to the relevant render sites, and gates them with * `isActionHidden`. Default (`undefined` / `[]`) hides nothing, matching * today's always-visible behaviour. * * `TOOLBAR_TABS` is also the canonical ribbon-tab list/order, replacing the * copy hand-duplicated in each binding (React's `TOOLBAR_SECTIONS`, Vue's * `ribbon-constants.ts`, Angular's `RIBBON_TABS`, etc.) so the tab set can't * drift between bindings. Per-tab icons stay in each binding (icon libraries * differ per framework); only id + i18n key + order are shared here. */ /** * A single toolbar button/control that can be hidden independently of the * ribbon tab it may also appear inside. `zoom` and `navigation` each cover a * whole control cluster (zoom in/out/fit, prev/next) rather than each button * in it, matching how hosts actually want to hide/keep them as a unit. */ type ToolbarButtonId = 'share' | 'broadcast' | 'export' | 'undo' | 'redo' | 'record' | 'notes' | 'fullscreen' | 'zoom' | 'navigation'; /** A top-level ribbon tab. `record` intentionally shares its id with the quick-access Record button above: both surface the same recording feature, so hiding one hides the other. */ type ToolbarTabId = 'file' | 'home' | 'insert' | 'draw' | 'design' | 'transitions' | 'animations' | 'slideShow' | 'record' | 'review' | 'view' | 'help'; type ToolbarActionId = ToolbarButtonId | ToolbarTabId; //#endregion //#region src/export/handout-layout.d.ts /** * Pure handout layout calculations, shared by every binding's print path. * * Handles distributing slides across pages, computing grid dimensions, and * positioning cells within A4 page space. No DOM/framework dependency: callers * render the resulting rectangles however their view layer prefers. */ /** Supported slides-per-page values. */ type HandoutSlidesPerPage = 1 | 2 | 3 | 4 | 6 | 9; //#endregion //#region src/export/print-document.d.ts /** What to print. */ type PrintWhat = 'slides' | 'handouts' | 'notes' | 'outline'; /** Page orientation for the printed output. */ type PrintOrientation = 'portrait' | 'landscape'; /** Colour mode for the printed output. */ type PrintColorMode = 'color' | 'grayscale' | 'blackAndWhite'; /** Slide range mode. */ type PrintSlideRange = 'all' | 'current' | 'custom'; /** Resolved print settings emitted on confirm. */ interface PrintSettings { printWhat: PrintWhat; orientation: PrintOrientation; colorMode: PrintColorMode; frameSlides: boolean; slidesPerPage: HandoutSlidesPerPage; slideRange: PrintSlideRange; customRangeFrom: number; customRangeTo: number; } //#endregion //#region src/ai/change-animator.d.ts /** Host-tunable options for how AI edits are animated on the canvas. */ interface AiChangeAnimationConfig { /** Master switch. Default true. */ enabled?: boolean; /** How long the motion + glow plays, in ms. Default 900. */ durationMs?: number; /** Draw the pulsing glow highlight on changed elements. Default true. */ glow?: boolean; /** Glide old->new bounds and cross-fade colours. Default true. */ tween?: boolean; /** Accent colour (any CSS colour) for the glow/ghosts. Default a blue. */ color?: string; } //#endregion //#region src/ai/config.d.ts /** The UI message shape exchanged with the assistant. Alias of the SDK type. */ type PptxAiUIMessage = UIMessage; /** * Canonical name of every tool the assistant can call. Document tools mirror the * `pptx-viewer-mcp` server exactly (they ARE the same functions, run against the * live deck); the viewer-only tools (navigation, deck outline, element/notes * readers, table merge) have no MCP counterpart. */ type PptxAiToolName = 'get_deck_overview' | 'get_slide' | 'get_element' | 'get_speaker_notes' | 'find_text' | 'get_theme' | 'go_to_slide' | 'select_elements' | 'merge_tables' | 'get_metadata' | 'get_layouts' | 'find_placeholders' | 'get_presentation_properties' | 'run_accessibility_check' | 'convert_to_markdown' | 'export_to_json' | 'add_element' | 'update_element' | 'delete_elements' | 'arrange_elements' | 'clone_element' | 'set_element_animation' | 'group_elements' | 'ungroup_elements' | 'batch_update_elements' | 'update_element_style' | 'replace_geometry' | 'set_element_lock' | 'manage_hyperlinks' | 'replace_text' | 'manage_comments' | 'update_table_cells' | 'manage_table_structure' | 'create_chart' | 'update_chart' | 'add_chart_series' | 'remove_chart_series' | 'update_chart_series_data' | 'manage_smart_art' | 'apply_template' | 'add_slide' | 'duplicate_slide' | 'delete_slides' | 'reorder_slides' | 'update_slide_properties' | 'set_slide_transition' | 'apply_theme_preset' | 'update_theme_colors' | 'update_theme_fonts' | 'set_canvas_size' | 'update_metadata' | 'manage_sections' | 'update_presentation_properties' | 'import_from_json' | 'apply_layout'; type Resolvable = T | (() => T | Promise); /** How the assistant reaches a language model. */ type PptxAiConnection = /** * Post messages to a host backend route (recommended for production so the * provider API key stays server-side). Maps to `DefaultChatTransport`. */ { kind: 'endpoint'; api: string; headers?: Resolvable>; body?: Resolvable>; credentials?: RequestCredentials; fetch?: typeof globalThis.fetch; } | /** * Run a language model in-process in the browser (bring-your-own key / * local model). Maps to a `ToolLoopAgent` behind a `DirectChatTransport`. */ { kind: 'model'; model: LanguageModel; system?: string; maxSteps?: number; } | /** Provide a fully-constructed transport (advanced / testing escape hatch). */ { kind: 'transport'; transport: ChatTransport; }; /** How writes proposed by the assistant reach the document. */ type PptxAiWritePolicy = 'stage' | 'approve' | 'auto'; /** Which deck context is fed to the model with each turn. */ type PptxAiContextStrategy = 'outline' | 'current-slide' | 'none'; /** Optional per-session history persistence hooks. */ interface PptxAiHistoryHooks { load?(id: string): Promise; save?(id: string, messages: PptxAiUIMessage[]): Promise; } /** Complete host configuration for an AI chat session. */ interface PptxAiConfig { connection: PptxAiConnection; /** Extra host instructions appended to the base system prompt. */ systemPromptExtras?: string; tools?: { /** Allowlist. When set, only these tools are exposed. */ enabled?: PptxAiToolName[]; /** Denylist, applied after `enabled`. */ disabled?: PptxAiToolName[]; /** Additional host-defined tools merged into the tool set. */ extra?: ToolSet; }; /** Default `'stage'`. */ writePolicy?: PptxAiWritePolicy; /** Default `'outline'`. */ contextStrategy?: PptxAiContextStrategy; history?: PptxAiHistoryHooks; /** * How AI edits are animated on the canvas so the user can watch them land * (glide old->new, fade/scale in-out, glow-pulse). Omit for the defaults; * set `{ enabled: false }` to turn it off. */ changeAnimation?: AiChangeAnimationConfig; onError?(error: Error): void; } //#endregion //#region src/ai/bridge.d.ts /** Lightweight, model-friendly summary of the whole deck. */ interface PptxAiDeckMeta { /** Total number of slides. */ slideCount: number; /** Zero-based index of the currently active slide. */ activeSlideIndex: number; /** Deck title, when known (first slide title / core properties). */ title?: string; /** Slide canvas width in CSS pixels. */ width: number; /** Slide canvas height in CSS pixels. */ height: number; } /** Severity hint for {@link PptxAiBridge.notify}. */ type PptxAiNotifyLevel = 'info' | 'success' | 'warning' | 'error'; /** * A target the user has scoped the assistant to: either a whole slide or a * single element on a slide. Returned by {@link PptxAiBridge.getFocusedTargets} * so the context builder can tell the model exactly what to focus on. */ type PptxAiFocusedTarget = { kind: 'slide'; slideIndex: number; } | { kind: 'element'; slideIndex: number; elementId: string; }; /** * A pure updater over the deck's slides. It receives a deep clone of the * current slides (mutation-safe) and returns the next slides array. The bridge * commits the returned array as ONE history entry. */ type PptxAiSlidesUpdater = (slides: PptxSlide[]) => PptxSlide[]; /** * A pure updater over the whole parsed deck ({@link PptxData}). Mirrors the * `pptx-viewer-mcp` tool model (data in, mutated data out) so presentation-level * MCP tools (metadata, sections, canvas size, presentation properties, layouts) * can be committed as ONE undoable history entry through {@link * PptxAiBridge.applyDeckData}. Optional: bindings that only track slide/theme * state can omit it, in which case those presentation-level tools report that * they are unavailable in this viewer while every slide/theme tool still works. */ type PptxAiDataUpdater = (data: PptxData) => PptxData; /** Field-level updates for a single element, mirroring the MCP update vocab. */ interface PptxAiElementUpdate { x?: number; y?: number; width?: number; height?: number; rotation?: number; opacity?: number; hidden?: boolean; flipHorizontal?: boolean; flipVertical?: boolean; text?: string; fontSize?: number; fontFamily?: string; fontColor?: string; bold?: boolean; italic?: boolean; underline?: boolean; align?: 'left' | 'center' | 'right' | 'justify'; fillColor?: string; strokeColor?: string; strokeWidth?: number; } /** * Implemented by each binding to expose its live editor to the AI core. * * Read methods must be cheap and synchronous. Write methods must route through * the binding's editor-history layer so AI edits are undoable like manual ones. */ interface PptxAiBridge { /** Return a summary of the whole deck. */ getDeckMeta(): PptxAiDeckMeta; /** Return the deck's slides. Callers must not mutate the returned array. */ getSlides(): PptxSlide[]; /** Return the zero-based index of the active slide. */ getActiveSlideIndex(): number; /** Return the resolved presentation theme, when available. */ getTheme(): PptxTheme | undefined; /** Return the underlying core handler, when the binding exposes one. */ getHandler(): PptxHandler | undefined; /** Navigate the viewer to a slide by zero-based index. */ goToSlide(index: number): void; /** Select the given elements on a slide (empty array clears selection). */ selectElements(slideIndex: number, elementIds: string[]): void; /** * Apply a slides updater as a single, atomic, undoable history entry. The * binding is responsible for cloning current slides before calling * `updater` and for installing the result. */ applySlidesUpdate(updater: PptxAiSlidesUpdater, label: string): void; /** Apply field updates to one element as a single history entry. */ updateElement(slideIndex: number, elementId: string, updates: PptxAiElementUpdate): void; /** Apply partial theme updates as a single history entry. */ applyTheme(updates: Partial): void; /** * Return the full parsed {@link PptxData} for the open deck, with the live * (edited) slides and theme overlaid. Enables `pptx-viewer-mcp` tools that * read presentation-level state (metadata, sections, layouts, presentation * properties). Optional: when absent, the AI core synthesises a minimal * PptxData from slides + dimensions, which is enough for every slide/theme * tool but not for presentation-level reads. */ getDeckData?(): PptxData | undefined; /** * Commit a whole-deck {@link PptxData} mutation as one undoable history entry. * Used to apply presentation-level MCP tool results (metadata, sections, * canvas size, presentation properties, layouts). Optional: when absent, * those tools report they are not supported in this viewer; slide/theme tools * are unaffected (they route through {@link PptxAiBridge.applySlidesUpdate} * and {@link PptxAiBridge.applyTheme}). */ applyDeckData?(updater: PptxAiDataUpdater, label: string): void; /** * Return the slides / elements the user has scoped the assistant to, if any. * When present and non-empty, the context builder tells the model to focus on * exactly these targets. Optional so existing bridges satisfy the contract * without change; a bridge that does not implement it behaves as before (no * focus scoping). */ getFocusedTargets?(): PptxAiFocusedTarget[]; /** Surface a transient message in the host UI (toast / status line). */ notify?(message: string, level?: PptxAiNotifyLevel): void; } //#endregion //#region src/i18n/locale-catalog.d.ts /** One selectable entry in the viewer chrome's built-in language picker (File > Options > Language). */ interface LocaleCatalogEntry { /** BCP-47-ish locale code, e.g. `'en'`, `'fr'`. Matches `pptx-viewer-locales`' exports. */ code: string; /** English display name, used before a translation dictionary for the target locale is loaded. */ label: string; /** The locale's own name for itself, e.g. `'Français'` for `fr`. */ nativeLabel: string; } /** * Animated-GIF export capture/encode pipeline. All the pure logic is shared: * `planGifFrames` derives the per-slide frame delays (default duration + * per-slide overrides), `clampGifDimensions` bounds the output size, and * `encodeGif` (median-cut quantisation + LZW GIF89a) produces the bytes with * each frame carrying its own plan delay. This module only owns the * DOM-adjacent glue: rasterising each slide via the injected capture callback * and normalising every frame onto a uniform-size canvas before pixel * extraction. Blob download is left to the caller (`ExportController`). */ /** Options for the animated-GIF export. */ interface ExportGifOptions { /** Duration each slide is shown, in milliseconds. Default 2000. */ slideDurationMs?: number; /** Per-slide duration overrides in milliseconds (index maps to slide index). */ slideTimingsMs?: number[]; /** * Longest allowed output side in pixels; frames are scaled down * proportionally. Default 960 (GIF encoding cost grows with pixel count: * every pixel is matched against a 256-colour palette per frame). */ maxDimension?: number; /** Capture-phase progress callback: `(currentSlide, totalSlides)`. */ onProgress?: ExportProgress; /** Abort the export early; checked before each slide capture. */ signal?: AbortSignal; } /** * Print flow: assemble the shared print document (slides / notes / handouts / * outline) and hand it to a print surface. All the pure logic is shared and * DOMPurify-hardened: `validatePrintSettings` normalises the caller's partial * settings, `computeSlideIndices` / `computeColorFilter` resolve the range and * colour mode, the `build*Html` helpers produce the escaped body markup, and * `buildPrintHtmlDocument` sanitises + assembles the final document. This * module (the Svelte counterpart of Vue's `usePrint`) exports direct slide * pages as vector SVG, rasterises notes/handout thumbnails, and opens the * print surface. * * **Print surface / popup-blocker caveats:** the default opener renders the * document into a hidden same-origin `