Introduction
pptxboss reads, verifies and creates PowerPoint .pptx files, and reads
legacy .ppt files through the same API. It is a clean-room
implementation of ECMA-376 (Office Open XML) in safe Rust, with no
compression or XML dependency. The same core is used by the command line,
the Rust crates and the Python extension. The PowerPoint 97-2003 binary
format is read from the MS-CFB, MS-PPT and MS-ODRAW specifications into
the same slide model, so text, notes, titles and pictures are read from a
.ppt the same way.
Leniency
Real decks are damaged. The reader tolerates bytes before the archive, a
missing central directory, and broken content types and relationships,
recovers the slide list from relationships when the presentation part does
not list it, and skips what it cannot read instead of refusing. Every skip
is reported: the CLI prints it to stderr as a warning: line, and
text_reporting returns it with the text.
Two views of a package
Package is the raw Open Packaging Conventions view: every item, content
type and relationship exactly as written, defects included. Document is
the lenient reader built on it. The verifier reads both, which is how it
can report the defects the reader works around.
Scope
Text extraction with paragraphs, line breaks, fields, tables, groups, charts and diagrams; Markdown output; speaker notes; comments, threaded comments included; sections; core and application properties; titles; pictures with their image parts; embedded objects; hyperlinks; alternative text; slide structure as a shape tree; a verifier with 72 clause-numbered rules; deck creation with titles, bullets, paragraphs, text boxes, tables, pictures and notes; Markdown to slides. Rendering slides to images is out of scope.
Where to go next
Installation covers the wheel, the binary and the crates; the Quickstart shows the CLI, Python and Rust doing real work. The guide then takes one task per chapter:
- Extracting text: slide text, reading order, slide selection, warnings for what was skipped.
- Notes, tables and structure: speaker notes, tables, the shape tree, titles and hyperlinks.
- Extracting images: each slide's pictures and the bytes of their image parts, read only when asked for.
- Verifying a deck: the clause-numbered rules, their codes and severities.
- Creating decks: titles, bullets, text boxes, tables, pictures and notes from the CLI and Rust.
- Markdown to slides: headings become slides, list
items bullets and
Notes:lines speaker notes.
The reference section holds the CLI reference, the Python API, the Rust crates, the verifier rules and the list of limitations.
pptxboss is dual-licensed under MIT or Apache-2.0, at your option.
Installation
Python
pip install pptxboss
Wheels are built for Linux x86_64 and macOS arm64 against the stable ABI for Python 3.12 and later. Other platforms build from the sdist with a Rust toolchain present.
Command line
cargo install pptxboss-cli
Coding agents get a bundled skill: pptxboss skill install writes it into ./.claude/skills/pptxboss/, and pptxboss skill install -g into the home directory.
Rust crates
[dependencies]
pptxboss-core = "0.2" # reading
pptxboss-check = "0.2" # verifying
pptxboss-write = "0.2" # creating
From source
git clone https://github.com/4thel00z/pptxboss
cd pptxboss
cargo test --workspace
python -m venv .venv && . .venv/bin/activate
pip install maturin pytest pyyaml
maturin develop && pytest
Quickstart
CLI
pptxboss info deck.pptx
pptxboss text --notes deck.pptx
pptxboss check deck.pptx
pptxboss create md out.pptx slides.md
Python
import pptxboss
doc = pptxboss.Document("deck.pptx")
for slide in doc:
print(slide.number, slide.title)
print(slide.text())
print(pptxboss.check("deck.pptx"))
Rust
use pptxboss_core::Document;
let doc = Document::open("deck.pptx")?;
for slide in doc.slides() {
let slide = slide?;
println!("{}: {}", slide.number(), slide.text());
}
Extracting text
What you get
Shapes in z-order, which ECMA-376 makes both the paint order and the reading order (19.3.1.45). Each shape's paragraphs come one per line; a line break inside a paragraph stays a line break; field text (slide numbers, dates) is included as cached. Tables give one line per row with tab-separated cells, merged-away cells omitted. Groups are descended. Date, footer, header and slide-number placeholders are left out by default, as are hidden shapes. Text is never inherited from a layout or master, so an empty placeholder is empty.
CLI
pptxboss text deck.pptx # slides separated by a blank line
pptxboss text --headings deck.pptx # --- slide N --- before each slide
pptxboss text --notes deck.pptx # speaker notes after each slide
pptxboss text --furniture deck.pptx # include date/footer/slide-number text
pptxboss text --skip-hidden deck.pptx # leave out slides marked hidden
pptxboss text --json deck.pptx # [{"number": 1, "text": "..."}, ...]
pptxboss text --slides 2-4,7 deck.pptx # only those slides, in that order
Anything the reader skipped is printed to stderr as warning: lines; the
exit code stays 0.
Python
doc = pptxboss.Document("deck.pptx")
doc.text() # whole deck
doc.text(notes=True, hidden_slides=False)
doc.slide_texts() # one string per slide, in parallel
doc.slide_texts(indexes=[2, 0]) # chosen slides, zero-based, in that order
text, warnings = doc.text_reporting() # what was skipped, one line each
text, report = doc.extract(indexes=[0]) # the same with an ExtractReport
doc[3].text(furniture=True)
doc[3].paragraphs() # every paragraph incl. table cells
Rust
use pptxboss_core::{Document, TextOptions};
let doc = Document::open("deck.pptx")?;
let (text, report) = doc.text_reporting(&TextOptions { notes: true, ..TextOptions::default() });
for warning in report.warnings() {
eprintln!("{warning}");
}
Document::map_slides runs a closure over every slide across the available
cores, or across the cap set with Document::with_threads; slide_texts
and text_reporting are built on it. map_slides_at, slide_texts_at
and markdown_at take a list of zero-based indices instead and keep the
written order, which is what --slides uses.
What is skipped, and how it is reported
A slide whose part is missing or malformed gives an empty string and
an entry in ExtractReport::failed_slides. Graphic frames whose type the
reader does not know (anything but tables, charts, diagrams and embedded
objects) are counted in unknown_graphics with their URI. Elements in
unknown namespaces inside a shape tree are skipped and counted. Hidden
slides left out because of the options are counted separately and do not
make the report incomplete. ExtractReport::is_complete is true when
nothing was dropped for a reason other than the options.
Encodings
Parts are UTF-8 or UTF-16; a UTF-16 part (byte order mark or <? in
either byte order) is transcoded before parsing, and the verifier notes it.
Text uses the _xHHHH_ escape convention for control characters, which
the reader decodes.
Charts and diagrams
A chart gives its title and a table of its cached data: a header of
series names, then one row per category with each series' value, cells
separated like table cells. Series with differing categories (scatter and
bubble charts) become one row each. A diagram (SmartArt) gives one line
per node in tree order. Both are on by default and switched off with
TextOptions::charts and TextOptions::diagrams (--no-charts,
--no-diagrams). Slide::charts and Slide::diagrams return the data
structured.
Markdown
Document::markdown and pptxboss markdown render the deck: a ##
heading per slide (the title, or Slide N), bullets with their levels,
plain paragraphs, bold, italic and links, GFM tables, 
images, a **Chart: title** table per chart, diagram outlines, embedded
objects as italic labels, and speaker notes and comments as block quotes
when asked. Slides are separated by a rule. Paragraphs that inherit their
bullet from the list style are bullets inside body placeholders and plain
text elsewhere.
Comments and alternative text
TextOptions::comments appends each slide's comments after its text and
notes, one line each: [comment] Author: text, replies indented as
[reply]. TextOptions::alt_text emits the descr of shapes that have
no text of their own (pictures, charts, diagrams, embedded objects). Both
are off by default so extracted text stays paragraph-for-paragraph
comparable with the slide content. Slide::comments returns the same
comments structured, with author, initials and date.
Notes, tables and structure
Speaker notes
Notes are stored in a notes slide part linked from the slide. The reader
takes the body placeholder of that part; when there is none, every text
shape other than the slide image and the date, footer and slide-number
placeholders.
pptxboss text --notes deck.pptx
doc[1].notes() # str or None
slide.notes_text()? // Option<String>
Tables
for rows in slide.tables():
for row in rows:
print(row) # cell texts, merged-away cells omitted
tables() gives cell text only. Spans and merges are on the table shape:
for shape in slide.shapes():
if shape.kind == "table":
for row in shape.table.rows:
[(cell.text, cell.grid_span, cell.row_span, cell.is_origin) for cell in row.cells]
In Rust a table is Content::Table(Table) with column_widths and rows
of Cell { body, grid_span, row_span, h_merge, v_merge }. Cell::is_origin
is false for cells merged into another.
The shape tree
for shape in slide.shapes():
shape.kind # text, picture, table, group, chart, diagram, ole, connector, content_part, unknown
shape.id, shape.name, shape.hidden, shape.placeholder, shape.placeholder_index
shape.text # for text shapes
shape.frame # (x, y, cx, cy) in EMU when the shape has a transform
shape.children # for groups
A text shape's paragraphs holds Paragraph objects with their runs,
each with bold, italic, size, hyperlink and the other run
properties as written; Slide.paragraphs() stays a flat list of strings.
In Rust, SlideContent::shapes holds the top-level Shape values and
SlideContent::walk() yields every shape in document order, descending
into groups. Shape::placeholder holds the placeholder kind and index;
Shape::is_title is true for title and centered-title placeholders.
Titles
Slide::title() returns the first title placeholder's text. Decks that
put their titles in plain text boxes have no title placeholder, and the
info listing shows (no title) for them.
Hyperlinks
Runs hold the relationship id of a click hyperlink in RunProps::hyperlink;
Slide::hyperlink_target(rel_id) resolves it to an external URL or an
internal part name.
Extracting images
Pictures are p:pic shapes whose a:blip names an image part through a
relationship. Embedded object previews count too.
for image in slide.images():
image.shape_id, image.rel_id, image.part, image.content_type, image.external
data = slide.image_bytes(image) # bytes of the image part
for image in slide.images()? {
let bytes = slide.image_bytes(&image)?;
}
Image bytes are read only when asked for, so a deck's media costs nothing
until then. Linked images (r:link) resolve to an external target
and have no bytes in the package. Embedded objects (p:oleObj) are listed
by Slide::objects with their progId and part, and Slide::object_bytes
reads them. Charts and diagrams are not rendered; Slide::charts and
Slide::diagrams give their text and data.
Verifying a deck
pptxboss check runs 72 structural rules from ECMA-376 Parts 1 and 2 over
a package: the ZIP container, part names, content types, relationships,
required parts, id ranges and uniqueness, XML well-formedness, namespace
consistency and core properties. There is no schema validation; the rules
are the constraints the specification states in prose.
CLI
pptxboss check deck.pptx
pptxboss check --quiet deck.pptx # errors only
pptxboss check --json deck.pptx
pptxboss check --no-crc deck.pptx # skip CRC-32 of XML parts
pptxboss rules # every rule
Exit code 0 when no errors were found, 1 when errors were found, 2 when the file could not be opened at all. Warnings alone exit 0.
Each finding prints as severity CODE part (location): message [clause].
Python
for finding in pptxboss.check("deck.pptx"):
finding.severity, finding.code, finding.clause, finding.part, finding.location, finding.message
report = pptxboss.check_report("deck.pptx") # .findings, .parts_checked, .truncated, .is_clean
pptxboss.rules()
Rust
use pptxboss_check::{check_path, CheckOptions};
let report = check_path("deck.pptx", &CheckOptions::default())?;
for finding in &report.findings {
println!("{finding}");
}
Severity policy
A violation of a "shall" is an error. A "should", an inconsistency PowerPoint itself tolerates, or a policy the specification leaves open is a warning. Anything merely notable is info. Decks saved by PowerPoint pass with no findings.
Limitations
The verifier reads every XML part once. It does not validate against the XSD schemas, does not check DrawingML value ranges beyond slide size and ids, and does not inspect chart, diagram or embedded object parts.
Creating decks
pptxboss-write builds decks with one master, four layouts (title, title
and content, title only, blank), a theme, and slides made of titles,
subtitles, bullet lists, paragraphs, text boxes, tables, pictures and
speaker notes. Output is deterministic: fixed timestamps, fixed part order,
ids in insertion order. Every deck it writes reads back through the core
and passes the verifier with no findings.
CLI
pptxboss create blank out.pptx --slides 3
pptxboss create text out.pptx --title "Hello" --bullet "one" --bullet "two" --notes "say hi"
pptxboss create md out.pptx slides.md
Rust
use pptxboss_write::{Metadata, Paragraph, Presentation, Rect, Slide, SlideSize};
let deck = Presentation::new()
.size(SlideSize::WIDESCREEN)
.metadata(Metadata { title: Some("Review".into()), ..Metadata::default() })
.slide(Slide::title_slide("Quarterly review", Some("Q3 2026")))
.slide(Slide::titled("Highlights").bullet("Revenue up").sub_bullet("in every region", 1).notes("Pause here"))
.slide(Slide::titled("Free form")
.text_box(Rect::inches(1.0, 1.5, 5.0, 1.0), vec![Paragraph::text("Bold claim").bold().size(28)])
.picture(std::fs::read("chart.png")?, Rect::inches(7.0, 1.5, 5.0, 3.0))
.table(Rect::inches(1.0, 4.0, 11.0, 2.0), vec![vec!["Region".into(), "Growth".into()], vec!["EMEA".into(), "12%".into()]], true));
deck.write_to("review.pptx")?;
The layout is inferred when not set: a title with body text uses Title and Content, a title alone uses Title Only, no title uses Blank.
Round trip
let bytes = deck.to_bytes()?;
let doc = pptxboss_core::Document::load(bytes.clone())?;
assert_eq!(doc.slide(1)?.text(), "Highlights\nRevenue up\nin every region");
assert!(pptxboss_check::check_bytes(bytes, &Default::default())?.findings.is_empty());
Limitations
Pictures must be PNG, JPEG, GIF, BMP or TIFF; their box is given explicitly. Tables use one built-in style and equal column widths. There is no chart, diagram or embedded object creation, and no editing of existing decks.
Markdown to slides
pptxboss create md out.pptx slides.md
cat slides.md | pptxboss create md out.pptx -
The converter is line-oriented:
| Markdown | Result |
|---|---|
# Title | a title slide; the next paragraph becomes its subtitle |
## Title, ### Title | a new content slide |
---, *** | a new untitled slide |
- item, * item, + item, 1. item, 1) item | a bullet; two spaces of indentation per level |
Notes: text | speaker notes for the current slide until a blank line |
| fenced code | plain paragraphs |
| any other line | a body paragraph without a bullet |
Inline **bold**, *italic*, _emphasis_ and backtick markers are
stripped; underscores inside words stay.
let deck = pptxboss_write::from_markdown(&markdown).font("Arial");
deck.write_to("out.pptx")?;
CLI reference
Every command takes a path to a .pptx (or .ppt, except check).
Warnings go to stderr as warning: lines; errors as error: lines. Exit code 1 means the input could not be read,
2 means bad usage, except for check, which uses 1 for findings at error
severity and 2 for an unreadable file.
--threads N, before or after the subcommand, caps the worker threads
used to parse slides; the default is every core, or the value of
PPTXBOSS_THREADS when that is set. --threads 1 keeps everything on the
calling thread, which is also the default for a legacy .ppt deck.
--slides RANGE on info, text and markdown picks slides by their
one-based number: a comma-separated list of numbers and low-high ranges
such as 1-3,7, printed in the written order, duplicates kept. info
still reports the whole deck's counts and lists only the picked slides;
text --json and --headings use the real slide numbers. A number the
deck does not have, or 0, is a usage error with exit code 2:
error: --slides 9: slide 9 out of range (deck has 3 slides).
pptxboss info FILE [--json] [--slides RANGE]
Format (pptx or ppt), slide count, presentation part, slide size in EMU
and inches with its declared type, master count, part count, the core
properties that are set
(title, subject, creator, modified by, created, modified, application),
one section: line per section with its slide numbers, then one line per
slide: number, title or (no title), and flags among hidden, notes,
pictures, tables, objects, comments. A slide that fails to parse
shows (unreadable: reason).
pptxboss text FILE [--slides RANGE] [--notes] [--comments] [--alt-text] [--no-charts] [--no-diagrams] [--furniture] [--hidden-shapes] [--skip-hidden] [--headings] [--json]
Slide text, slides separated by a blank line; empty slides are skipped
unless --headings or --json is given. --comments appends
[comment] Author: text lines (replies as [reply]) after a slide's text
and notes; --alt-text adds the alternative text of pictures and other
shapes that have no text. Chart data and diagram text are included unless
--no-charts or --no-diagrams is given.
pptxboss markdown FILE [--slides RANGE] [--notes] [--comments] [--skip-hidden] [--hidden-shapes] [--furniture] [--no-headings] [--no-images]
The deck as Markdown on stdout: a ## Title heading per slide, bullets,
paragraphs, GFM tables, images, chart tables and diagram outlines, slides
separated by a rule; --notes and --comments add block quotes. Warnings
go to stderr as for text.
pptxboss check FILE [--json] [--quiet] [--max-findings N] [--no-crc]
Findings sorted most severe first, then a summary line
FILE: ok|not ok: E error(s), W warning(s), P part(s) checked.
pptxboss rules [--json]
Every rule: severity, code, clause, summary.
pptxboss create blank OUT [--slides N] [--standard]
pptxboss create text OUT --title T [--bullet B]... [--notes N] [--standard]
pptxboss create md OUT INPUT [--standard] [--font F]
INPUT may be - for standard input.
pptxboss skill install [-g|--global], pptxboss skill show
Installs the bundled agent skill into ./.claude/skills/pptxboss/SKILL.md,
or with -g into ~/.claude/skills/pptxboss/SKILL.md, overwriting an
earlier install. show prints it.
Python reference
The .pyi stubs shipped in the package give the exact signatures.
Document(path=None, *, data=None, threads=None)
threads caps the workers whole-deck calls use; None or 0 means every
core (or PPTXBOSS_THREADS), 1 stays on the calling thread. Read back as
threads. format is "pptx" or "ppt"; a legacy deck has no
properties, sections, comments or charts, check() refuses it, and by
default it stays on the calling thread.
slide_count, path, presentation_part, slide_size, slide_size_type,
defects; len(), indexing with negative indexes, iteration; slide(i),
slides(), titles(), text(...), text_reporting(...), extract(...),
slide_texts(...), markdown(...), core_properties(),
app_properties(), sections(), comment_authors(), presentation(),
package(). Text methods take notes, furniture, hidden_shapes,
hidden_slides, alt_text, comments, charts, diagrams; markdown
takes headings, notes, comments, hidden_slides, hidden_shapes,
furniture, images. extract, slide_texts and markdown also take
indexes, a list of zero-based slide indexes (negatives count from the
end) read in the written order, which is what the CLI's --slides uses.
text_reporting returns (text, warnings); extract returns
(text, ExtractReport).
Slide
index, number, part, hidden, name, title, warnings;
text(furniture=, hidden_shapes=, alt_text=, charts=, diagrams=),
markdown(...), paragraphs(), notes(), comments(), tables(),
shapes(), images(), image_bytes(image), charts(), diagrams(),
embedded_objects(), object_bytes(object), hyperlink(rel_id),
notes_part(), comments_part(), layout_part().
ExtractReport
What extract skipped: failed_slides, failed_notes,
failed_comments, failed_frames as (index, error) pairs,
hidden_slides_skipped, unknown_graphics, unknown_graphic_uris,
unknown_elements; is_complete is true when nothing was dropped for a
reason other than the options, and warnings gives the same lines as
text_reporting.
Chart, ChartSeries, Diagram
A chart's shape_id, title, kinds (e.g. barChart), axis titles and
series, each with name, categories and values as written. A
diagram's shape_id and items, (level, text) tuples depth-first.
CoreProperties, AppProperties
Every field of docProps/core.xml as optional text (title, subject,
creator, keywords, description, last_modified_by, revision,
created, modified, last_printed, category, content_status,
language, identifier, version); docProps/app.xml as application,
app_version, company, manager, template, presentation_format,
the counts slides, notes, hidden_slides, words, paragraphs,
total_time, and titles_of_parts.
Section
name, slides (zero-based slide indexes).
Presentation, SlideId, MasterId
The parsed presentation part: slides and masters as (id, rel_id)
records, notes_master and handout_master relationship ids,
slide_size, slide_size_type, notes_size, first_slide_num, rtl.
DocumentDefects
located (relationship, content_type, conventional_path or
legacy_stream), unresolved_slides as (position, rel_id) pairs,
slides_recovered_from_rels.
CommentAuthor
id, name, initials.
Package
The raw package, from Document.package() or
Package(path=None, *, data=None): parts() as Part records (name,
content_type, size, compressed_size), has(name),
content_type(name), read(name, raw=False) (UTF-16 XML comes back as
UTF-8 unless raw), rels(source="/") as Relationship records (id,
type, target, external), resolve(source, rel_id),
content_types() with defaults and overrides dicts, and defects, a
PackageDefects record: content_types_missing, content_types_case,
content_types_error, content_types_unreadable, invalid_names,
collisions, derivable, directories, incomplete_pieces.
Comment
author, initials, date, text, reply.
EmbeddedObject
shape_id, prog_id, rel_id, part, content_type, external.
Shape
id, name, kind, hidden, placeholder, placeholder_index, text,
description, hyperlink, frame, rotation, image_rel, rows,
table, paragraphs, children, is_title. rows is cell text only;
table is the grid with spans and merges; paragraphs is the text with
its runs.
Table, Row, Cell
A table's column_widths (EMU) and rows; a row's height and cells;
a cell's text, paragraphs, grid_span, row_span, h_merge,
v_merge and is_origin, false for a cell merged into another.
Paragraph, Run
A paragraph's text, level, bullet (inherited, none, char,
auto_number, picture), bullet_char, number_scheme, number_start
and runs. A run's kind (text, line_break, field), field,
text, and the formatting as written, None where not set: bold,
italic, underline, strike, size in hundredths of a point,
hyperlink (a relationship id for Slide.hyperlink), lang, typeface.
Image
shape_id, rel_id, part, content_type, external.
check(path=None, *, data=None, max_findings=1000, xml_well_formed=True, verify_crc=True) -> list[Finding]
check_report(...) -> CheckReport
Same arguments; returns findings, parts_checked, truncated (true
when max_findings cut the list short), errors and warnings counts,
is_clean and codes.
Finding
code, severity, clause, part, location, message.
rules() -> list[Rule]
code, severity, clause, summary.
PptxError
Raised for any processing error. ValueError for bad arguments,
IndexError for slide indexes out of range.
Threading
Documents and slides are frozen and usable from any thread. Calls that
read the archive release the GIL and run on a private Document rebuilt
over the same archive, so calls from different threads run in parallel.
Whole-deck calls (slides(), titles(), text(), slide_texts(),
text_reporting()) use up to the thread count set at construction.
Rust reference
| Crate | Start here |
|---|---|
pptxboss-core | Document::open, Document::slide, Slide::text, Document::map_slides, Document::markdown, Document::core_properties, Document::sections, Slide::comments, Slide::charts, Slide::diagrams, Slide::objects; Package for the raw view; zip::Archive, inflate, xml::Reader, opc, model, chart, diagram, markdown; cfb::Compound and ppt::LegacyDeck behind .ppt files (Document::legacy) |
pptxboss-check | check, check_path, check_bytes, rules, Finding, Severity, CheckOptions |
pptxboss-write | Presentation, Slide, Paragraph, Rect, SlideSize, Layout, from_markdown |
pptxboss-cli | the binary |
Document is single-threaded; Document::seed() gives a Send + Sync
handle from which any thread rebuilds a Document over the same archive
with private caches. Package works the same way. Document::map_slides
spreads slides over every core unless Document::with_threads (or
set_threads, or the PPTXBOSS_THREADS variable) caps the workers; the
seed keeps the cap. A legacy .ppt deck stays on the calling thread
unless a cap is set explicitly, because spreading them costs more than
it saves.
Every error type is a thiserror enum per crate: pptxboss_core::Error,
pptxboss_write::Error.
Verifier rules
pptxboss rules prints the current list with severities and clauses. The
codes are stable. Families:
| Prefix | Covers | Clauses |
|---|---|---|
ZIP | container records, compression methods, flags, header consistency, CRC-32, duplicate items, directories | Part 2 7.3 and Annex B |
OPC | part name grammar, equivalence, derivability, the content types stream | Part 2 6.2.2, 7.2.3, 7.3.7 |
CTY | Default and Override elements, media type syntax, every part typed | Part 2 6.2.3, 7.2.3 |
REL | Relationships parts, ids, targets, reachability | Part 2 6.5 |
PKG | the package relationships: one presentation, core properties, thumbnails | Part 1 13.3.6, 15.2; Part 2 8.2 |
XML | well-formedness, encodings, Strict versus Transitional mixing | Part 2 6.2.5; Part 4 7 |
PML | presentation and slide-family structure, id ranges and uniqueness, required relationships | Part 1 13.3, 19.2, 19.3, 19.7, 20.1.2.2.8 |
CPR | core properties | Part 2 8 |
Limitations
- Password-protected files (encrypted packages, encrypted
.ppt) are detected and refused, not decrypted. - Legacy
.pptdecks: text, titles, notes, hidden flags, slide size and pictures are read, and the file is read whole into memory; tables, charts, comments and document properties of the binary format are not read, and the verifier does not cover it. PowerPoint 95 files are refused. - Charts give their cached title, series, categories and values; nothing
is recomputed from the embedded workbook. Extended charts (
cx:) insidemc:AlternateContentfall back to their picture, since the reader does not claim to understand their versioned namespaces. Embedded objects are listed with their bytes, not interpreted. - Text formatting beyond bold, italic, underline, strike, size, language, typeface and hyperlinks is not modelled.
- Placeholder inheritance of formatting from layouts and masters is not resolved; text is never inherited by design.
- No rendering of slides to images.
- The verifier does not validate against the XSD schemas.
- The writer embeds PNG, JPEG, GIF, BMP and TIFF pictures only, creates no charts or diagrams, and does not edit existing decks.