Document Structure

A PaperDocument is a tree: Document contains Slides, each slide contains Nodes. The format is the canonical input for both the self-hosted @paperjsx/core engine and the hosted V2 API.

Code
PaperDocument
  ├── meta: { title, author }
  ├── slideSize: { width, height }   (default 960x540 px)
  ├── theme?: ThemeConfig
  ├── template?: Buffer              (self-hosted only)
  └── slides: PaperSlide[]
        └── children: PaperNode[]

Minimal Working Example

JSON
{
  "type": "Document",
  "meta": { "title": "Hello World" },
  "slides": [
    {
      "type": "Slide",
      "children": [
        {
          "type": "Text",
          "style": {
            "position": "absolute",
            "top": 200, "left": 80, "width": 800,
            "fontSize": 32, "fontWeight": "bold",
            "textAlign": "center"
          },
          "content": "Hello, CmdCal"
        }
      ]
    }
  ]
}

Node Types

CmdCal defines 9 node types. Every node accepts a style property using FlexStyle for layout.

Text

Renders a text box. Supports plain strings or rich text runs with per-run styling.

JSON
{
  "type": "Text",
  "style": { "position": "absolute", "top": 100, "left": 60, "width": 400, "fontSize": 18 },
  "content": "Plain string"
}

For rich text, use content as an array of TextRun objects, or use paragraphs for multi-paragraph control with bullets, alignment, and indentation:

JSON
{
  "type": "Text",
  "paragraphs": [
    { "runs": [{ "text": "Bold intro.", "style": { "fontWeight": "bold" } }] },
    { "runs": [{ "text": "Second paragraph." }], "spaceBefore": 8 }
  ]
}

View

A shape container. Supports children, preset shapes via shapeType, and custom geometry.

JSON
{
  "type": "View",
  "style": { "position": "absolute", "left": 60, "top": 100, "width": 200, "height": 120, "backgroundColor": "#2563EB" },
  "shapeType": "roundRect",
  "children": [
    { "type": "Text", "style": { "fontSize": 14, "color": "#FFFFFF", "padding": 12 }, "content": "Card" }
  ]
}

Image

Embeds raster or SVG images. Supports base64 data URLs and HTTPS URLs.

Key properties: src (required), svgSrc (native SVG embed with PNG fallback in src), crop ({ left, top, right, bottom } as percentages), borderRadius, imageEffects.

Table

Renders a native PPTX table. Defined via tableData containing columns (pixel widths), rows (arrays of cells), and optional style for banding and borders.

Chart

Renders a native OOXML chart. Defined via chartData with chartType (one of: bar, line, area, pie, doughnut, scatter, bubble, radar, combo, waterfall, stock, funnel, treemap, sunburst, histogram, boxWhisker), categories, series, axis configuration, and data labels.

Group

A logical grouping of child nodes. Does not render a visible shape but applies a shared transform and supports locks for edit protection.

Connector

A line connecting two points or shapes. Defined via connectorType (straight, elbow, curved), start/end coordinates, and optional arrow configs.

Video and Audio

Embed media files. Both accept src (URL or data URI), mimeType, and playback options (autoPlay, loop, showControls, volume).

FlexStyle Layout System

All nodes use FlexStyle for positioning. The engine uses Yoga (flexbox) for layout computation.

TYPESCRIPT
interface FlexStyle {
  // Positioning
  position?: "relative" | "absolute";
  top?: number; right?: number; bottom?: number; left?: number;

  // Dimensions
  width?: number | `${number}%`;
  height?: number | `${number}%`;
  minWidth?: number | `${number}%`;
  maxWidth?: number | `${number}%`;

  // Flex container
  flexDirection?: "row" | "column";
  justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around";
  alignItems?: "flex-start" | "flex-end" | "center" | "stretch";
  flexWrap?: "nowrap" | "wrap" | "wrap-reverse";
  gap?: number;

  // Flex item
  flexGrow?: number;
  flexShrink?: number;
  flexBasis?: number | `${number}%`;

  // Spacing
  padding?: number; margin?: number;  // also paddingTop/Right/Bottom/Left

  // Visual
  backgroundColor?: ColorValue;
  fill?: Fill;
  borderWidth?: number; borderColor?: ColorValue;
  opacity?: number; rotation?: number;
}
Dimensions are in pixels. The engine converts to EMU (English Metric Units) for OOXML serialization at 914400 EMU per inch (96 DPI).

Color Formats

The ColorValue type accepts three formats:

  1. Hex string: "#2563EB" (6-digit, no alpha)
  2. Scheme token: "accent1", "dk1", "lt1", etc. (resolved from the document theme)
  3. Color modifier object: Applies tint, shade, or luminance transforms to a scheme color
JSON
{ "scheme": "accent1", "tint": 40, "lumMod": 75 }

Available modifier fields: tint (0-100), shade (0-100), lumMod, lumOff, satMod, satOff, hueMod, hueOff, comp, inv, gray.

Validation with Zod Schemas

The @paperjsx/core package exports Zod schemas for compile-time and runtime validation:

TYPESCRIPT
import { PaperDocumentSchema, PaperSlideSchema, PaperNodeSchema } from "@paperjsx/core/engine";

const result = PaperDocumentSchema.safeParse(myJson);
if (!result.success) {
  console.error(result.error.issues);
}

These schemas enforce structural constraints like maximum array lengths (1000 nodes per slide, 16384 data points per chart series, 100 columns per table) and valid enum values for all string-union fields.

Hosted API: PresentationSpec

If you use the hosted V2 API, you can submit either a raw PaperDocument or a higher-level PresentationSpec with semantic slide types:

JSON
{
  "version": "2.0",
  "title": "Quarterly Review",
  "accentColor": "#2563EB",
  "slides": [
    { "slideType": "title-body", "title": "Q4 Results", "body": ["Revenue up 23%", "Margin expanded 400bp"] },
    { "slideType": "kpi-grid", "title": "Key Metrics", "items": [
      { "label": "ARR", "value": "$4.2M", "trend": "up" },
      { "label": "NRR", "value": "118%", "trend": "up" }
    ]}
  ]
}

Available slide types: title-body, kpi-grid, comparison-table, market-map, timeline, org-chart, waterfall, tombstone-grid.

The protocol compiler converts PresentationSpec into a PaperDocument internally before rendering.