Pipeline Overview

The PaperEngine.render() call executes a multi-stage pipeline:

Code
PaperDocument JSON
  → Document validation (Zod schema + structural checks)
  → Z-index flattening
  → Layout computation (Yoga flexbox)
  → Typography shaping (HarfBuzz / fontkit)
  → OOXML serialization (XML generation)
  → Compatibility analysis
  → Archive assembly (ZIP packaging)
  → Structural validation (optional)
  → Structural repair (optional)
  → Quality report generation
  → Buffer output

Each stage can fail with a typed PaperError that includes a code and phase field for programmatic handling.

Engine Methods

The PaperEngine object exposes several methods at different levels of detail:

render()

The primary method. Returns a Buffer containing valid PPTX bytes.

TYPESCRIPT
const buffer = await PaperEngine.render(doc, {
  signal: abortController.signal,
  onProgress: (slideIndex, totalSlides) => {
    console.log(`Rendering slide ${slideIndex + 1}/${totalSlides}`);
  },
});

renderStream()

Same as render() but returns a Node.js Readable stream instead of a buffer.

renderWithQualityReport()

Returns both the PPTX buffer and a full QualityReport:

TYPESCRIPT
const { pptx, qualityReport } = await PaperEngine.renderWithQualityReport(doc, undefined, {
  outputMode: "editable_preferred",
  validationMode: "structural",
});

renderWithPreviews()

Returns the PPTX, slide preview images (as PNG buffers), layout trees, and quality report:

TYPESCRIPT
const { pptx, previews, layoutTrees, qualityReport } =
  await PaperEngine.renderWithPreviews(doc, { width: 1920 });

preflight()

A dry-run that validates the document and produces a QualityReport without generating a PPTX file. Useful for checking compatibility before committing to a full render.

TYPESCRIPT
const report = await PaperEngine.preflight(doc, {
  outputMode: "strict_editable",
});
if (!report.contractPassed) {
  console.error("Document would violate the editability contract");
}

Render Options

The EngineRenderOptions interface controls render behavior:

TYPESCRIPT
interface EngineRenderOptions {
  // Cancellation
  signal?: AbortSignal;
  onProgress?: (slideIndex: number, totalSlides: number) => void;

  // Quality contract
  outputMode?: "strict_editable" | "editable_preferred" | "visual_safe";
  validationMode?: "none" | "structural" | "desktop_async" | "desktop_blocking";
  repairMode?: "none" | "structural";
  maxFallbackLevel?: "native_editable" | "native_anchored" | "alternate_content" | "visual_fallback";
}

Output Modes

ModeBehavior
strict_editableAll slides must be fully editable in PowerPoint. Fails if any require fallback.
editable_preferredDefault. Allows native_anchored fallback for complex slides.
visual_safeAllows visual-only fallback. Maximizes visual fidelity over editability.

Validation Modes

ModeWhat it checks
noneNo post-render validation. Fastest.
structuralValidates the PPTX ZIP archive structure: content types, relationship IDs, XML well-formedness.
desktop_asyncSubmits to a PowerPoint desktop oracle (hosted API only).
desktop_blockingSame as async but blocks until the desktop check completes (hosted API only).
`desktop_async` and `desktop_blocking` require an external PowerPoint validation backend and are only available through the hosted V2 API. Calling them in the self-hosted engine throws `VALIDATION_BACKEND_UNAVAILABLE`.

Repair Mode

When repairMode is set to "structural", the engine runs a post-render repair pass that fixes common OOXML issues (duplicate relationship IDs, missing content types, malformed XML attribute ordering). The repair summary is included in the quality report.

Cancellation

Pass an AbortSignal to cancel a render mid-flight:

TYPESCRIPT
const controller = new AbortController();
setTimeout(() => controller.abort(), 30000);

try {
  const buffer = await PaperEngine.render(doc, { signal: controller.signal });
} catch (err) {
  if (err instanceof PaperError && err.code === "RENDER_CANCELLED") {
    console.log("Render was cancelled");
  }
}

The engine checks the signal between stages and at the mutex acquisition step. A cancelled render throws PaperError with code RENDER_CANCELLED.

Quality Reports

The QualityReport returned by preflight(), renderWithQualityReport(), and renderWithPreviews() contains:

FieldTypeDescription
documentVerdictQualityDocumentVerdictnative_editable, editable_with_constraints, visual_fallback, or rejected
editabilityScorenumber (0-100)Per-slide editability aggregate
deckScorenumber (0-100)Overall quality score (editability, risk, fallbacks)
repairRisk`"low""medium"
findingsQualityFinding[]Actionable issues with codes, severity, and fix hints
slideReportsSlideQualityReport[]Per-slide compatibility verdict and font info
contractPassedbooleanWhether the output meets the requested outputMode
structuralValidationStructuralValidationSummaryResults of ZIP/XML structural checks
repairSummaryRepairSummaryActions taken during structural repair

PaperError

All engine errors are instances of PaperError with structured fields:

TYPESCRIPT
class PaperError extends Error {
  readonly code: PaperErrorCode;
  readonly phase: ErrorPhase;
  readonly slideIndex?: number;
  readonly nodeId?: string;
}

Error codes: VALIDATION_FAILED, RESOURCE_LIMIT_EXCEEDED, RENDER_CANCELLED, WASM_INIT_FAILED, FONT_NOT_FOUND, MEDIA_FETCH_FAILED, MEDIA_CORRUPT, RENDER_TIMEOUT, COMPATIBILITY_CONTRACT_VIOLATION, STRUCTURAL_VALIDATION_FAILED, FEATURE_REQUIRES_UPGRADE.

Error phases: validation, compilation, layout, typography, media, chart, serialization, archive, wasm-init, font, template.

Concurrency

The engine uses an internal RenderMutex -- only one render executes at a time per process. Additional calls queue up and execute sequentially. The mutex respects AbortSignal, so queued renders can be cancelled before they start.

Lite vs Full Mode

The createEngine() factory supports two modes:

TYPESCRIPT
import { createEngine } from "@paperjsx/core/engine";

const lite = createEngine({ mode: "lite" });
const full = createEngine({ mode: "full" });
FeatureFullLite
Templates (.potx)YesNo (throws FEATURE_REQUIRES_UPGRADE)
All chart typesYes6 basic types
HarfBuzz shapingYesfontkit only
Canvas previewsYesNo
AutoFitYesNo