PPTX Studio
Packages

census

What is inside a deck, as plain JSON: parts, features, namespaces and problems.

@pptx-studio/census0.1.2runs inTabWorkerNodeLive demo →npm →

Tells you what is inside a deck as plain JSON - parts, features, namespaces, problems - without refusing a broken file.

npm install @pptx-studio/census @pptx-studio/opc @pptx-studio/xml

What it is

censusPackage opens a deck and returns one JSON-safe object describing it: the archive, every part with its size and element count, the relationship graph, the presentation structure, a feature census keyed to the phase of the plan that draws each feature, the namespaces actually used, and any problems it met on the way.

It refuses nothing. A broken deck still gets a count, with the problem recorded beside it, which is what makes it the right thing to attach to a bug report.

30-second example

census.worker.ts
// census.worker.ts
self.onmessage = ({ data }: MessageEvent<ArrayBuffer>) => {
  const census = censusPackage(new Uint8Array(data), {
    onProgress: (done, total) => self.postMessage({ progress: { done, total } }),
  });
  self.postMessage({ census }); // plain JSON by design, so it survives postMessage
};

Key concepts

Plain JSON on purpose

No Map, no class instance, nothing that would not survive postMessage. The census is meant to be computed in a Web Worker and handed back, and the CLI's inspect prints the very same object.

Features by namespace, never by prefix

A chart is recognised by the namespace its element is bound to, not by the c: prefix a writer happened to choose, so a deck that spells its prefixes differently counts the same.

Each feature names its phase

Every feature in the census carries the sub-phase of the plan that renders it, so the answer to "will this deck draw?" is in the census itself.

API reference

32 exports of @pptx-studio/census 0.1.2, read from the package this site installed. Click a name for its declaration.

src/types.ts

  • NameCounttypeA { name, count } histogram entry. Sorted by count descending, then name.
    interface NameCount {
      readonly name: string;
      readonly count: number;
    }
  • ArchiveCensustypeHow the archive itself is put together, before anything is inflated.
    interface ArchiveCensus {
      /** The `.pptx` as read: compressed, headers and all. */
      readonly bytes: number;
      readonly entries: number;
      /** Entries that are OPC parts. The content-type stream is not one. */
      readonly parts: number;
      /**
       * Entries whose name ends in a slash.
       *
       * PowerPoint writes none and our writer never will. A package that has them
       * still opens, so this is a note rather than a problem - but it is the
       * clearest single signal that a deck was rebuilt by a third-party tool.
       */
      readonly directoryEntries: number;
      readonly storedEntries: number;
      readonly deflatedEntries: number;
      /**
       * True when the archive carries a ZIP64 end-of-central-directory record.
       *
       * PowerPoint never writes one and our writer never will, but other producers
       * emit one even for small archives - so this says which producer wrote the
       * file, not how big it is.
       */
      readonly zip64: boolean;
      /** Entries carrying a streaming data descriptor rather than sizes up front. */
      readonly dataDescriptorEntries: number;
      /** Sum of every entry's uncompressed size, as the central directory declares. */
      readonly declaredInflatedBytes: number;
      readonly compressedBytes: number;
      readonly largestPart: NameCount | null;
    }
  • PartKindtype
    type PartKind = 'xml' | 'binary';
  • PartCensustypeOne part, and what a scan of it found. Ordered as the archive stores them.
    interface PartCensus {
      readonly name: string;
      readonly contentType: string;
      /** Where the content type came from: a `Default` extension or an `Override`. */
      readonly contentTypeOrigin: 'default' | 'override' | 'unknown';
      readonly kind: PartKind;
      readonly bytes: number;
      readonly compressedBytes: number;
      /** Elements in the part. `null` for a part that was not scanned. */
      readonly elements: number | null;
      /** Deepest element nesting. `null` for a part that was not scanned. */
      readonly maxDepth: number | null;
      /** Outbound relationships declared in this part's own relationship part. */
      readonly relationships: number;
    }
  • DanglingRelationshiptype
    interface DanglingRelationship {
      readonly source: string;
      readonly id: string;
      readonly target: string;
    }
  • RelationshipCensustype
    interface RelationshipCensus {
      readonly total: number;
      readonly internal: number;
      readonly external: number;
      /** Relationship type histogram, shortened to the last path segment. */
      readonly byType: readonly NameCount[];
      /** Edges whose target part is not in the package. PowerPoint tolerates these. */
      readonly dangling: readonly DanglingRelationship[];
      /** Parts nothing relates to. Harmless, but they are what a media GC would take. */
      readonly unreachableParts: readonly string[];
    }
  • EmbeddedFontCensustypeAn embedded font, as p:embeddedFontLst declares it.
    interface EmbeddedFontCensus {
      readonly typeface: string;
      readonly panose: string | null;
      readonly charset: number | null;
      readonly pitchFamily: number | null;
      /** Which of regular/bold/italic/boldItalic carry a relationship. */
      readonly slots: readonly string[];
    }
  • PresentationCensustype
    interface PresentationCensus {
      readonly slideWidthEmu: number | null;
      readonly slideHeightEmu: number | null;
      /** `p:sldSz/@type`, for instance `screen16x9`. */
      readonly slideSizeType: string | null;
      readonly notesWidthEmu: number | null;
      readonly notesHeightEmu: number | null;
      readonly slides: number;
      readonly slideLayouts: number;
      readonly slideMasters: number;
      readonly notesSlides: number;
      readonly notesMasters: number;
      readonly handoutMasters: number;
      /**
       * `p:presentation/@embedTrueTypeFonts`.
       *
       * Omit it and PowerPoint ignores a perfect `p:embeddedFontLst` entirely, so
       * the flag and the list are reported separately on purpose: a deck with fonts
       * and no flag is a bug someone shipped, not a deck without fonts.
       */
      readonly embedTrueTypeFonts: boolean;
      /** `p:presentation/@saveSubsetFonts`. Governs subsetting, never compression. */
      readonly saveSubsetFonts: boolean;
      readonly embeddedFonts: readonly EmbeddedFontCensus[];
      /** `p14:sectionLst` entries. */
      readonly sections: number;
      readonly customShows: number;
      readonly firstSlideNum: number | null;
      readonly rtl: boolean;
    }
  • TextCensustype
    interface TextCensus {
      readonly paragraphs: number;
      readonly runs: number;
      readonly characters: number;
      /** `a:fld` - dates, slide numbers, and the GUIDs that must not be regenerated. */
      readonly fields: number;
      readonly hardBreaks: number;
      readonly bulletChar: number;
      readonly bulletAutoNum: number;
      readonly bulletBlip: number;
      /** `a:normAutofit` - text shrunk to fit, at the scale PowerPoint chose. */
      readonly normAutofit: number;
      /** `a:spAutoFit` - the shape grows instead. */
      readonly shapeAutofit: number;
    }
  • NamespaceCensustypeA namespace as it was actually used, with every prefix it was spelled with.
    interface NamespaceCensus {
      readonly uri: string;
      /** Elements and attributes in this namespace, across every part. */
      readonly count: number;
      readonly prefixes: readonly string[];
      /** True when ECMA-376 itself defines the namespace. */
      readonly standard: boolean;
      /** True for a post-ECMA-376 extension namespace we recognise by name. */
      readonly extension: boolean;
      /** True when it appeared in an `mc:Choice` requirement. */
      readonly required: boolean;
      /** True when some `mc:Ignorable` named it. */
      readonly ignorable: boolean;
    }
  • FeatureCensustypeA detected feature, and the phase of the plan that makes it render.
    interface FeatureCensus {
      readonly key: string;
      readonly label: string;
      readonly count: number;
      /** The sub-phase or phase that implements it, for instance `4.6` or `10.2`. */
      readonly phase: string;
    }
  • ProblemSeveritytype
    type ProblemSeverity = 'error' | 'warning' | 'note';
  • CensusProblemtype
    interface CensusProblem {
      readonly severity: ProblemSeverity;
      readonly code: string;
      readonly message: string;
      readonly part: string | null;
    }
  • CensusTimingstype
    interface CensusTimings {
      /** Opening the archive: end-of-central-directory, entry table, content types. */
      readonly openMs: number;
      /** Inflating every XML part. */
      readonly inflateMs: number;
      /** Tokenizing and counting. */
      readonly scanMs: number;
      readonly totalMs: number;
      /** XML bytes handed to the tokenizer, so throughput is derivable. */
      readonly xmlBytesScanned: number;
      readonly partsScanned: number;
    }
  • PackageCensustype
    interface PackageCensus {
      /** Schema version of this document, so a stored census can be re-read later. */
      readonly censusVersion: 1;
      readonly archive: ArchiveCensus;
      readonly parts: readonly PartCensus[];
      readonly contentTypes: readonly NameCount[];
      readonly relationships: RelationshipCensus;
      readonly presentation: PresentationCensus | null;
      readonly features: readonly FeatureCensus[];
      readonly namespaces: readonly NamespaceCensus[];
      /** The most common qualified element names, spelled as they were written. */
      readonly elements: readonly NameCount[];
      /** `a:prstGeom/@prst` - which of the 187 presets this deck actually uses. */
      readonly presetGeometry: readonly NameCount[];
      /** Every typeface named by `a:latin`, `a:ea`, `a:cs` or `a:sym`. */
      readonly typefaces: readonly NameCount[];
      /** Every `@lang` seen on a run. */
      readonly languages: readonly NameCount[];
      readonly text: TextCensus;
      readonly problems: readonly CensusProblem[];
      readonly timings: CensusTimings;
    }

src/census.ts

  • CensusOptionstypeWhat is inside this .pptx.
    interface CensusOptions {
      /** Passed to the ZIP reader. Defaults hold a 200 MB / 1 GB envelope. */
      readonly limits?: Partial<ZipLimits> | undefined;
      readonly tokenizerLimits?: XmlTokenizerLimits | undefined;
      /**
       * Called before each part is scanned.
       *
       * A 200 MB deck takes long enough that a progress bar is the difference
       * between "working" and "hung", and the worker has nothing else to report.
       */
      readonly onProgress?: ((done: number, total: number, part: string) => void) | undefined;
      /** Injectable clock, so a test can assert on timings without a real one. */
      readonly now?: (() => number) | undefined;
    }
  • censusPackagefunction
    function censusPackage(bytes: Uint8Array, options?: CensusOptions): PackageCensus;

src/format.ts

  • FormatOptionstypeA census as text.
    interface FormatOptions {
      /** Include the full per-part table. Off by default: a real deck has hundreds. */
      readonly parts?: boolean | undefined;
      /** Include the namespace histogram. */
      readonly namespaces?: boolean | undefined;
      /** Rows to show in each histogram before truncating. */
      readonly top?: number | undefined;
    }
  • humanBytesfunctionSizes a human reads. Deliberately binary units, and always labelled.
    function humanBytes(bytes: number): string;
  • formatCensusfunction
    function formatCensus(census: PackageCensus, options?: FormatOptions): string;

src/namespaces.ts

  • OOXML_NSconstECMA-376 namespaces: the standard vocabulary.
    const OOXML_NS: {
      /** DrawingML main. Shapes, colours, text, geometry. */
      readonly a: "http://schemas.openxmlformats.org/drawingml/2006/main";
      /** PresentationML main. */
      readonly p: "http://schemas.openxmlformats.org/presentationml/2006/main";
      /** Relationship references *inside* part XML - `r:id`, `r:embed`, `r:link`. */
      readonly r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
      /** Markup Compatibility and Extensibility. */
      readonly mc: "http://schemas.openxmlformats.org/markup-compatibility/2006";
      /** DrawingML charts. */
      readonly c: "http://schemas.openxmlformats.org/drawingml/2006/chart";
      /** SmartArt data, layout, colours and quick styles. */
      readonly dgm: "http://schemas.openxmlformats.org/drawingml/2006/diagram";
      /** The `p:pic` wrapper used inside a `a:graphicFrame`. */
      readonly pic: "http://schemas.openxmlformats.org/drawingml/2006/picture";
      /** SpreadsheetML - reachable through an embedded chart workbook. */
      readonly x: "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
      /** WordprocessingML - reachable through an embedded document. */
      readonly w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
      /** Office Math. */
      readonly m: "http://schemas.openxmlformats.org/officeDocument/2006/math";
      /** `docProps/app.xml`. */
      readonly extendedProperties: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
      /** `docProps/core.xml`. */
      readonly coreProperties: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
      /** The `a:lockedCanvas` wrapper. */
      readonly lockedCanvas: "http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas";
    };
  • EXTENSION_NSconstMicrosoft extension namespaces, and the two VML ones.
    const EXTENSION_NS: {
      /** DrawingML 2010: `a14:imgProps`, `a14:hiddenFill`, `a14:cameraTool`. */
      readonly a14: "http://schemas.microsoft.com/office/drawing/2010/main";
      /** DrawingML 2014: `a16:creationId`, `a16:rowId`. */
      readonly a16: "http://schemas.microsoft.com/office/drawing/2014/main";
      /** PowerPoint 2010: media trim, sections, `p14:creationId`. */
      readonly p14: "http://schemas.microsoft.com/office/powerpoint/2010/main";
      /** PowerPoint 2012: `p15:sldGuideLst`, `p15:notesGuideLst`, threaded comments. */
      readonly p15: "http://schemas.microsoft.com/office/powerpoint/2012/main";
      /** PowerPoint 2015. */
      readonly p16: "http://schemas.microsoft.com/office/powerpoint/2015/main";
      /** The older PowerPoint extension namespace, still present in real decks. */
      readonly p2007: "http://schemas.microsoft.com/office/powerpoint/2007/7/12/main";
      /** Chart 2007. */
      readonly c14: "http://schemas.microsoft.com/office/drawing/2007/8/2/chart";
      /** Chart 2014. */
      readonly c16: "http://schemas.microsoft.com/office/drawing/2014/chart";
      /** ChartEx: waterfall, funnel, treemap, sunburst, histogram, box-whisker, map. */
      readonly cx: "http://schemas.microsoft.com/office/drawing/2014/chartex";
      /** Chart styles - `style1.xml`. */
      readonly chartStyle: "http://schemas.microsoft.com/office/drawing/2012/chartStyle";
      /** The SmartArt *drawing* fallback: `drawingN.xml`. */
      readonly dsp: "http://schemas.microsoft.com/office/drawing/2008/diagram";
      /** SmartArt 2016 extensions. */
      readonly dgm1612: "http://schemas.microsoft.com/office/drawing/2016/12/diagram";
      /** Scalable vector blips - `asvg:svgBlip`. */
      readonly asvg: "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
      /** 3-D models - `am3d:model3d`. */
      readonly am3d: "http://schemas.microsoft.com/office/drawing/2017/model3d";
      /** The decorative marker screen readers are meant to honour. */
      readonly adec: "http://schemas.microsoft.com/office/drawing/2017/decorative";
      /** Ink annotations wrapper. */
      readonly aink: "http://schemas.microsoft.com/office/drawing/2016/ink";
      /** W3C InkML: the trace data itself. */
      readonly inkml: "http://www.w3.org/2003/InkML";
      /** VML, still the carrier for OLE object previews. */
      readonly v: "urn:schemas-microsoft-com:vml";
      /** The VML companion namespace. */
      readonly o: "urn:schemas-microsoft-com:office:office";
    };
  • CONVENTIONAL_PREFIXconstPrefix conventionally used for a URI, for display only. Never for matching.
    const CONVENTIONAL_PREFIX: ReadonlyMap<string, string>;
  • isStandardNamespacefunctionTrue for a namespace that ECMA-376 itself defines.
    function isStandardNamespace(uri: string): boolean;
  • isExtensionNamespacefunctionTrue for a namespace that postdates ECMA-376 - the extension surface.
    function isExtensionNamespace(uri: string): boolean;

src/features.ts

  • FeatureRuletypeWhat a census looks for, and which phase of the plan makes it render.
    interface FeatureRule {
      readonly key: string;
      readonly label: string;
      /** Sub-phase that implements it, or `preserve` for carry-across-only. */
      readonly phase: string;
      readonly uri: string;
      readonly local: string;
    }
  • FEATURE_RULESconst
    const FEATURE_RULES: readonly FeatureRule[];
  • PART_FEATURE_RULESconstFeatures detected from the package rather than from any element.
    const PART_FEATURE_RULES: readonly {
      readonly key: string;
      readonly label: string;
      readonly phase: string;
      readonly match: (partName: string, contentType: string) => boolean;
    }[];

src/scan.ts

  • ScanTotalstypeEverything a census accumulates, across every part.
    interface ScanTotals {
      readonly elements: Map<string, number>;
      readonly namespaces: Map<string, NamespaceUse>;
      readonly features: Map<string, number>;
      readonly presetGeometry: Map<string, number>;
      readonly typefaces: Map<string, number>;
      readonly languages: Map<string, number>;
      paragraphs: number;
      runs: number;
      characters: number;
      fields: number;
      hardBreaks: number;
      bulletChar: number;
      bulletAutoNum: number;
      bulletBlip: number;
      normAutofit: number;
      shapeAutofit: number;
      /** Total elements across every part scanned. */
      totalElements: number;
      /** Bindings already built, so every part in the package shares the objects. */
      readonly bindings: Map<string, Binding>;
    }
  • createTotalsfunction
    function createTotals(): ScanTotals;
  • PartScantypeWhat one part contributed, for the per-part table.
    interface PartScan {
      readonly elements: number;
      readonly maxDepth: number;
      /**
       * Why the part is not well formed, or `null`.
       *
       * A token stream is not a tree, so nothing in the tokenizer objects to
       * `<a><b></a>` or to a document that simply stops - it hands over the tokens
       * it found and falls silent. The first version of this scanner therefore
       * reported a truncated slide as perfectly healthy, which is precisely the
       * wrong answer from a tool whose whole job is to describe a file that is
       * already suspect. Balance is checked here, against a stack of open names,
       * because this is the only place that has one.
       */
      readonly imbalance: string | null;
    }
  • scanXmlPartfunctionScan one XML part, folding what it holds into totals.
    function scanXmlPart(source: string, totals: ScanTotals, limits?: XmlTokenizerLimits): PartScan;

Errors

This package declares no error class of its own; what it throws comes from the packages beneath it.

Try it live

Reading the deck…

Open the full demo →

Read more

On this page