render-svg
A slide to an SVG string, in a tab, in a Worker, or in Node.
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
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-exportre-exported from @pptx-studio/paint
src/errors.ts
RenderErrorCodetypeEvery failure in this package is aRenderErrorwith 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_CODESconstconst RENDER_ERROR_CODES: readonly RenderErrorCode[];
RenderErrorclassclass 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); }isRenderErrorfunctionfunction 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 ofa: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_FRAMEconstconst IDENTITY_FRAME: Frame;
frameOffunctionAna:xfrmas a frame, with@rotconverted 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_SPACEconstconst 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 SVGtransformthat 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 whatevera:avLstthe 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;
SvgElementtypeinterface SvgElement { readonly kind: 'element'; readonly tag: string; readonly attrs: Readonly<Record<string, AttributeValue>>; readonly children: readonly SvgNode[]; }SvgTexttypeinterface SvgText { readonly kind: 'text'; readonly text: string; }SvgNodetypetype SvgNode = SvgElement | SvgText;
elementfunctionfunction element(tag: string, attrs?: Readonly<Record<string, AttributeValue>>, children?: readonly SvgNode[]): SvgElement;
textfunctionfunction 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;
serializeSvgfunctionfunction 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[];
OverlayOptionstypeinterface 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 livegdLstdump.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[])[]; }ShapeOverlaytypeinterface 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 anr: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
Attrstypetype 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; }fillAttributesfunctionThefillattributes for one shape.function fillAttributes(fill: Fill | null, ctx: ColorContext, box: Box, defs: Defs): Attrs;
StrokePainttypeinterface 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; }strokeAttributesfunctionThestrokeattributes for one shape, ornullwhen 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; }ResolvedParagraphtypeinterface 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; }ResolvedFrametypeinterface 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; }ResolvedTexttypeinterface ResolvedText { readonly frame: ResolvedFrame; readonly paragraphs: readonly ResolvedParagraph[]; }resolveTextfunctionEverything a shape's text needs, ornullwhere 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; }TextLinetypeinterface 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[]; }TextBlocktypeinterface 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[]; }LayoutTextOptionstypeWhatlayoutTextneeds 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@capapplied 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;
strutHeightfunctionTheline-heightthat puts a browser's baseline atdropPt.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, ornullwhere 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, fromp:presentation/p:sldSz.interface SlideSize { readonly cx: number; readonly cy: number; }RenderOptionstypeinterface 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; }SlideRendertypeinterface 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;
dataUrifunctionAdata:URI, encoded here rather than throughbtoa.function dataUri(bytes: Uint8Array, contentType: string): string;
src/image/media.ts
MediaStoretypeThe three questions a picture asks of a package, asPartStoreanswers 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…