PPTX Studio
Packages

render-svg

A slide to an SVG string, in a tab, in a Worker, or in Node.

@pptx-studio/render-svg0.6.6runs inTabWorkerNodeLive demo →npm →

A slide to an SVG string - in a tab, in a Worker, or in Node.

npm install @pptx-studio/render-svg @pptx-studio/opc @pptx-studio/model

What it is

renderSlide takes a sheet from the model and returns SVG: transforms, groups, geometry, fills, strokes, effects, pictures and text, as a string, with no DOM involved. The same node tree is what render-dom mounts live, so the two never drift.

Text is measured through an OffscreenCanvas by default; in Node, pass a measurer or text: false.

30-second example

render.ts
const store = PartStore.open(bytes);
const doc = loadDocument(store);
const slide = doc.slides[0];
if (slide === undefined) throw new Error('no slides');

const svg: string = renderSlide(slide, doc.slideSize, {
  width: 1280,
  media: mediaFromStore(store), // pictures as data: URIs
  text: { defaultTextStyle: doc.defaultTextStyle }, // or `text: false` for geometry only
});

Key concepts

A string, and only a string

No document, no window. That is what lets the same renderer run in a Worker, in the CLI, and in the release smoke test.

Flip before rotate

A shape mirrored after being rotated is saved with a different angle from one rotated after being mirrored; only one order of composition makes both read back correctly, and it was settled by making PowerPoint write the file.

Group child space

A group maps its children through its own child offset and extent. placeChild composes the two frames; nested rotated groups were the test.

@showMasterSp hides everything above

A slide that turns master shapes off hides the layout's too. layoutSlide gives the shapes drawn; layoutSheet gives the sheet's own.

Real <text>, never foreignObject

Every line is placed as SVG text at a computed baseline, which is what a thumbnail, a PDF and an export all need.

API reference

92 exports of @pptx-studio/render-svg 0.6.6, read from the package this site installed. Click a name for its declaration.

index.ts

  • MIN_PX_PER_PTre-export
    re-exported from @pptx-studio/paint

src/errors.ts

  • RenderErrorCodetypeEvery failure in this package is a RenderError with a machine-readable code.
    type RenderErrorCode =
    /** `a:prstGeom/@prst` is not one of the 187 `ST_ShapeType` values. */
    'RENDER_UNKNOWN_PRESET' |
    /** A group tree nests deeper than `MAX_GROUP_DEPTH`. Hostile input. */
    'RENDER_GROUP_DEPTH' |
    /** The host handed `mount` something that is not an element. */
    'RENDER_NO_HOST' |
    /** An `a:bodyPr` attribute that is not a length or an angle. */
    'RENDER_TEXT_FRAME' |
    /** A text direction this package lays out but does not yet draw. */
    'RENDER_TEXT_UNSUPPORTED' |
    /** An image whose header is not a PNG, JPEG, GIF or BMP. */
    'RENDER_IMAGE_FORMAT' |
    /** A `devicePixelRatio` that is not a positive number, or one given with no width. */
    'RENDER_DEVICE_PIXEL_RATIO';
  • RENDER_ERROR_CODESconst
    const RENDER_ERROR_CODES: readonly RenderErrorCode[];
  • RenderErrorclass
    class RenderError extends Error {
      readonly code: RenderErrorCode;
      /** Whatever names the thing that went wrong: a shape name, a preset, a host. */
      readonly subject: string | undefined;
      constructor(code: RenderErrorCode, message: string, subject?: string);
    }
  • isRenderErrorfunction
    function isRenderError(value: unknown): value is RenderError;

src/transform.ts

  • EMU_PER_POINTconst914400 EMU to the inch, 72 points to the inch.
    const EMU_PER_POINT = 12700;
  • ANGLE_UNITS_PER_DEGREEconstSixtieths of a degree: the unit of a:xfrm/@rot.
    const ANGLE_UNITS_PER_DEGREE = 60000;
  • BoxtypeAn axis-aligned rectangle in EMU.
    interface Box {
      readonly x: number;
      readonly y: number;
      readonly cx: number;
      readonly cy: number;
    }
  • FrametypeA placed shape's rectangle and orientation, in slide EMU.
    interface Frame extends Box {
      /** Degrees, clockwise on screen. Normalised to `[0, 360)`. */
      readonly rot: number;
      readonly flipH: boolean;
      readonly flipV: boolean;
    }
  • IDENTITY_FRAMEconst
    const IDENTITY_FRAME: Frame;
  • frameOffunctionAn a:xfrm as a frame, with @rot converted out of sixtieths of a degree.
    function frameOf(xfrm: Xfrm): Frame;
  • ChildSpacetypeThe affine part of a group's child map: a scale and an origin, per axis.
    interface ChildSpace {
      readonly sx: number;
      readonly sy: number;
      readonly ox: number;
      readonly oy: number;
    }
  • UNIT_CHILD_SPACEconst
    const UNIT_CHILD_SPACE: ChildSpace;
  • childSpacefunctionThe child map of a group, given the rectangle the group actually occupies.
    function childSpace(frame: Box, xfrm: Xfrm): ChildSpace;
  • swapsExtentsfunctionWhether a group's two scale factors change places for a child at this angle.
    function swapsExtents(degrees: number): boolean;
  • composeTurnfunctionCompose two turns.
    function composeTurn(outer: {
      rot: number;
      flipH: boolean;
      flipV: boolean;
    }, inner: {
      rot: number;
      flipH: boolean;
      flipV: boolean;
    }): {
      rot: number;
      flipH: boolean;
      flipV: boolean;
    };
  • turnVectorfunctionA point turned about the origin: mirrored first, then rotated.
    function turnVector(turn: {
      rot: number;
      flipH: boolean;
      flipV: boolean;
    }, x: number, y: number): {
      x: number;
      y: number;
    };
  • placeChildfunctionWhere a group puts one of its children.
    function placeChild(group: Frame, space: ChildSpace, child: Frame): Frame;
  • frameTransformfunctionThe SVG transform that puts a shape's local space on the slide.
    function frameTransform(frame: Frame): string;
  • VectypeA point, in whichever space the function taking it names.
    interface Vec {
      readonly x: number;
      readonly y: number;
    }
  • framePointfunctionA point in the shape's own space, moved onto the slide.
    function framePoint(frame: Frame, point: Vec): Vec;
  • inverseFramePointfunctionA point on the slide, brought back into the shape's own space.
    function inverseFramePoint(frame: Frame, point: Vec): Vec;
  • unionBoxfunctionThe union of two boxes.
    function unionBox(a: Box | null, b: Box | null): Box | null;

src/layout.ts

  • PlacedtypeA shape with its position, outline and paint all resolved.
    interface Placed {
      readonly shape: Shape;
      readonly sheet: Sheet;
      readonly frame: Frame;
      /**
       * The outline, evaluated at the frame's extent.
       *
       * `null` for a shape that declares no geometry and inherits none - a group, a
       * `p:graphicFrame`, or a text box that is nothing but its text.
       */
      readonly geometry: ResolvedGeometry | null;
      /**
       * The definition `geometry` was evaluated from, and the adjust values in
       * force.
       *
       * `geometry` is the answer at one size; this is what produced it. The adjust
       * handles need the definition itself - `resolveHandles` and `dragHandle` both
       * take a `Geometry` - and re-deriving it in the overlay would mean redoing the
       * inheritance walk somewhere that cannot see the sheet chain.
       */
      readonly geometrySource: GeometrySource | null;
      readonly appearance: ResolvedAppearance;
      /** What is painted, after `a:grpFill` has been followed. `null` paints nothing. */
      readonly fill: Fill | null;
      /** The colour `phClr` takes inside `fill`. */
      readonly fillPhClr: Rgba | null;
      /**
       * The rectangle the fill is laid out over, in slide EMU.
       *
       * The shape's own frame, except for an `a:grpFill`, where it is the content
       * bounds of the group the fill came from.
       */
      readonly fillBox: Box;
      readonly colorContext: ColorContext;
      readonly children: readonly Placed[];
      /** For a group: the bounding box of every leaf inside it. `null` otherwise. */
      readonly contentBounds: Box | null;
    }
  • GeometrySourcetypeA geometry definition, with whatever a:avLst the file put over it.
    interface GeometrySource {
      readonly geometry: Geometry;
      readonly adjust: readonly PresetGuide[];
    }
  • MAX_GROUP_DEPTHconstHow deep a group tree may nest before it is treated as hostile.
    const MAX_GROUP_DEPTH = 64;
  • layoutSheetfunctionThe whole shape tree of a sheet, placed.
    function layoutSheet(sheet: Sheet): readonly Placed[];
  • inheritedSheetsfunctionThe sheets whose own shapes appear beneath this one's, furthest away first.
    function inheritedSheets(sheet: Sheet): readonly Sheet[];
  • layoutSlidefunctionEverything drawn on a sheet: the inherited furniture, then its own shapes.
    function layoutSlide(sheet: Sheet): readonly Placed[];
  • flattenfunctionEvery placed shape in a tree, depth first, groups included.
    function flatten(placed: readonly Placed[]): readonly Placed[];

src/node.ts

  • AttributeValuetypeA tiny SVG node type, and the string emitter over it.
    type AttributeValue = string | number | null | undefined;
  • SvgElementtype
    interface SvgElement {
      readonly kind: 'element';
      readonly tag: string;
      readonly attrs: Readonly<Record<string, AttributeValue>>;
      readonly children: readonly SvgNode[];
    }
  • SvgTexttype
    interface SvgText {
      readonly kind: 'text';
      readonly text: string;
    }
  • SvgNodetype
    type SvgNode = SvgElement | SvgText;
  • elementfunction
    function element(tag: string, attrs?: Readonly<Record<string, AttributeValue>>, children?: readonly SvgNode[]): SvgElement;
  • textfunction
    function text(value: string): SvgText;
  • numfunctionA number, short enough to read and long enough to be right.
    function num(value: number, precision?: number): string;
  • escapeXmlfunctionEscape everything, including > and both quotes.
    function escapeXml(value: string): string;
  • serializeSvgfunction
    function serializeSvg(node: SvgNode): string;

src/overlay.ts

  • DEFAULT_UNITconstOne EMU per screen pixel at 100% on a 10-inch-wide slide is 12700.
    const DEFAULT_UNIT = 12700;
  • LOCUS_SAMPLESconstHow many positions of a handle's locus are drawn.
    const LOCUS_SAMPLES = 25;
  • PATH_COLORSconstA path colour per subpath, cycled.
    const PATH_COLORS: readonly string[];
  • OverlayOptionstype
    interface OverlayOptions {
      /** EMU per screen pixel, so chrome stays one thickness at any zoom. */
      readonly unit?: number;
      readonly paths?: boolean;
      readonly textRect?: boolean;
      readonly connectionSites?: boolean;
      readonly handles?: boolean;
      /** Sample each handle axis across its range and draw the curve. Default true. */
      readonly locus?: boolean;
    }
  • OverlayGuidetypeOne guide, for the live gdLst dump.
    interface OverlayGuide {
      readonly name: string;
      readonly value: number;
      /** True for one of the 44 seeded built-ins rather than the shape's own. */
      readonly builtin: boolean;
      readonly finite: boolean;
    }
  • OverlayHandletypeOne handle, with everything the pointer and the panel both need.
    interface OverlayHandle {
      readonly index: number;
      readonly handle: PresetAdjustHandle;
      readonly resolved: ResolvedHandle;
      /** The curve `resolved.pos` traces as each axis sweeps its range, in shape space. */
      readonly locus: readonly (readonly Point[])[];
    }
  • ShapeOverlaytype
    interface ShapeOverlay {
      /** The whole overlay for this shape, already carrying the shape's transform. */
      readonly node: SvgElement;
      readonly guides: readonly OverlayGuide[];
      readonly handles: readonly OverlayHandle[];
      readonly textRect: ResolvedRect | null;
      readonly connectionSites: readonly ResolvedConnectionSite[];
      /** Paths whose evaluation reached a non-finite number, by index. */
      readonly brokenPaths: readonly number[];
    }
  • shapeOverlayfunctionThe overlay for one placed shape.
    function shapeOverlay(placed: Placed, options?: OverlayOptions): ShapeOverlay;

src/image/blip.ts

  • MediaImagetypeWhat the caller hands back for an r:embed, having resolved it themselves.
    interface MediaImage {
      readonly bytes: Uint8Array;
      readonly contentType: string;
    }
  • MediaResolvertypeResolves a relationship id against the part the fill was written in.
    type MediaResolver = (embed: string, part: string) => MediaImage | undefined;
  • blipPaintfunctionAn image fill as a <pattern> reference.
    function blipPaint(fill: BlipFill, ctx: ColorContext, box: Box, defs: Defs, media: MediaResolver | undefined, size: (bytes: Uint8Array) => ImageSize, uri: (bytes: Uint8Array, contentType: string) => string): Attrs;

src/paint.ts

  • Attrstype
    type Attrs = Record<string, AttributeValue>;
  • DefsclassEverything that has to live in <defs>, with the ids that reach it.
    class Defs {
      private readonly nodes;
      private next;
      /** A per-slide prefix, so two slides in one document cannot collide. */
      private readonly prefix;
      /** How an image fill reaches its picture; absent means none can be drawn. */
      readonly media: MediaResolver | undefined;
      /** Device pixels to the point the slide is drawn at, or null for a scale nobody has named. */
      readonly pxPerPt: number | null;
      constructor(prefix: string, media?: MediaResolver, pxPerPt?: number | null);
      id(): string;
      add(node: SvgElement): void;
      get length(): number;
      toNode(): SvgElement | null;
    }
  • fillAttributesfunctionThe fill attributes for one shape.
    function fillAttributes(fill: Fill | null, ctx: ColorContext, box: Box, defs: Defs): Attrs;
  • StrokePainttype
    interface StrokePaint {
      readonly attrs: Attrs;
      readonly band: StrokeBand;
      readonly line: ResolvedLine;
      /** Half a device pixel in EMU for an odd pen at a named scale, else zero (F3, `snap.json`). */
      readonly shift: number;
      /** Whether the true width is a whole number of device pixels at the named scale, so the pen is the width. */
      readonly whole: boolean;
    }
  • strokeAttributesfunctionThe stroke attributes for one shape, or null when nothing is stroked.
    function strokeAttributes(line: ResolvedLine | null, ctx: ColorContext, box: Box, defs: Defs, forced?: StrokeBand): StrokePaint | null;
  • withEffectsfunctionA shape's children under its effects, or the children as they were for none.
    function withEffects(children: readonly SvgElement[], effects: readonly Effect[], ctx: ColorContext, box: Box, defs: Defs): readonly SvgElement[];

src/text/resolve.ts

  • ResolvedRuntypeOne piece of a paragraph, with every property already answered.
    interface ResolvedRun {
      readonly text: string;
      readonly font: RunFont;
      /** `null` where nothing in the cascade named a colour. */
      readonly color: Rgba | null;
      readonly highlight: Rgba | null;
      readonly underline: Underline | undefined;
      readonly strike: Strike | undefined;
      readonly caps: Caps | undefined;
      /** `a:rPr/@baseline`, thousandths of a percent. */
      readonly baseline: number;
      /** A hard `a:br` ends the line after this run. */
      readonly hardBreak: boolean;
    }
  • ResolvedParagraphtype
    interface ResolvedParagraph {
      readonly level: number;
      readonly align: TextAlign;
      /** `@marL` and `@indent`, in points. */
      readonly marginLeft: number;
      readonly indent: number;
      readonly lineSpacing: LineSpacing;
      readonly spaceBefore: LineSpacing;
      readonly spaceAfter: LineSpacing;
      readonly runs: readonly ResolvedRun[];
      /** The properties of the paragraph mark, which set an empty line's height. */
      readonly endRun: ResolvedRun;
      /** What is drawn in front of the first line, or null where the cascade names no bullet. */
      readonly bullet: DrawableBullet<Rgba> | null;
    }
  • ResolvedFrametype
    interface ResolvedFrame {
      readonly anchor: TextAnchor;
      readonly anchorCtr: boolean;
      readonly vertical: VerticalText;
      readonly wrap: 'none' | 'square';
      /** In points, each edge on its own. */
      readonly insets: {
        left: number;
        top: number;
        right: number;
        bottom: number;
      };
      readonly columns: number;
      readonly columnSpacing: number;
      readonly rtlColumns: boolean;
      /** `a:bodyPr/@rot` in degrees, positive clockwise. */
      readonly bodyRotation: number;
      readonly upright: boolean;
      readonly vertOverflow: 'overflow' | 'ellipsis' | 'clip';
      /** `a:normAutofit/@fontScale` as a multiplier, already quantised. */
      readonly fontScale: number;
      readonly lineSpaceReduction: number;
    }
  • ResolvedTexttype
    interface ResolvedText {
      readonly frame: ResolvedFrame;
      readonly paragraphs: readonly ResolvedParagraph[];
    }
  • resolveTextfunctionEverything a shape's text needs, or null where it has none.
    function resolveText(placed: Placed, defaultTextStyle?: ListStyle): ResolvedText | null;

src/text/layout.ts

  • TextPiecetypeOne stretch of characters drawn in one font at one place.
    interface TextPiece {
      /** The characters as the file has them, before `@cap`. */
      readonly source: string;
      /** The characters to draw, after `@cap` has been applied. */
      readonly text: string;
      readonly font: RunFont;
      /** The CSS `font-family` value, computed once so two emitters cannot differ. */
      readonly cssFamily: string;
      readonly color: Rgba | null;
      readonly highlight: Rgba | null;
      /** Points from the line's own left edge. */
      readonly leftPt: number;
      readonly widthPt: number;
      /** Points above the line's baseline; a subscript is negative. */
      readonly risePt: number;
      /** The rules this piece draws, already placed. */
      readonly rules: readonly PieceRule[];
      /**
       * The glyphs of this piece, each placed and turned a quarter out of the line.
       *
       * Empty for every piece that flows, which is all of them outside an `eaVert`
       * frame. A piece with entries draws these and not `text`.
       */
      readonly upright: readonly UprightGlyph[];
    }
  • UprightGlyphtypeOne glyph stood upright inside a turned line, in the line's coordinates.
    interface UprightGlyph {
      readonly text: string;
      /** The glyph's alphabetic baseline, from the line's own left edge. */
      readonly alongPt: number;
      /** The glyph's own x origin, from the line's own top edge. */
      readonly acrossPt: number;
      /**
       * The `line-height` that lands a browser's baseline `sizePt` below the top.
       *
       * The SVG emitter places a baseline as a coordinate and ignores this; the HTML
       * layer cannot, and a glyph drawn from its own pen is outside the line's own
       * strut. `sizePt` is the anchor because a single glyph has no other.
       */
      readonly strutPt: number;
    }
  • PieceRuletypeOne drawn rule, in points from the line's left edge and its baseline.
    interface PieceRule {
      readonly leftPt: number;
      readonly widthPt: number;
      /** The rule's top edge below the baseline; a strikethrough's is negative. */
      readonly topPt: number;
      readonly thickness: number;
      readonly pattern: DrawnRule['pattern'];
      readonly wavy: boolean;
    }
  • TextLinetype
    interface TextLine {
      /**
       * The paragraph's bullet, on its first line only, placed from the line's own left edge.
       *
       * Negative `leftPt`, usually: the bullet sits in the hanging indent to the left of the text.
       */
      readonly bullet: TextPiece | null;
      /** Points from the frame's top-left corner, before the block is turned. */
      readonly topPt: number;
      readonly heightPt: number;
      /** The baseline, from the same corner. */
      readonly baselinePt: number;
      readonly leftPt: number;
      readonly widthPt: number;
      /** The largest size on the line, which is what its box follows. */
      readonly sizePt: number;
      /**
       * The CSS `line-height` that lands a browser's baseline on `baselinePt`.
       *
       * A browser puts the baseline of an inline box a half-leading plus an ascent
       * below the line box; solving that for the share PowerPoint uses is one line
       * of arithmetic, done here so the HTML layer and the SVG cannot answer it
       * differently. It lands within one CSS pixel and no closer, because Chromium
       * rounds the font's ascent to a whole pixel before it lays a line out.
       */
      readonly strutPt: number;
      /**
       * Extra advance on every space, in points, from a justifying alignment.
       *
       * A width rather than a set of positions: an emitter that repositioned each
       * word would break the shaping of the line, and T8 measured that a line is
       * shaped whole.
       */
      readonly wordSpacingPt: number;
      readonly pieces: readonly TextPiece[];
    }
  • TextBlocktype
    interface TextBlock {
      /** Degrees clockwise about the frame centre, after every turn is composed. */
      readonly turnDeg: number;
      /** The rectangle the block was laid out in, in the shape's own space. */
      readonly boxPt: Box$1;
      readonly lines: readonly TextLine[];
    }
  • LayoutTextOptionstypeWhat layoutText needs besides the resolved text.
    interface LayoutTextOptions {
      /** The shape's own extent, in points. */
      readonly widthPt: number;
      readonly heightPt: number;
      /** `a:xfrm/@rot`, in degrees clockwise, as `Frame` already has it. */
      readonly rot: number;
      readonly flipH: boolean;
      readonly flipV: boolean;
      readonly measurer?: TextMeasurer | undefined;
      readonly faceBox?: FaceBoxProbe | undefined;
      /** The rules for a typeface, so a caller decides what an unmeasured one gets. */
      readonly rulesFor: (typeface: string) => FaceRules;
      /** The CSS `font-family` a piece is drawn in; defaults to the run's own family, quoted. */
      readonly cssFamilyFor?: ((font: RunFont) => string) | undefined;
    }
  • SHIFT_SIZE_RATIOconstA superscript or subscript is drawn at two thirds of the size it asked for.
    const SHIFT_SIZE_RATIO: number;
  • SMALL_CAPS_RATIOconstcap="small" draws the uppercased lowercase at four fifths of the size.
    const SMALL_CAPS_RATIO = 0.8;
  • textTurnfunctionThe angle the block is drawn at, and the box it is laid out in.
    function textTurn(frame: ResolvedFrame, options: Pick<LayoutTextOptions, 'widthPt' | 'heightPt' | 'rot' | 'flipH' | 'flipV'>): {
      turnDeg: number;
      boxPt: Box$1;
    };
  • capStretchesfunction@cap applied to a run's text.
    function capStretches(text: string, caps: string | undefined): readonly CapStretch[];
  • alignOffsetfunctionWhere a line starts inside its column, in points.
    function alignOffset(align: string, columnWidthPt: number, anchorWidthPt: number): number;
  • stretchesfunctionWhether this alignment stretches this line to the full column.
    function stretches(align: string, lastLine: boolean): boolean;
  • layoutTextfunctionLay a resolved body out inside a shape.
    function layoutText(text: ResolvedText, options: LayoutTextOptions): TextBlock;
  • strutHeightfunctionThe line-height that puts a browser's baseline at dropPt.
    function strutHeight(dropPt: number, face: FaceBox, sizePt: number): number;

src/text/draw.ts

  • TextOptionstypeHow a caller supplies the three things text drawing cannot invent.
    interface TextOptions {
      /** `p:defaultTextStyle`, the cascade's seventh source. */
      readonly defaultTextStyle?: ListStyle | undefined;
      /** Defaults to one over `OffscreenCanvas`, which needs a browser or a Worker. */
      readonly measurer?: TextMeasurer | undefined;
      readonly faceBox?: FaceBoxProbe | undefined;
      /**
       * What to do about a typeface whose rules nobody measured.
       *
       * Defaults to Arial's, because a renderer that refused to draw an underline on
       * an unmeasured face would refuse most decks. A caller that wants the
       * diagnostic passes `faceRules` itself and handles the throw.
       */
      readonly rulesFor?: ((typeface: string) => FaceRules) | undefined;
      /**
       * The CSS `font-family` a run is drawn in.
       *
       * Defaults to the run's own family, quoted, which is right where the caller
       * measures through that same name; a caller that measured in another face
       * names it here so the markup says what was drawn.
       */
      readonly cssFamilyFor?: ((font: RunFont) => string) | undefined;
    }
  • approximateRulesfunctionArial's rules for anything unmeasured, which is what a renderer wants.
    function approximateRules(typeface: string): FaceRules;
  • TextEnginetypeEverything drawing needs, made once per slide rather than once per shape.
    interface TextEngine {
      readonly defaultTextStyle: ListStyle | undefined;
      readonly measurer: TextMeasurer;
      readonly faceBox: FaceBoxProbe;
      readonly rulesFor: (typeface: string) => FaceRules;
      readonly cssFamilyFor: (font: RunFont) => string;
    }
  • createTextEnginefunctionThe measurer and the probe, made once and not before they are needed.
    function createTextEngine(options?: TextOptions): TextEngine;
  • textBlockOffunctionThe laid-out block of a placed shape, or null where it has no text.
    function textBlockOf(placed: Placed, engine: TextEngine): TextBlock | null;
  • shapeTextNodesfunctionThe text of a placed shape, as a sibling group of its geometry.
    function shapeTextNodes(placed: Placed, engine: TextEngine): readonly SvgNode[];

src/shape.ts

  • shapeNodesfunctionThe nodes for one shape, in painting order.
    function shapeNodes(placed: Placed, defs: Defs, text?: TextEngine | null): readonly SvgNode[];

src/text/emit.ts

  • textNodesfunctionA block as one <g>, turned about the frame's centre and scaled to points.
    function textNodes(block: TextBlock, frame: {
      x: number;
      y: number;
      cx: number;
      cy: number;
    }, attrs?: Record<string, string | number | null>): readonly SvgNode[];

src/slide.ts

  • SlideSizetypeThe slide size in EMU, from p:presentation/p:sldSz.
    interface SlideSize {
      readonly cx: number;
      readonly cy: number;
    }
  • RenderOptionstype
    interface RenderOptions {
      /**
       * A prefix for every generated `id`.
       *
       * Two slides in one HTML document share a URL space, so a `url(#g-1)` from
       * the second silently paints with the first's gradient unless the ids differ.
       * Defaults to something unique per call.
       */
      readonly idPrefix?: string;
      /** The `width` attribute in CSS pixels, which also names the device scale strokes round to. */
      readonly width?: number;
      /** The `height` attribute in CSS pixels: a box the slide is stretched into, as the export is (F2). */
      readonly height?: number;
      /** Device pixels per CSS pixel, so a 2x display rounds strokes to its own pixels. Default 1. */
      readonly devicePixelRatio?: number;
      /** Leave off `xmlns`, for a fragment going into an existing SVG document. */
      readonly inline?: boolean;
      /** Draw the master's and layout's shapes beneath the sheet's own. Default true. */
      readonly inherited?: boolean;
      /**
       * How an image fill or picture reaches its bytes.
       *
       * The renderer resolves no relationships: a fill written in a layout means
       * the layout part own rels, and only the caller knows which part it came from.
       */
      readonly media?: MediaResolver;
      /**
       * How to draw text, or `false` to draw none.
       *
       * Text needs a measuring surface, so a caller with no `OffscreenCanvas` - a
       * Node script, a test asserting geometry alone - turns it off rather than
       * getting a throw from a package it did not know it was using.
       */
      readonly text?: TextOptions | false;
    }
  • SlideRendertype
    interface SlideRender {
      readonly node: SvgElement;
      readonly placed: readonly Placed[];
    }
  • slideNodefunctionThe node tree for one sheet.
    function slideNode(sheet: Sheet, size: SlideSize, options?: RenderOptions): SlideRender;
  • renderSlidefunctionOne sheet as an SVG string.
    function renderSlide(sheet: Sheet, size: SlideSize, options?: RenderOptions): string;

src/image/header.ts

  • imageSizefunctionThe size of a PNG, JPEG, GIF or BMP.
    function imageSize(bytes: Uint8Array): ImageSize;
  • dataUrifunctionA data: URI, encoded here rather than through btoa.
    function dataUri(bytes: Uint8Array, contentType: string): string;

src/image/media.ts

  • MediaStoretypeThe three questions a picture asks of a package, as PartStore answers them.
    interface MediaStore {
      relationships(part: string): {
        targetOf(id: string): string | undefined;
      };
      contentTypeOf(part: string): string | undefined;
      read(part: string): Uint8Array;
    }
  • mediaFromStorefunctionAn rId means the rels of the part the fill was written in; an untyped or missing target paints nothing.
    function mediaFromStore(store: MediaStore): MediaResolver;

Errors

Throws RenderError, whose code is one of:

  • RENDER_UNKNOWN_PRESET
  • RENDER_GROUP_DEPTH
  • RENDER_NO_HOST
  • RENDER_TEXT_FRAME
  • RENDER_TEXT_UNSUPPORTED
  • RENDER_IMAGE_FORMAT
  • RENDER_DEVICE_PIXEL_RATIO

Try it live

Reading the deck…

Open the full demo →

Read more

On this page