generate files from templates

This commit is contained in:
Júlio César Batista 2023-04-10 15:45:10 -03:00
parent e39b9c15c2
commit 0fad458858
No known key found for this signature in database
GPG key ID: 10C4B861BF314878
9 changed files with 176 additions and 106 deletions

View file

@ -6,7 +6,7 @@ import (
_ "embed" _ "embed"
"fmt" "fmt"
"strings" "strings"
"time" "text/template"
) )
// Measurements in OOXML are made in English Metric Units (EMUs) where 1 inch = 914,400 EMUs // 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 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 //go:embed templates/slide.xml.rels
var RELS_SLIDE_XML string var RELS_SLIDE_XML string
func getRelsSlideXml(imageID string) string { type RelsSlideXmlContent struct {
return fmt.Sprintf(RELS_SLIDE_XML, imageID, imageID) FileName string
RelationshipID string
} }
//go:embed templates/slide.xml //go:embed templates/slide.xml
var SLIDE_XML string var SLIDE_XML string
func getSlideXml(boardPath []string, imageID string, top, left, width, height int) string { type SlideXmlContent struct {
var slideTitle string 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] boardName := boardPath[len(boardPath)-1]
prefixPath := boardPath[:len(boardPath)-1] prefixPath := boardPath[:len(boardPath)-1]
var prefix string
if len(prefixPath) > 0 { if len(prefixPath) > 0 {
prefix := strings.Join(prefixPath, " / ") + " / " 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 { return SlideXmlContent{
slideTitle = fmt.Sprintf(`<a:r><a:rPr b="1" /><a:t>%s</a:t></a:r>`, boardName) 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 //go:embed templates/rels_presentation.xml
var RELS_PRESENTATION_XML string var RELS_PRESENTATION_XML string
func getPresentationXmlRels(slideFileNames []string) string { type RelsPresentationSlideXmlContent struct {
var builder strings.Builder RelationshipID string
FileName string
}
type RelsPresentationXmlContent struct {
Slides []RelsPresentationSlideXmlContent
}
func getRelsPresentationXmlContent(slideFileNames []string) RelsPresentationXmlContent {
var content RelsPresentationXmlContent
for _, name := range slideFileNames { for _, name := range slideFileNames {
builder.WriteString(fmt.Sprintf( content.Slides = append(content.Slides, RelsPresentationSlideXmlContent{
`<Relationship Id="%s" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/%s.xml" />`, name, name, RelationshipID: name,
)) FileName: name,
})
} }
return fmt.Sprintf(RELS_PRESENTATION_XML, builder.String()) return content
} }
//go:embed templates/content_types.xml //go:embed templates/content_types.xml
var CONTENT_TYPES_XML string var CONTENT_TYPES_XML string
func getContentTypesXml(slideFileNames []string) string { type ContentTypesXmlContent struct {
var builder strings.Builder FileNames []string
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())
} }
//go:embed templates/presentation.xml //go:embed templates/presentation.xml
var PRESENTATION_XML string var PRESENTATION_XML string
func getPresentationXml(slideFileNames []string) string { type PresentationSlideXmlContent struct {
var builder strings.Builder ID int
for i, name := range slideFileNames { RelationshipID string
// 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 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 //go:embed templates/core.xml
var CORE_XML string var CORE_XML string
func getCoreXml(title, subject, description, creator string) string { type CoreXmlContent struct {
dateTime := time.Now().Format(time.RFC3339) Title string
return fmt.Sprintf( Subject string
CORE_XML, Creator string
title, Description string
subject, LastModifiedBy string
creator, Created string
description, Modified string
creator,
dateTime,
dateTime,
)
} }
//go:embed templates/app.xml //go:embed templates/app.xml
var APP_XML string var APP_XML string
func getAppXml(slides []*Slide, d2version string) string { type AppXmlContent struct {
var builder strings.Builder SlideCount int
for _, slide := range slides { TitlesOfPartsCount int
builder.WriteString(fmt.Sprintf(`<vt:lpstr>%s</vt:lpstr>`, strings.Join(slide.BoardPath, "/"))) Titles []string
} D2Version string
return fmt.Sprintf( }
APP_XML,
len(slides), func addFileFromTemplate(zipFile *zip.Writer, filePath, templateContent string, templateData interface{}) error {
len(slides), w, err := zipFile.Create(filePath)
len(slides)+3, // number of entries, len(slides) + Office Theme + 2 Fonts if err != nil {
builder.String(), return err
d2version, }
)
tmpl, err := template.New(filePath).Parse(templateContent)
if err != nil {
return err
}
return tmpl.Execute(w, templateData)
} }

View file

@ -16,6 +16,8 @@ import (
"fmt" "fmt"
"image/png" "image/png"
"os" "os"
"strings"
"time"
) )
type Presentation struct { type Presentation struct {
@ -142,42 +144,61 @@ func (p *Presentation) SaveTo(filePath string) error {
return err 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 { if err != nil {
return err return err
} }
err = addFile( err = addFileFromTemplate(zipWriter, fmt.Sprintf("ppt/slides/%s.xml", slideFileName), SLIDE_XML, getSlideXmlContent(imageID, slide))
zipWriter,
fmt.Sprintf("ppt/slides/%s.xml", slideFileName),
getSlideXml(slide.BoardPath, imageID, slide.ImageTop, slide.ImageLeft, slide.ImageWidth, slide.ImageHeight),
)
if err != nil { if err != nil {
return err 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 { if err != nil {
return err 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 { if err != nil {
return err return err
} }
err = addFile(zipWriter, "ppt/presentation.xml", getPresentationXml(slideFileNames)) err = addFileFromTemplate(zipWriter, "ppt/presentation.xml", PRESENTATION_XML, getPresentationXmlContent(slideFileNames))
if err != nil { if err != nil {
return err 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 { if err != nil {
return err 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 { if err != nil {
return err return err
} }

View file

@ -4,9 +4,9 @@
<TotalTime>1</TotalTime> <TotalTime>1</TotalTime>
<Words>0</Words> <Words>0</Words>
<Application>D2</Application> <Application>D2</Application>
<PresentationFormat>On-screen Show (4:3)</PresentationFormat> <PresentationFormat>On-screen Show (16:9)</PresentationFormat>
<Paragraphs>0</Paragraphs> <Paragraphs>0</Paragraphs>
<Slides>%d</Slides> <Slides>{{.SlideCount}}</Slides>
<Notes>0</Notes> <Notes>0</Notes>
<HiddenSlides>0</HiddenSlides> <HiddenSlides>0</HiddenSlides>
<MMClips>0</MMClips> <MMClips>0</MMClips>
@ -29,23 +29,23 @@
<vt:lpstr>Slide Titles</vt:lpstr> <vt:lpstr>Slide Titles</vt:lpstr>
</vt:variant> </vt:variant>
<vt:variant> <vt:variant>
<vt:i4>%d</vt:i4> <vt:i4>{{.SlideCount}}</vt:i4>
</vt:variant> </vt:variant>
</vt:vector> </vt:vector>
</HeadingPairs> </HeadingPairs>
<TitlesOfParts> <TitlesOfParts>
<vt:vector size="%d" baseType="lpstr"> <vt:vector size="{{.TitlesOfPartsCount}}" baseType="lpstr">
<vt:lpstr>Arial</vt:lpstr> <vt:lpstr>Arial</vt:lpstr>
<vt:lpstr>Calibri</vt:lpstr> <vt:lpstr>Calibri</vt:lpstr>
<vt:lpstr>Office Theme</vt:lpstr> <vt:lpstr>Office Theme</vt:lpstr>
%s {{range .Titles}}
<vt:lpstr>{{.}}</vt:lpstr>
{{end}}
</vt:vector> </vt:vector>
</TitlesOfParts> </TitlesOfParts>
<Manager></Manager>
<Company></Company>
<LinksUpToDate>false</LinksUpToDate> <LinksUpToDate>false</LinksUpToDate>
<SharedDoc>false</SharedDoc> <SharedDoc>false</SharedDoc>
<HyperlinkBase></HyperlinkBase> <HyperlinkBase></HyperlinkBase>
<HyperlinksChanged>false</HyperlinksChanged> <HyperlinksChanged>false</HyperlinksChanged>
<AppVersion>%s</AppVersion> <AppVersion>{{.D2Version}}</AppVersion>
</Properties> </Properties>

View file

@ -54,7 +54,9 @@
<Override <Override
PartName="/ppt/slideMasters/slideMaster1.xml" PartName="/ppt/slideMasters/slideMaster1.xml"
ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+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" <Override PartName="/ppt/tableStyles.xml"
ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml" /> ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml" />
<Override <Override

View file

@ -4,14 +4,14 @@
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcmitype="http://purl.org/dc/dcmitype/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dc:title>%s</dc:title> <dc:title>{{.Title}}</dc:title>
<dc:subject>%s</dc:subject> <dc:subject>{{.Subject}}</dc:subject>
<dc:creator>%s</dc:creator> <dc:creator>{{.Creator}}</dc:creator>
<cp:keywords /> <cp:keywords />
<dc:description>%s</dc:description> <dc:description>{{.Description}}</dc:description>
<cp:lastModifiedBy>%s</cp:lastModifiedBy> <cp:lastModifiedBy>{{.LastModifiedBy}}</cp:lastModifiedBy>
<cp:revision>1</cp:revision> <cp:revision>1</cp:revision>
<dcterms:created xsi:type="dcterms:W3CDTF">%s</dcterms:created> <dcterms:created xsi:type="dcterms:W3CDTF">{{.Created}}</dcterms:created>
<dcterms:modified xsi:type="dcterms:W3CDTF">%s</dcterms:modified> <dcterms:modified xsi:type="dcterms:W3CDTF">{{.Modified}}</dcterms:modified>
<cp:category /> <cp:category />
</cp:coreProperties> </cp:coreProperties>

View file

@ -6,8 +6,12 @@
<p:sldMasterIdLst> <p:sldMasterIdLst>
<p:sldMasterId id="2147483648" r:id="rId1" /> <p:sldMasterId id="2147483648" r:id="rId1" />
</p:sldMasterIdLst> </p:sldMasterIdLst>
<p:sldIdLst>%s</p:sldIdLst> <p:sldIdLst>
<p:sldSz cx="%d" cy="%d" type="screen4x3" /> {{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:notesSz cx="6858000" cy="9144000" />
<p:defaultTextStyle> <p:defaultTextStyle>
<a:defPPr> <a:defPPr>

View file

@ -15,5 +15,7 @@
<Relationship Id="rId1" <Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster"
Target="slideMasters/slideMaster1.xml" /> 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> </Relationships>

View file

@ -19,22 +19,22 @@
</p:grpSpPr> </p:grpSpPr>
<p:pic> <p:pic>
<p:nvPicPr> <p:nvPicPr>
<p:cNvPr id="2" name="%s" descr="%s" /> <p:cNvPr id="2" name="{{.Description}}" descr="{{.Description}}" />
<p:cNvPicPr> <p:cNvPicPr>
<a:picLocks noChangeAspect="1" /> <a:picLocks noChangeAspect="1" />
</p:cNvPicPr> </p:cNvPicPr>
<p:nvPr /> <p:nvPr />
</p:nvPicPr> </p:nvPicPr>
<p:blipFill> <p:blipFill>
<a:blip r:embed="%s" /> <a:blip r:embed="{{.ImageID}}" />
<a:stretch> <a:stretch>
<a:fillRect /> <a:fillRect />
</a:stretch> </a:stretch>
</p:blipFill> </p:blipFill>
<p:spPr> <p:spPr>
<a:xfrm> <a:xfrm>
<a:off x="%d" y="%d" /> <a:off x="{{.ImageLeft}}" y="{{.ImageTop}}" />
<a:ext cx="%d" cy="%d" /> <a:ext cx="{{.ImageWidth}}" cy="{{.ImageHeight}}" />
</a:xfrm> </a:xfrm>
<a:prstGeom prst="rect"> <a:prstGeom prst="rect">
<a:avLst /> <a:avLst />
@ -43,14 +43,14 @@
</p:pic> </p:pic>
<p:sp> <p:sp>
<p:nvSpPr> <p:nvSpPr>
<p:cNvPr id="95" name="%s" /> <p:cNvPr id="95" name="{{.Description}}" />
<p:cNvSpPr txBox="1" /> <p:cNvSpPr txBox="1" />
<p:nvPr /> <p:nvPr />
</p:nvSpPr> </p:nvSpPr>
<p:spPr> <p:spPr>
<a:xfrm> <a:xfrm>
<a:off x="4001" y="6239" /> <a:off x="4001" y="6239" />
<a:ext cx="9135998" cy="%d" /> <a:ext cx="9135998" cy="{{.HeaderHeight}}" />
</a:xfrm> </a:xfrm>
<a:prstGeom prst="rect"> <a:prstGeom prst="rect">
<a:avLst /> <a:avLst />
@ -75,7 +75,17 @@
<a:defRPr sz="2400" /> <a:defRPr sz="2400" />
</a:lvl1pPr> </a:lvl1pPr>
</a:lstStyle> </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:txBody>
</p:sp> </p:sp>
</p:spTree> </p:spTree>

View file

@ -3,7 +3,7 @@
<Relationship Id="rId1" <Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout"
Target="../slideLayouts/slideLayout7.xml" /> Target="../slideLayouts/slideLayout7.xml" />
<Relationship Id="%s" <Relationship Id="{{.RelationshipID}}"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
Target="../media/%s.png" /> Target="../media/{{.FileName}}.png" />
</Relationships> </Relationships>