diff --git a/docs/DeveloperTroubleshooting.md b/docs/DeveloperTroubleshooting.md index afa5d2e4..04b6c073 100644 --- a/docs/DeveloperTroubleshooting.md +++ b/docs/DeveloperTroubleshooting.md @@ -80,5 +80,62 @@ Problem: The zip entry paths mut not contain a `../` part. +-----------------+ | word/styles.xml | > shared across all +-----------------+ +``` + +## OOXML Presentation Model + +- Entry point is the same `.rels` file and main document is usually `ppt/_rels/presentation.xml` +- Main document references both `slide` and `slideMaster` and `theme` and `notesMaster` + - SlideMaster references `slideLayout` (and `theme`) pages + - Slide references `slideLayout` pages and `notesSlide` + - Theme page has no references + - SlideLayout references `slideMaster` pages. Note, there is a circular reference there! + - NotesSlide references `NotesMaster` + - NotesMaster references back to Theme. + +### SlideMaster + +> The master slide is the template upon which presentation slides are built. It specifies the shapes and objects as placeholders for content on presentation slides, as well as the formatting of the content within the placeholders. Of course the content and formatting specified on a master slide can be altered by layout slides and the presentation slides themselves, but absent such overrides, the master slide establishes the overall look and feel of the presentation. [Source](http://officeopenxml.com/prSlideMaster.php) +### SlideLayout + +> A slide layout is essentially a template design which can be applied to one or more slides, defining the default appearance and positioning of objects on the slide. It "sits" on top of the master slide, acting as an override to alter or supplement information provided on the master slide. When applied to a slide, all corresponding content within objects on the slide is mapped to the slide layout placeholders. [Source](http://officeopenxml.com/prSlideLayout.php) + + +``` + ┌─────────────┐ + │ _rels/.rels │ + └──────┬──────┘ + │ + ▼ + ┌─────────────────────┐ +┌───────────────────────────────┤/ppt/presentation.xml├───────────────────────────┐ +│ └─────────┬───────────┘ │ +│ │ │ +│ ▼ │ +│ ┌────────────────────────┐ │ +│ │/ppt/slides/slide{N}.xml│ │ +│ └─────┬──────────────────┘ │ +│ │ ▲ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌────────────────────────────────────┐ ┌─────────────────┐ │ +│ │/ppt/slideLayouts/slideLayout{N}.xml│ │notesSlide{N}.xml│ │ +│ └────────────────────────────────────┘ └─────┬───────────┘ │ +│ ▲ │ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌────────────────────────────────────┐ ┌──────────────────────────────────┐ │ +└─►│/ppt/slideMasters/slideMaster{N}.xml│ │/ppt/notesMasters/notesMaster1.xml│◄─┤ + └───────────────────────────────┬────┘ └─────┬────────────────────────────┘ │ + │ │ │ + │ ▼ │ + │ ┌────────────┐ │ + └──────►│theme{N}.xml│◄────────────────────────┤ + └────────────┘ │ + ▲ │ + │ │ + ┌───────┴────────────────────────────┐ │ + │/handoutMasters/handoutMaster{N}.xml│◄─┘ + └────────────────────────────────────┘ ``` diff --git a/src/stencil/model.clj b/src/stencil/model.clj index 5878d6b8..ebf7899b 100644 --- a/src/stencil/model.clj +++ b/src/stencil/model.clj @@ -24,6 +24,21 @@ (cleanup/process) (select-keys [:variables :dynamic? :executable :fragments])))) +(defn- ->submodel [dir f] + {::path (fs/unix-path (fs/unroll f)) + :source-file (file dir f) + :executable (->exec (file dir f)) + :relations (relations/->rels dir f)}) + +(defn- assoc-slide-layouts-notes [main-document dir] + (->> (for [hf (:headers+footers main-document) + :when (:relations hf) + target (relations/targets-by-type (:relations hf) + #{relations/rel-type-slide-layout relations/rel-type-notes-slide})] + (->submodel dir (file (fs/parent-file (file (::path hf))) target))) + (doall) + (assoc main-document ::slide-layouts))) + (defn load-template-model [dir, options-map] (assert (fs/exists? dir)) (assert (fs/directory? dir)) @@ -41,12 +56,9 @@ :executable (->exec (file dir main-document)) :relations main-document-rels :headers+footers (doall - (for [t (relations/targets-by-type main-document-rels relations/extra-relations) - :let [f (file (fs/parent-file (file main-document)) t)]] - {::path (fs/unix-path f) - :source-file (file dir f) - :executable (->exec (file dir f)) - :relations (relations/->rels dir f)}))} + (for [t (relations/targets-by-type main-document-rels relations/extra-relations)] + (->submodel dir (fs/unroll (file (fs/parent-file (file main-document)) t)))))} + (assoc-slide-layouts-notes dir) (style/assoc-style dir) (numbering/assoc-numbering dir))})) @@ -93,10 +105,11 @@ (assoc :result result)))))] (-> template-model (update-in [:main :headers+footers] (partial mapv evaluate)) + (update-in [:main ::slide-layouts] (partial mapv evaluate)) (update :main evaluate)))))))) (defn- model-seq [model] - (let [model-keys [:relations :headers+footers :main :style :content-types :fragments ::numbering :result]] + (let [model-keys [:relations :headers+footers :main :style :content-types :fragments ::numbering :result ::slide-layouts]] (tree-seq map? (fn [node] (flatten (keep node model-keys))) model))) @@ -161,7 +174,7 @@ ;; TODO: we could speed this up! (if-let [f (attr-mappers (:tag xml-tree))] (update-in xml-tree [:attrs ooxml/val] f) - (assoc xml-tree :content (mapv (partial xml-map-attrs attr-mappers) (:content xml-tree)))) + (assoc xml-tree :content (mapv (partial xml-map-attrs attr-mappers) (:content xml-tree)))) xml-tree)) ; And therefore: diff --git a/src/stencil/model/relations.clj b/src/stencil/model/relations.clj index 0835fa25..9e01d0f1 100644 --- a/src/stencil/model/relations.clj +++ b/src/stencil/model/relations.clj @@ -31,12 +31,28 @@ (def rel-type-header "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header") +;; PPTX + (def rel-type-slide "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") -(def extra-relations - #{rel-type-footer rel-type-header rel-type-slide}) +(def rel-type-slide-master + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster") + +(def rel-type-slide-layout + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") + +(def rel-type-theme + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme") +(def rel-type-notes-slide + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") + +(def rel-type-notes-master + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster") + +(def extra-relations + #{rel-type-footer rel-type-header rel-type-slide rel-type-slide-master rel-type-notes-master}) (defn- parse [rel-file] (with-open [reader (io/input-stream (file rel-file))] @@ -53,7 +69,7 @@ (defn ->rels [^java.io.File dir f] (let [rels-path (if f - (unix-path (file (fs/parent-file (file f)) "_rels" (str (.getName (file f)) ".rels"))) + (unix-path (fs/unroll (file (fs/parent-file (file f)) "_rels" (str (.getName (file f)) ".rels")))) (unix-path (file "_rels" ".rels"))) rels-file (file dir rels-path)] (when (fs/exists? rels-file) diff --git a/src/stencil/ooxml.clj b/src/stencil/ooxml.clj index 37126787..db0ce296 100644 --- a/src/stencil/ooxml.clj +++ b/src/stencil/ooxml.clj @@ -118,7 +118,13 @@ "http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" "xr2" "http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" "xr3" "http://schemas.microsoft.com/office/spreadsheetml/2016/revision6" "xr6" - "http://schemas.microsoft.com/office/spreadsheetml/2016/revision10" "xr10"}) + "http://schemas.microsoft.com/office/spreadsheetml/2016/revision10" "xr10" + ;additional aliases from PowerPoint + "http://schemas.openxmlformats.org/drawingml/2006/main" "a" + "http://schemas.openxmlformats.org/presentationml/2006/main" "p" + "http://schemas.microsoft.com/office/powerpoint/2010/main" "p14" + "http://schemas.microsoft.com/office/powerpoint/2012/main" "p15" + }) ;; drawing, binary large image or picture (def blip :xmlns.http%3A%2F%2Fschemas.openxmlformats.org%2Fdrawingml%2F2006%2Fmain/blip) diff --git a/test-resources/presentation/presentation.pptx b/test-resources/presentation/presentation.pptx new file mode 100644 index 00000000..630c4527 Binary files /dev/null and b/test-resources/presentation/presentation.pptx differ diff --git a/test/stencil/model_test.clj b/test/stencil/model_test.clj index 08698de5..7c6d3c52 100644 --- a/test/stencil/model_test.clj +++ b/test/stencil/model_test.clj @@ -30,6 +30,13 @@ )) +(deftest test-load-template-model-presentation + (with-open [template (api/prepare "test-resources/presentation/presentation.pptx")] + (let [model (datafy template) + slide-layouts (:stencil.model/slide-layouts (:main model))] + (is (seq slide-layouts)) + (is (= 26 (count slide-layouts)))))) + (defn- debug-model [model] (-> model (assoc-in [:content-types] :CT)