generate files from templates
This commit is contained in:
parent
e39b9c15c2
commit
0fad458858
9 changed files with 176 additions and 106 deletions
165
lib/pptx/pptx.go
165
lib/pptx/pptx.go
|
|
@ -6,7 +6,7 @@ import (
|
|||
_ "embed"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// Measurements in OOXML are made in English Metric Units (EMUs) where 1 inch = 914,400 EMUs
|
||||
|
|
@ -41,111 +41,142 @@ func copyPptxTemplateTo(w *zip.Writer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func addFile(zipFile *zip.Writer, filePath, content string) error {
|
||||
w, err := zipFile.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Write([]byte(content))
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:embed templates/slide.xml.rels
|
||||
var RELS_SLIDE_XML string
|
||||
|
||||
func getRelsSlideXml(imageID string) string {
|
||||
return fmt.Sprintf(RELS_SLIDE_XML, imageID, imageID)
|
||||
type RelsSlideXmlContent struct {
|
||||
FileName string
|
||||
RelationshipID string
|
||||
}
|
||||
|
||||
//go:embed templates/slide.xml
|
||||
var SLIDE_XML string
|
||||
|
||||
func getSlideXml(boardPath []string, imageID string, top, left, width, height int) string {
|
||||
var slideTitle string
|
||||
type SlideXmlContent struct {
|
||||
Title string
|
||||
TitlePrefix string
|
||||
Description string
|
||||
HeaderHeight int
|
||||
ImageID string
|
||||
ImageLeft int
|
||||
ImageTop int
|
||||
ImageWidth int
|
||||
ImageHeight int
|
||||
}
|
||||
|
||||
func getSlideXmlContent(imageID string, slide *Slide) SlideXmlContent {
|
||||
boardPath := slide.BoardPath
|
||||
boardName := boardPath[len(boardPath)-1]
|
||||
prefixPath := boardPath[:len(boardPath)-1]
|
||||
var prefix string
|
||||
if len(prefixPath) > 0 {
|
||||
prefix := strings.Join(prefixPath, " / ") + " / "
|
||||
slideTitle = fmt.Sprintf(`<a:r><a:t>%s</a:t></a:r><a:r><a:rPr b="1" /><a:t>%s</a:t></a:r>`, prefix, boardName)
|
||||
} else {
|
||||
slideTitle = fmt.Sprintf(`<a:r><a:rPr b="1" /><a:t>%s</a:t></a:r>`, boardName)
|
||||
prefix = strings.Join(prefixPath, " / ") + " / "
|
||||
}
|
||||
return SlideXmlContent{
|
||||
Title: boardName,
|
||||
TitlePrefix: prefix,
|
||||
Description: strings.Join(boardPath, " / "),
|
||||
HeaderHeight: HEADER_HEIGHT,
|
||||
ImageID: imageID,
|
||||
ImageLeft: slide.ImageLeft,
|
||||
ImageTop: slide.ImageTop + HEADER_HEIGHT,
|
||||
ImageWidth: slide.ImageWidth,
|
||||
ImageHeight: slide.ImageHeight,
|
||||
}
|
||||
slideDescription := strings.Join(boardPath, " / ")
|
||||
top += HEADER_HEIGHT
|
||||
return fmt.Sprintf(SLIDE_XML, slideDescription, slideDescription, imageID, left, top, width, height, slideDescription, HEADER_HEIGHT, slideTitle)
|
||||
}
|
||||
|
||||
//go:embed templates/rels_presentation.xml
|
||||
var RELS_PRESENTATION_XML string
|
||||
|
||||
func getPresentationXmlRels(slideFileNames []string) string {
|
||||
var builder strings.Builder
|
||||
type RelsPresentationSlideXmlContent struct {
|
||||
RelationshipID string
|
||||
FileName string
|
||||
}
|
||||
|
||||
type RelsPresentationXmlContent struct {
|
||||
Slides []RelsPresentationSlideXmlContent
|
||||
}
|
||||
|
||||
func getRelsPresentationXmlContent(slideFileNames []string) RelsPresentationXmlContent {
|
||||
var content RelsPresentationXmlContent
|
||||
for _, name := range slideFileNames {
|
||||
builder.WriteString(fmt.Sprintf(
|
||||
`<Relationship Id="%s" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/%s.xml" />`, name, name,
|
||||
))
|
||||
content.Slides = append(content.Slides, RelsPresentationSlideXmlContent{
|
||||
RelationshipID: name,
|
||||
FileName: name,
|
||||
})
|
||||
}
|
||||
|
||||
return fmt.Sprintf(RELS_PRESENTATION_XML, builder.String())
|
||||
return content
|
||||
}
|
||||
|
||||
//go:embed templates/content_types.xml
|
||||
var CONTENT_TYPES_XML string
|
||||
|
||||
func getContentTypesXml(slideFileNames []string) string {
|
||||
var builder strings.Builder
|
||||
for _, name := range slideFileNames {
|
||||
builder.WriteString(fmt.Sprintf(
|
||||
`<Override PartName="/ppt/slides/%s.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml" />`, name,
|
||||
))
|
||||
}
|
||||
|
||||
return fmt.Sprintf(CONTENT_TYPES_XML, builder.String())
|
||||
type ContentTypesXmlContent struct {
|
||||
FileNames []string
|
||||
}
|
||||
|
||||
//go:embed templates/presentation.xml
|
||||
var PRESENTATION_XML string
|
||||
|
||||
func getPresentationXml(slideFileNames []string) string {
|
||||
var builder strings.Builder
|
||||
for i, name := range slideFileNames {
|
||||
// in the exported presentation, the first slide ID was 256, so keeping it here for compatibility
|
||||
builder.WriteString(fmt.Sprintf(`<p:sldId id="%d" r:id="%s" />`, 256+i, name))
|
||||
type PresentationSlideXmlContent struct {
|
||||
ID int
|
||||
RelationshipID string
|
||||
}
|
||||
|
||||
type PresentationXmlContent struct {
|
||||
SlideWidth int
|
||||
SlideHeight int
|
||||
Slides []PresentationSlideXmlContent
|
||||
}
|
||||
|
||||
func getPresentationXmlContent(slideFileNames []string) PresentationXmlContent {
|
||||
content := PresentationXmlContent{
|
||||
SlideWidth: SLIDE_WIDTH,
|
||||
SlideHeight: SLIDE_HEIGHT,
|
||||
}
|
||||
return fmt.Sprintf(PRESENTATION_XML, builder.String(), SLIDE_WIDTH, SLIDE_HEIGHT)
|
||||
for i, name := range slideFileNames {
|
||||
content.Slides = append(content.Slides, PresentationSlideXmlContent{
|
||||
// in the exported presentation, the first slide ID was 256, so keeping it here for compatibility
|
||||
ID: 256 + i,
|
||||
RelationshipID: name,
|
||||
})
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
//go:embed templates/core.xml
|
||||
var CORE_XML string
|
||||
|
||||
func getCoreXml(title, subject, description, creator string) string {
|
||||
dateTime := time.Now().Format(time.RFC3339)
|
||||
return fmt.Sprintf(
|
||||
CORE_XML,
|
||||
title,
|
||||
subject,
|
||||
creator,
|
||||
description,
|
||||
creator,
|
||||
dateTime,
|
||||
dateTime,
|
||||
)
|
||||
type CoreXmlContent struct {
|
||||
Title string
|
||||
Subject string
|
||||
Creator string
|
||||
Description string
|
||||
LastModifiedBy string
|
||||
Created string
|
||||
Modified string
|
||||
}
|
||||
|
||||
//go:embed templates/app.xml
|
||||
var APP_XML string
|
||||
|
||||
func getAppXml(slides []*Slide, d2version string) string {
|
||||
var builder strings.Builder
|
||||
for _, slide := range slides {
|
||||
builder.WriteString(fmt.Sprintf(`<vt:lpstr>%s</vt:lpstr>`, strings.Join(slide.BoardPath, "/")))
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
APP_XML,
|
||||
len(slides),
|
||||
len(slides),
|
||||
len(slides)+3, // number of entries, len(slides) + Office Theme + 2 Fonts
|
||||
builder.String(),
|
||||
d2version,
|
||||
)
|
||||
type AppXmlContent struct {
|
||||
SlideCount int
|
||||
TitlesOfPartsCount int
|
||||
Titles []string
|
||||
D2Version string
|
||||
}
|
||||
|
||||
func addFileFromTemplate(zipFile *zip.Writer, filePath, templateContent string, templateData interface{}) error {
|
||||
w, err := zipFile.Create(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New(filePath).Parse(templateContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(w, templateData)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import (
|
|||
"fmt"
|
||||
"image/png"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Presentation struct {
|
||||
|
|
@ -142,42 +144,61 @@ func (p *Presentation) SaveTo(filePath string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = addFile(zipWriter, fmt.Sprintf("ppt/slides/_rels/%s.xml.rels", slideFileName), getRelsSlideXml(imageID))
|
||||
err = addFileFromTemplate(zipWriter, fmt.Sprintf("ppt/slides/_rels/%s.xml.rels", slideFileName), RELS_SLIDE_XML, RelsSlideXmlContent{
|
||||
FileName: imageID,
|
||||
RelationshipID: imageID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = addFile(
|
||||
zipWriter,
|
||||
fmt.Sprintf("ppt/slides/%s.xml", slideFileName),
|
||||
getSlideXml(slide.BoardPath, imageID, slide.ImageTop, slide.ImageLeft, slide.ImageWidth, slide.ImageHeight),
|
||||
)
|
||||
err = addFileFromTemplate(zipWriter, fmt.Sprintf("ppt/slides/%s.xml", slideFileName), SLIDE_XML, getSlideXmlContent(imageID, slide))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = addFile(zipWriter, "[Content_Types].xml", getContentTypesXml(slideFileNames))
|
||||
err = addFileFromTemplate(zipWriter, "[Content_Types].xml", CONTENT_TYPES_XML, ContentTypesXmlContent{
|
||||
FileNames: slideFileNames,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = addFile(zipWriter, "ppt/_rels/presentation.xml.rels", getPresentationXmlRels(slideFileNames))
|
||||
err = addFileFromTemplate(zipWriter, "ppt/_rels/presentation.xml.rels", RELS_PRESENTATION_XML, getRelsPresentationXmlContent(slideFileNames))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = addFile(zipWriter, "ppt/presentation.xml", getPresentationXml(slideFileNames))
|
||||
err = addFileFromTemplate(zipWriter, "ppt/presentation.xml", PRESENTATION_XML, getPresentationXmlContent(slideFileNames))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = addFile(zipWriter, "docProps/core.xml", getCoreXml(p.Title, p.Subject, p.Description, p.Creator))
|
||||
dateTime := time.Now().Format(time.RFC3339)
|
||||
err = addFileFromTemplate(zipWriter, "docProps/core.xml", CORE_XML, CoreXmlContent{
|
||||
Creator: p.Creator,
|
||||
Subject: p.Subject,
|
||||
Description: p.Description,
|
||||
LastModifiedBy: p.Creator,
|
||||
Title: p.Title,
|
||||
Created: dateTime,
|
||||
Modified: dateTime,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = addFile(zipWriter, "docProps/app.xml", getAppXml(p.Slides, p.D2Version))
|
||||
titles := make([]string, 0, len(p.Slides))
|
||||
for _, slide := range p.Slides {
|
||||
titles = append(titles, strings.Join(slide.BoardPath, "/"))
|
||||
}
|
||||
err = addFileFromTemplate(zipWriter, "docProps/app.xml", APP_XML, AppXmlContent{
|
||||
SlideCount: len(p.Slides),
|
||||
TitlesOfPartsCount: len(p.Slides) + 3,
|
||||
D2Version: p.D2Version,
|
||||
Titles: titles,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
<TotalTime>1</TotalTime>
|
||||
<Words>0</Words>
|
||||
<Application>D2</Application>
|
||||
<PresentationFormat>On-screen Show (4:3)</PresentationFormat>
|
||||
<PresentationFormat>On-screen Show (16:9)</PresentationFormat>
|
||||
<Paragraphs>0</Paragraphs>
|
||||
<Slides>%d</Slides>
|
||||
<Slides>{{.SlideCount}}</Slides>
|
||||
<Notes>0</Notes>
|
||||
<HiddenSlides>0</HiddenSlides>
|
||||
<MMClips>0</MMClips>
|
||||
|
|
@ -29,23 +29,23 @@
|
|||
<vt:lpstr>Slide Titles</vt:lpstr>
|
||||
</vt:variant>
|
||||
<vt:variant>
|
||||
<vt:i4>%d</vt:i4>
|
||||
<vt:i4>{{.SlideCount}}</vt:i4>
|
||||
</vt:variant>
|
||||
</vt:vector>
|
||||
</HeadingPairs>
|
||||
<TitlesOfParts>
|
||||
<vt:vector size="%d" baseType="lpstr">
|
||||
<vt:vector size="{{.TitlesOfPartsCount}}" baseType="lpstr">
|
||||
<vt:lpstr>Arial</vt:lpstr>
|
||||
<vt:lpstr>Calibri</vt:lpstr>
|
||||
<vt:lpstr>Office Theme</vt:lpstr>
|
||||
%s
|
||||
{{range .Titles}}
|
||||
<vt:lpstr>{{.}}</vt:lpstr>
|
||||
{{end}}
|
||||
</vt:vector>
|
||||
</TitlesOfParts>
|
||||
<Manager></Manager>
|
||||
<Company></Company>
|
||||
<LinksUpToDate>false</LinksUpToDate>
|
||||
<SharedDoc>false</SharedDoc>
|
||||
<HyperlinkBase></HyperlinkBase>
|
||||
<HyperlinksChanged>false</HyperlinksChanged>
|
||||
<AppVersion>%s</AppVersion>
|
||||
<AppVersion>{{.D2Version}}</AppVersion>
|
||||
</Properties>
|
||||
|
|
@ -54,7 +54,9 @@
|
|||
<Override
|
||||
PartName="/ppt/slideMasters/slideMaster1.xml"
|
||||
ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml" />
|
||||
%s
|
||||
{{range .FileNames}}
|
||||
<Override PartName="/ppt/slides/{{.}}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml" />
|
||||
{{end}}
|
||||
<Override PartName="/ppt/tableStyles.xml"
|
||||
ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml" />
|
||||
<Override
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/"
|
||||
xmlns:dcmitype="http://purl.org/dc/dcmitype/"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dc:title>%s</dc:title>
|
||||
<dc:subject>%s</dc:subject>
|
||||
<dc:creator>%s</dc:creator>
|
||||
<dc:title>{{.Title}}</dc:title>
|
||||
<dc:subject>{{.Subject}}</dc:subject>
|
||||
<dc:creator>{{.Creator}}</dc:creator>
|
||||
<cp:keywords />
|
||||
<dc:description>%s</dc:description>
|
||||
<cp:lastModifiedBy>%s</cp:lastModifiedBy>
|
||||
<dc:description>{{.Description}}</dc:description>
|
||||
<cp:lastModifiedBy>{{.LastModifiedBy}}</cp:lastModifiedBy>
|
||||
<cp:revision>1</cp:revision>
|
||||
<dcterms:created xsi:type="dcterms:W3CDTF">%s</dcterms:created>
|
||||
<dcterms:modified xsi:type="dcterms:W3CDTF">%s</dcterms:modified>
|
||||
<dcterms:created xsi:type="dcterms:W3CDTF">{{.Created}}</dcterms:created>
|
||||
<dcterms:modified xsi:type="dcterms:W3CDTF">{{.Modified}}</dcterms:modified>
|
||||
<cp:category />
|
||||
</cp:coreProperties>
|
||||
|
|
@ -6,8 +6,12 @@
|
|||
<p:sldMasterIdLst>
|
||||
<p:sldMasterId id="2147483648" r:id="rId1" />
|
||||
</p:sldMasterIdLst>
|
||||
<p:sldIdLst>%s</p:sldIdLst>
|
||||
<p:sldSz cx="%d" cy="%d" type="screen4x3" />
|
||||
<p:sldIdLst>
|
||||
{{range .Slides}}
|
||||
<p:sldId id="{{.ID}}" r:id="{{.RelationshipID}}" />
|
||||
{{end}}
|
||||
</p:sldIdLst>
|
||||
<p:sldSz cx="{{.SlideWidth}}" cy="{{.SlideHeight}}" type="screen4x3" />
|
||||
<p:notesSz cx="6858000" cy="9144000" />
|
||||
<p:defaultTextStyle>
|
||||
<a:defPPr>
|
||||
|
|
|
|||
|
|
@ -15,5 +15,7 @@
|
|||
<Relationship Id="rId1"
|
||||
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster"
|
||||
Target="slideMasters/slideMaster1.xml" />
|
||||
%s
|
||||
{{range .Slides}}
|
||||
<Relationship Id="{{.RelationshipID}}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/{{.FileName}}.xml" />
|
||||
{{end}}
|
||||
</Relationships>
|
||||
|
|
@ -19,22 +19,22 @@
|
|||
</p:grpSpPr>
|
||||
<p:pic>
|
||||
<p:nvPicPr>
|
||||
<p:cNvPr id="2" name="%s" descr="%s" />
|
||||
<p:cNvPr id="2" name="{{.Description}}" descr="{{.Description}}" />
|
||||
<p:cNvPicPr>
|
||||
<a:picLocks noChangeAspect="1" />
|
||||
</p:cNvPicPr>
|
||||
<p:nvPr />
|
||||
</p:nvPicPr>
|
||||
<p:blipFill>
|
||||
<a:blip r:embed="%s" />
|
||||
<a:blip r:embed="{{.ImageID}}" />
|
||||
<a:stretch>
|
||||
<a:fillRect />
|
||||
</a:stretch>
|
||||
</p:blipFill>
|
||||
<p:spPr>
|
||||
<a:xfrm>
|
||||
<a:off x="%d" y="%d" />
|
||||
<a:ext cx="%d" cy="%d" />
|
||||
<a:off x="{{.ImageLeft}}" y="{{.ImageTop}}" />
|
||||
<a:ext cx="{{.ImageWidth}}" cy="{{.ImageHeight}}" />
|
||||
</a:xfrm>
|
||||
<a:prstGeom prst="rect">
|
||||
<a:avLst />
|
||||
|
|
@ -43,14 +43,14 @@
|
|||
</p:pic>
|
||||
<p:sp>
|
||||
<p:nvSpPr>
|
||||
<p:cNvPr id="95" name="%s" />
|
||||
<p:cNvPr id="95" name="{{.Description}}" />
|
||||
<p:cNvSpPr txBox="1" />
|
||||
<p:nvPr />
|
||||
</p:nvSpPr>
|
||||
<p:spPr>
|
||||
<a:xfrm>
|
||||
<a:off x="4001" y="6239" />
|
||||
<a:ext cx="9135998" cy="%d" />
|
||||
<a:ext cx="9135998" cy="{{.HeaderHeight}}" />
|
||||
</a:xfrm>
|
||||
<a:prstGeom prst="rect">
|
||||
<a:avLst />
|
||||
|
|
@ -75,7 +75,17 @@
|
|||
<a:defRPr sz="2400" />
|
||||
</a:lvl1pPr>
|
||||
</a:lstStyle>
|
||||
<a:p>%s</a:p>
|
||||
<a:p>
|
||||
{{if .TitlePrefix}}
|
||||
<a:r>
|
||||
<a:t>{{.TitlePrefix}}</a:t>
|
||||
</a:r>
|
||||
{{end}}
|
||||
<a:r>
|
||||
<a:rPr b="1" />
|
||||
<a:t>{{.Title}}</a:t>
|
||||
</a:r>
|
||||
</a:p>
|
||||
</p:txBody>
|
||||
</p:sp>
|
||||
</p:spTree>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<Relationship Id="rId1"
|
||||
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout"
|
||||
Target="../slideLayouts/slideLayout7.xml" />
|
||||
<Relationship Id="%s"
|
||||
<Relationship Id="{{.RelationshipID}}"
|
||||
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
|
||||
Target="../media/%s.png" />
|
||||
Target="../media/{{.FileName}}.png" />
|
||||
</Relationships>
|
||||
Loading…
Reference in a new issue