Documentation
¶
Overview ¶
Package pptx is Eden Press's hand-rolled, stdlib-only (archive/zip + encoding/xml) OOXML PresentationML writer: it emits editable-text-box .pptx files directly from chase/model.Document, with NO headless browser and NO third-party OOXML library (unioffice and its forks were evaluated and rejected -- AGPLv3 licensing / commercial license-key + network check-in; see 06-RESEARCH.md's re-confirmed decision gate). This package is a new top-level export surface and is never imported by press/, chase/, or profiles/ -- it consumes chase/model.Document as an output-only consumer, keeping the no-chromedp render-path boundary those packages enforce completely untouched.
notes.go maps chase/model.Section.Notes -> the speaker-notes OPC parts (06-RESEARCH Pattern 1 "notes slide" + Pattern 4 notes-slide shape): ppt/notesSlides/notesSlideN.xml (one <a:p> per note string, in the mandatory <p:ph type="body"> placeholder shape) plus the once-per-deck ppt/notesMasters/notesMaster1.xml, and the .rels wiring that closes the slide -> notesSlide -> notesMaster -> theme relationship graph (06-RESEARCH Code Examples / Pattern 1). Notes parts are CONDITIONAL: buildNotesSlide is emitted only for a Section that HAS notes; notesMaster1 appears at most once, iff ANY section in the deck has notes. This file extends shapes.go's escaping helper and parts_static.go's rels/theme plumbing rather than duplicating them (06-04 key-link).
parts_static.go builds every INVARIANT part in the minimal OPC part graph (06-RESEARCH Pattern 1): package/presentation relationships, docProps, presProps/viewProps/tableStyles, the theme, and the slide master/layout pair -- everything except the per-slide content itself (which 06-04 makes model-driven; this TRD's own trivial slide1.xml lives in openable_test.go, deliberately test-only scaffolding).
pptx.go is the PUBLIC surface of the whole convert/pptx objective: ToPPTX composes 06-01 (Section.Blocks -> editable body text), 06-02 (EMU placement + grouped-shape transform), 06-03 (deterministic OPC packager + static part scaffold), and 06-05 (notes.go: Section.Notes -> notesSlideN.xml) into a real, editable-text-box .pptx built DIRECTLY from the chase/model docmodel -- zero rendered HTML, zero chromedp. Each Section becomes one ppt/slides/slideN.xml (slide.go), wired into presentation.xml's sldIdLst, presentation.xml.rels, and [Content_Types].xml Overrides three-fold per slide; a Section WITH notes additionally gets a ppt/notesSlides/notesSlideN.xml (+ rels -> slideN + notesMaster1) and the slide's own rels gain a -> notesSlideN entry, with the once-per-deck ppt/notesMasters/notesMaster1.xml emitted iff any section has notes. Assembled by 06-03's fixed-timestamp/zip.Store packager, in a FIXED order (determinism).
shapes.go is the SINGLE place a Block-kind -> DrawingML mapping lives (06-04 key-link): the <p:sp> editable text-box builder, the <a:p> paragraph/list builder, and the <p:grpSp> identity group builder that slide.go composes and that 06-05 (speaker notes) extends rather than duplicates. Every user string (title/paragraph/list-item text, shape name) is routed through xml.EscapeText so a `<`/`&`/`"` in source content can never corrupt the OOXML (06-RESEARCH escaping trap); all positions/extents are 06-02 EMU values and font sizes are 06-02 centipoints (a:rPr/@sz), never EMU.
slide.go turns one chase/model.Section into one ppt/slides/slideN.xml (+ its .rels -> slideLayout1): the title comes from the Section's lowest-Level Outline heading (06-01), the body from Section.Blocks in document order (paragraph -> body run, list -> bulleted/numbered paragraphs, heading -> heading text box, code/math -> raw-text body run). Body shapes are wrapped in a single identity <p:grpSp> (06-02) so every content-bearing slide carries the criterion-3 grouped-shape case. It reads Section.Blocks directly -- never HTML, never chromedp -- and composes shapes via shapes.go's builders.
Index ¶
- Variables
- func Centimeters(v float64) int64
- func Centipoints(pt float64) int
- func Inches(v float64) int64
- func Millimeters(v float64) int64
- func Points(v float64) int64
- func ToPPTX(doc *model.Document, opts Options) ([]byte, error)
- type Extent
- type GroupTransform
- type Options
- type Point
- type SlideSize
Constants ¶
This section is empty.
Variables ¶
var ( // SlideSize16x9 is the 16:9 widescreen slide size (13.333in x 7.5in). SlideSize16x9 = SlideSize{CX: 12192000, CY: 6858000, Type: "screen16x9"} // SlideSize4x3 is the 4:3 standard slide size (10in x 7.5in). SlideSize4x3 = SlideSize{CX: 9144000, CY: 6858000, Type: "screen4x3"} // NotesSize is the portrait speaker-notes page size (<p:notesSz>); it // carries no type attribute in the schema, so Type is left "". NotesSize = SlideSize{CX: 6858000, CY: 9144000} )
Authoritative slide/notes-size constants, matching ECMA-376's <p:sldSz> and <p:notesSz> elements exactly. Every later TRD that emits a slide or notes size must source it from these values -- never recompute or hardcode the cx/cy pair again.
Functions ¶
func Centimeters ¶
Centimeters converts a measurement in centimeters to EMU.
func Centipoints ¶
Centipoints converts a font size in points to CENTIPOINTS (hundredths of a point) -- the unit DrawingML's a:rPr/@sz attribute is expressed in.
THIS IS NOT EMU. Font size is centipoints; positions and extents are EMU. Mixing the two units is the classic PPTX-writer bug this helper exists to prevent: Centipoints(44) == 4400 (sz="4400" for a 44pt run), never an EMU value.
func Inches ¶
Inches converts a measurement in inches to EMU. Whole-inch inputs are exact (Inches(1) == 914400); fractional inputs use the documented round-to-nearest rule.
func Millimeters ¶
Millimeters converts a measurement in millimeters to EMU.
func Points ¶
Points converts a measurement in points to EMU. 72 points == 1 inch == 914400 EMU is a useful cross-check (Points(72) == Inches(1)), asserted as its own test rather than derived as a computed dependency.
func ToPPTX ¶
ToPPTX renders a chase/model.Document to an editable .pptx byte slice, DIRECTLY from the docmodel -- no HTML parsing, no browser. Each doc.Section becomes one slide whose title (its lowest-Level Outline heading) and body (its Blocks: paragraphs, lists, headings) are real, editable <p:sp> text-box shapes with <a:t> runs (never a screenshot image). Output is deterministic: calling ToPPTX twice with the same document and Options yields byte-identical bytes.
Types ¶
type Extent ¶
type Extent struct {
CX, CY int64
}
Extent is a (cx, cy) EMU width/height pair, matching a DrawingML <a:ext>.
type GroupTransform ¶
type GroupTransform struct {
// Off/Ext are the group SHAPE's own position/extent in slide-EMU space
// (the group's <a:off>/<a:ext>).
Off Point
Ext Extent
// ChOff/ChExt are the group's CHILD coordinate-space origin/size (the
// group's <a:chOff>/<a:chExt>).
ChOff Point
ChExt Extent
}
GroupTransform implements the DrawingML grouped-shape chOff/chExt child-to-slide coordinate mapping (ECMA-376 CT_GroupTransform2D, 06- RESEARCH Pattern 4): a child shape's (off, ext), expressed in the GROUP's own child coordinate space (defined by ChOff/ChExt), maps into SLIDE-EMU coordinate space via:
scaleX = Ext.CX / ChExt.CX scaleY = Ext.CY / ChExt.CY slideX = Off.X + (child.off.X - ChOff.X) * scaleX slideY = Off.Y + (child.off.Y - ChOff.Y) * scaleY slideCX = child.ext.CX * scaleX slideCY = child.ext.CY * scaleY
This TRD only PROVES the formula; it emits no XML. 06-04 is the consumer that will build a GroupTransform from a real <p:grpSpPr>'s <a:xfrm> off/ext/chOff/chExt and call MapChild for each child shape.
func IdentityGroupTransform ¶
func IdentityGroupTransform(off Point, ext Extent) GroupTransform
IdentityGroupTransform builds the v1 safe-simplification group transform (06-RESEARCH Pattern 4 / Pitfall 1): ChOff == off and ChExt == ext, so MapChild always scales by 1 and translates by 0 -- a child's own off/ext ARE already literal slide-EMU coordinates, unchanged. This is the case 06-04 uses for its first grouped shape; only construct a non-identity GroupTransform directly once a real, non-1:1 nested group is needed.
func (GroupTransform) MapChild ¶
func (t GroupTransform) MapChild(off Point, ext Extent) (Point, Extent)
MapChild maps a child shape's (off, ext), expressed in this group's child coordinate space (ChOff/ChExt), into slide-EMU coordinate space.
Order of operations matters and mirrors CT_GroupTransform2D exactly: subtract ChOff from the child's raw offset FIRST, THEN scale, THEN add Off. Applying scale to the raw child offset before subtracting ChOff is the classic transform bug this ordering avoids.
type Options ¶
type Options struct {
// SlideSize selects the deck aspect ratio. The zero value defaults to
// SlideSize16x9 (widescreen); pass SlideSize4x3 for the 4:3 standard size.
SlideSize SlideSize
}
Options configures ToPPTX.