Merge pull request #1204 from ejulio-ts/gh-1199-navbar
GH 1199: navbar links
This commit is contained in:
commit
ee93929750
5 changed files with 143 additions and 65 deletions
|
|
@ -33,7 +33,7 @@ import (
|
|||
"oss.terrastruct.com/d2/lib/background"
|
||||
"oss.terrastruct.com/d2/lib/imgbundler"
|
||||
ctxlog "oss.terrastruct.com/d2/lib/log"
|
||||
pdflib "oss.terrastruct.com/d2/lib/pdf"
|
||||
"oss.terrastruct.com/d2/lib/pdf"
|
||||
"oss.terrastruct.com/d2/lib/png"
|
||||
"oss.terrastruct.com/d2/lib/pptx"
|
||||
"oss.terrastruct.com/d2/lib/textmeasure"
|
||||
|
|
@ -380,7 +380,10 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
|||
return svg, true, nil
|
||||
case PDF:
|
||||
pageMap := buildBoardIDToIndex(diagram, nil, nil)
|
||||
pdf, err := renderPDF(ctx, ms, plugin, renderOpts, outputPath, page, ruler, diagram, nil, nil, pageMap)
|
||||
path := []pdf.BoardTitle{
|
||||
{Name: "root", BoardID: "root"},
|
||||
}
|
||||
pdf, err := renderPDF(ctx, ms, plugin, renderOpts, outputPath, page, ruler, diagram, nil, path, pageMap)
|
||||
if err != nil {
|
||||
return pdf, false, err
|
||||
}
|
||||
|
|
@ -398,7 +401,10 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
|||
p := pptx.NewPresentation(rootName, description, rootName, username, version.OnlyNumbers())
|
||||
|
||||
boardIdToIndex := buildBoardIDToIndex(diagram, nil, nil)
|
||||
svg, err := renderPPTX(ctx, ms, p, plugin, renderOpts, ruler, outputPath, page, diagram, nil, boardIdToIndex)
|
||||
path := []pptx.BoardTitle{
|
||||
{Name: "root", BoardID: "root", LinkToSlide: boardIdToIndex["root"] + 1},
|
||||
}
|
||||
svg, err := renderPPTX(ctx, ms, p, plugin, renderOpts, ruler, outputPath, page, diagram, path, boardIdToIndex)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
|
@ -711,21 +717,13 @@ func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts
|
|||
return svg, nil
|
||||
}
|
||||
|
||||
func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts d2svg.RenderOpts, outputPath string, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram, pdf *pdflib.GoFPDF, boardPath []string, pageMap map[string]int) (svg []byte, err error) {
|
||||
func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opts d2svg.RenderOpts, outputPath string, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram, doc *pdf.GoFPDF, boardPath []pdf.BoardTitle, pageMap map[string]int) (svg []byte, err error) {
|
||||
var isRoot bool
|
||||
if pdf == nil {
|
||||
pdf = pdflib.Init()
|
||||
if doc == nil {
|
||||
doc = pdf.Init()
|
||||
isRoot = true
|
||||
}
|
||||
|
||||
var currBoardPath []string
|
||||
// Root board doesn't have a name, so we use the output filename
|
||||
if diagram.Name == "" {
|
||||
currBoardPath = append(boardPath, getFileName(outputPath))
|
||||
} else {
|
||||
currBoardPath = append(boardPath, diagram.Name)
|
||||
}
|
||||
|
||||
if !diagram.IsFolderOnly {
|
||||
rootFill := diagram.Root.Fill
|
||||
// gofpdf will print the png img with a slight filter
|
||||
|
|
@ -769,33 +767,45 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opt
|
|||
if err != nil {
|
||||
return svg, err
|
||||
}
|
||||
err = pdf.AddPDFPage(pngImg, currBoardPath, opts.ThemeID, rootFill, diagram.Shapes, int64(opts.Pad), viewboxX, viewboxY, pageMap)
|
||||
err = doc.AddPDFPage(pngImg, boardPath, opts.ThemeID, rootFill, diagram.Shapes, int64(opts.Pad), viewboxX, viewboxY, pageMap)
|
||||
if err != nil {
|
||||
return svg, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, dl := range diagram.Layers {
|
||||
_, err := renderPDF(ctx, ms, plugin, opts, "", page, ruler, dl, pdf, currBoardPath, pageMap)
|
||||
path := append(boardPath, pdf.BoardTitle{
|
||||
Name: dl.Name,
|
||||
BoardID: strings.Join([]string{boardPath[len(boardPath)-1].BoardID, LAYERS, dl.Name}, "."),
|
||||
})
|
||||
_, err := renderPDF(ctx, ms, plugin, opts, "", page, ruler, dl, doc, path, pageMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, dl := range diagram.Scenarios {
|
||||
_, err := renderPDF(ctx, ms, plugin, opts, "", page, ruler, dl, pdf, currBoardPath, pageMap)
|
||||
path := append(boardPath, pdf.BoardTitle{
|
||||
Name: dl.Name,
|
||||
BoardID: strings.Join([]string{boardPath[len(boardPath)-1].BoardID, SCENARIOS, dl.Name}, "."),
|
||||
})
|
||||
_, err := renderPDF(ctx, ms, plugin, opts, "", page, ruler, dl, doc, path, pageMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, dl := range diagram.Steps {
|
||||
_, err := renderPDF(ctx, ms, plugin, opts, "", page, ruler, dl, pdf, currBoardPath, pageMap)
|
||||
path := append(boardPath, pdf.BoardTitle{
|
||||
Name: dl.Name,
|
||||
BoardID: strings.Join([]string{boardPath[len(boardPath)-1].BoardID, STEPS, dl.Name}, "."),
|
||||
})
|
||||
_, err := renderPDF(ctx, ms, plugin, opts, "", page, ruler, dl, doc, path, pageMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if isRoot {
|
||||
err := pdf.Export(outputPath)
|
||||
err := doc.Export(outputPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -804,15 +814,7 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opt
|
|||
return svg, nil
|
||||
}
|
||||
|
||||
func renderPPTX(ctx context.Context, ms *xmain.State, presentation *pptx.Presentation, plugin d2plugin.Plugin, opts d2svg.RenderOpts, ruler *textmeasure.Ruler, outputPath string, page playwright.Page, diagram *d2target.Diagram, boardPath []string, boardIdToIndex map[string]int) ([]byte, error) {
|
||||
var currBoardPath []string
|
||||
// Root board doesn't have a name, so we use the output filename
|
||||
if diagram.Name == "" {
|
||||
currBoardPath = append(boardPath, getFileName(outputPath))
|
||||
} else {
|
||||
currBoardPath = append(boardPath, diagram.Name)
|
||||
}
|
||||
|
||||
func renderPPTX(ctx context.Context, ms *xmain.State, presentation *pptx.Presentation, plugin d2plugin.Plugin, opts d2svg.RenderOpts, ruler *textmeasure.Ruler, outputPath string, page playwright.Page, diagram *d2target.Diagram, boardPath []pptx.BoardTitle, boardIDToIndex map[string]int) ([]byte, error) {
|
||||
var svg []byte
|
||||
if !diagram.IsFolderOnly {
|
||||
// gofpdf will print the png img with a slight filter
|
||||
|
|
@ -849,7 +851,7 @@ func renderPPTX(ctx context.Context, ms *xmain.State, presentation *pptx.Present
|
|||
return nil, err
|
||||
}
|
||||
|
||||
slide, err := presentation.AddSlide(pngImg, currBoardPath)
|
||||
slide, err := presentation.AddSlide(pngImg, boardPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -886,7 +888,7 @@ func renderPPTX(ctx context.Context, ms *xmain.State, presentation *pptx.Present
|
|||
if err != nil || key.Path[0].Unbox().ScalarString() != "root" {
|
||||
// External link
|
||||
link.ExternalUrl = shape.Link
|
||||
} else if pageNum, ok := boardIdToIndex[shape.Link]; ok {
|
||||
} else if pageNum, ok := boardIDToIndex[shape.Link]; ok {
|
||||
// Internal link
|
||||
link.SlideIndex = pageNum + 1
|
||||
}
|
||||
|
|
@ -894,19 +896,37 @@ func renderPPTX(ctx context.Context, ms *xmain.State, presentation *pptx.Present
|
|||
}
|
||||
|
||||
for _, dl := range diagram.Layers {
|
||||
_, err := renderPPTX(ctx, ms, presentation, plugin, opts, ruler, "", page, dl, currBoardPath, boardIdToIndex)
|
||||
boardID := strings.Join([]string{boardPath[len(boardPath)-1].BoardID, LAYERS, dl.Name}, ".")
|
||||
path := append(boardPath, pptx.BoardTitle{
|
||||
Name: dl.Name,
|
||||
BoardID: boardID,
|
||||
LinkToSlide: boardIDToIndex[boardID] + 1,
|
||||
})
|
||||
_, err := renderPPTX(ctx, ms, presentation, plugin, opts, ruler, "", page, dl, path, boardIDToIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, dl := range diagram.Scenarios {
|
||||
_, err := renderPPTX(ctx, ms, presentation, plugin, opts, ruler, "", page, dl, currBoardPath, boardIdToIndex)
|
||||
boardID := strings.Join([]string{boardPath[len(boardPath)-1].BoardID, SCENARIOS, dl.Name}, ".")
|
||||
path := append(boardPath, pptx.BoardTitle{
|
||||
Name: dl.Name,
|
||||
BoardID: boardID,
|
||||
LinkToSlide: boardIDToIndex[boardID] + 1,
|
||||
})
|
||||
_, err := renderPPTX(ctx, ms, presentation, plugin, opts, ruler, "", page, dl, path, boardIDToIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, dl := range diagram.Steps {
|
||||
_, err := renderPPTX(ctx, ms, presentation, plugin, opts, ruler, "", page, dl, currBoardPath, boardIdToIndex)
|
||||
boardID := strings.Join([]string{boardPath[len(boardPath)-1].BoardID, STEPS, dl.Name}, ".")
|
||||
path := append(boardPath, pptx.BoardTitle{
|
||||
Name: dl.Name,
|
||||
BoardID: boardID,
|
||||
LinkToSlide: boardIDToIndex[boardID] + 1,
|
||||
})
|
||||
_, err := renderPPTX(ctx, ms, presentation, plugin, opts, ruler, "", page, dl, path, boardIDToIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1009,6 +1029,10 @@ func loadFonts(ms *xmain.State, pathToRegular, pathToItalic, pathToBold, pathToS
|
|||
return d2fonts.AddFontFamily("custom", regularTTF, italicTTF, boldTTF, semiboldTTF)
|
||||
}
|
||||
|
||||
const LAYERS = "layers"
|
||||
const STEPS = "steps"
|
||||
const SCENARIOS = "scenarios"
|
||||
|
||||
// buildBoardIDToIndex returns a map from board path to page int
|
||||
// To map correctly, it must follow the same traversal of pdf/pptx building
|
||||
func buildBoardIDToIndex(diagram *d2target.Diagram, dictionary map[string]int, path []string) map[string]int {
|
||||
|
|
@ -1022,13 +1046,13 @@ func buildBoardIDToIndex(diagram *d2target.Diagram, dictionary map[string]int, p
|
|||
dictionary[key] = len(dictionary)
|
||||
|
||||
for _, dl := range diagram.Layers {
|
||||
buildBoardIDToIndex(dl, dictionary, append(newPath, "layers"))
|
||||
buildBoardIDToIndex(dl, dictionary, append(newPath, LAYERS))
|
||||
}
|
||||
for _, dl := range diagram.Scenarios {
|
||||
buildBoardIDToIndex(dl, dictionary, append(newPath, "scenarios"))
|
||||
buildBoardIDToIndex(dl, dictionary, append(newPath, SCENARIOS))
|
||||
}
|
||||
for _, dl := range diagram.Steps {
|
||||
buildBoardIDToIndex(dl, dictionary, append(newPath, "steps"))
|
||||
buildBoardIDToIndex(dl, dictionary, append(newPath, STEPS))
|
||||
}
|
||||
|
||||
return dictionary
|
||||
|
|
|
|||
|
|
@ -15,10 +15,17 @@ import (
|
|||
"oss.terrastruct.com/d2/lib/color"
|
||||
)
|
||||
|
||||
const TITLE_SEP = " / "
|
||||
|
||||
type GoFPDF struct {
|
||||
pdf *gofpdf.Fpdf
|
||||
}
|
||||
|
||||
type BoardTitle struct {
|
||||
Name string
|
||||
BoardID string
|
||||
}
|
||||
|
||||
func Init() *GoFPDF {
|
||||
newGofPDF := gofpdf.NewCustom(&gofpdf.InitType{
|
||||
UnitStr: "pt",
|
||||
|
|
@ -59,9 +66,13 @@ func (g *GoFPDF) GetFillRGB(themeID int64, fill string) (color.RGB, error) {
|
|||
return color.Hex2RGB(fill)
|
||||
}
|
||||
|
||||
func (g *GoFPDF) AddPDFPage(png []byte, boardPath []string, themeID int64, fill string, shapes []d2target.Shape, pad int64, viewboxX, viewboxY float64, pageMap map[string]int) error {
|
||||
func (g *GoFPDF) AddPDFPage(png []byte, titlePath []BoardTitle, themeID int64, fill string, shapes []d2target.Shape, pad int64, viewboxX, viewboxY float64, pageMap map[string]int) error {
|
||||
var opt gofpdf.ImageOptions
|
||||
opt.ImageType = "png"
|
||||
boardPath := make([]string, len(titlePath))
|
||||
for i, t := range titlePath {
|
||||
boardPath[i] = t.Name
|
||||
}
|
||||
imageInfo := g.pdf.RegisterImageOptionsReader(strings.Join(boardPath, "/"), opt, bytes.NewReader(png))
|
||||
if g.pdf.Err() {
|
||||
return g.pdf.Error()
|
||||
|
|
@ -102,20 +113,30 @@ func (g *GoFPDF) AddPDFPage(png []byte, boardPath []string, themeID int64, fill
|
|||
g.pdf.SetFont("source", "", 14)
|
||||
|
||||
// Draw board path prefix
|
||||
var prefixWidth float64
|
||||
prefixPath := boardPath[:len(boardPath)-1]
|
||||
if len(prefixPath) > 0 {
|
||||
prefix := strings.Join(boardPath[:len(boardPath)-1], " / ") + " / "
|
||||
prefixWidth = g.pdf.GetStringWidth(prefix)
|
||||
prefixWidth := headerMargin
|
||||
if len(titlePath) > 1 {
|
||||
for _, t := range titlePath[:len(titlePath)-1] {
|
||||
g.pdf.SetXY(prefixWidth, 0)
|
||||
w := g.pdf.GetStringWidth(t.Name)
|
||||
var linkID int
|
||||
if pageNum, ok := pageMap[t.BoardID]; ok {
|
||||
linkID = g.pdf.AddLink()
|
||||
g.pdf.SetLink(linkID, 0, pageNum+1)
|
||||
}
|
||||
g.pdf.CellFormat(w, headerHeight, t.Name, "", 0, "", false, linkID, "")
|
||||
prefixWidth += w
|
||||
|
||||
g.pdf.SetXY(headerMargin, 0)
|
||||
g.pdf.CellFormat(prefixWidth, headerHeight, prefix, "", 0, "", false, 0, "")
|
||||
g.pdf.SetXY(prefixWidth, 0)
|
||||
w = g.pdf.GetStringWidth(TITLE_SEP)
|
||||
g.pdf.CellFormat(prefixWidth, headerHeight, TITLE_SEP, "", 0, "", false, 0, "")
|
||||
prefixWidth += w
|
||||
}
|
||||
}
|
||||
|
||||
// Draw board name
|
||||
boardName := boardPath[len(boardPath)-1]
|
||||
g.pdf.SetFont("source", "B", 14)
|
||||
g.pdf.SetXY(prefixWidth+headerMargin, 0)
|
||||
g.pdf.SetXY(prefixWidth, 0)
|
||||
g.pdf.CellFormat(pageWidth-prefixWidth-headerMargin, headerHeight, boardName, "", 0, "", false, 0, "")
|
||||
|
||||
// Draw image
|
||||
|
|
|
|||
|
|
@ -16,11 +16,17 @@ import (
|
|||
"fmt"
|
||||
"image/png"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BoardTitle struct {
|
||||
LinkID string
|
||||
Name string
|
||||
BoardID string
|
||||
LinkToSlide int
|
||||
}
|
||||
|
||||
type Presentation struct {
|
||||
Title string
|
||||
Description string
|
||||
|
|
@ -32,8 +38,9 @@ type Presentation struct {
|
|||
|
||||
Slides []*Slide
|
||||
}
|
||||
|
||||
type Slide struct {
|
||||
BoardPath []string
|
||||
BoardTitle []BoardTitle
|
||||
Links []*Link
|
||||
Image []byte
|
||||
ImageId string
|
||||
|
|
@ -76,7 +83,7 @@ func NewPresentation(title, description, subject, creator, d2Version string) *Pr
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Presentation) AddSlide(pngContent []byte, boardPath []string) (*Slide, error) {
|
||||
func (p *Presentation) AddSlide(pngContent []byte, titlePath []BoardTitle) (*Slide, error) {
|
||||
src, err := png.Decode(bytes.NewReader(pngContent))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding PNG image: %v", err)
|
||||
|
|
@ -127,7 +134,7 @@ func (p *Presentation) AddSlide(pngContent []byte, boardPath []string) (*Slide,
|
|||
left := (SLIDE_WIDTH - width) / 2
|
||||
|
||||
slide := &Slide{
|
||||
BoardPath: make([]string, len(boardPath)),
|
||||
BoardTitle: make([]BoardTitle, len(titlePath)),
|
||||
ImageId: fmt.Sprintf("slide%dImage", len(p.Slides)+1),
|
||||
Image: pngContent,
|
||||
ImageWidth: width,
|
||||
|
|
@ -137,7 +144,10 @@ func (p *Presentation) AddSlide(pngContent []byte, boardPath []string) (*Slide,
|
|||
ImageScaleFactor: float64(width) / srcWidth,
|
||||
}
|
||||
// it must copy the board path to avoid slice reference issues
|
||||
copy(slide.BoardPath, boardPath)
|
||||
for i := 0; i < len(titlePath); i++ {
|
||||
titlePath[i].LinkID = fmt.Sprintf("navLink%d", i)
|
||||
slide.BoardTitle[i] = titlePath[i]
|
||||
}
|
||||
|
||||
p.Slides = append(p.Slides, slide)
|
||||
return slide, nil
|
||||
|
|
@ -215,11 +225,11 @@ func (p *Presentation) SaveTo(filePath string) error {
|
|||
|
||||
titles := make([]string, 0, len(p.Slides))
|
||||
for _, slide := range p.Slides {
|
||||
titles = append(titles, strings.Join(slide.BoardPath, "/"))
|
||||
titles = append(titles, slide.BoardTitle[len(slide.BoardTitle)-1].BoardID)
|
||||
}
|
||||
err = addFileFromTemplate(zipWriter, "docProps/app.xml", APP_XML, AppXmlContent{
|
||||
SlideCount: len(p.Slides),
|
||||
TitlesOfPartsCount: len(p.Slides) + 3,
|
||||
TitlesOfPartsCount: len(p.Slides) + 3, // + 3 for fonts and theme
|
||||
D2Version: p.D2Version,
|
||||
Titles: titles,
|
||||
})
|
||||
|
|
@ -291,6 +301,13 @@ func getSlideXmlRelsContent(imageID string, slide *Slide) RelsSlideXmlContent {
|
|||
})
|
||||
}
|
||||
|
||||
for _, t := range slide.BoardTitle {
|
||||
content.Links = append(content.Links, RelsSlideXmlLinkContent{
|
||||
RelationshipID: t.LinkID,
|
||||
SlideIndex: t.LinkToSlide,
|
||||
})
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
|
|
@ -308,9 +325,14 @@ type SlideLinkXmlContent struct {
|
|||
Height int
|
||||
}
|
||||
|
||||
type SlideXmlTitlePathContent struct {
|
||||
Name string
|
||||
RelationshipID string
|
||||
}
|
||||
|
||||
type SlideXmlContent struct {
|
||||
Title string
|
||||
TitlePrefix string
|
||||
TitlePrefix []SlideXmlTitlePathContent
|
||||
Description string
|
||||
HeaderHeight int
|
||||
ImageID string
|
||||
|
|
@ -323,17 +345,18 @@ type SlideXmlContent struct {
|
|||
}
|
||||
|
||||
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, " / ") + " / "
|
||||
title := make([]SlideXmlTitlePathContent, len(slide.BoardTitle)-1)
|
||||
for i := 0; i < len(slide.BoardTitle)-1; i++ {
|
||||
t := slide.BoardTitle[i]
|
||||
title[i] = SlideXmlTitlePathContent{
|
||||
Name: t.Name,
|
||||
RelationshipID: t.LinkID,
|
||||
}
|
||||
}
|
||||
content := SlideXmlContent{
|
||||
Title: boardName,
|
||||
TitlePrefix: prefix,
|
||||
Description: strings.Join(boardPath, " / "),
|
||||
Title: slide.BoardTitle[len(slide.BoardTitle)-1].Name,
|
||||
TitlePrefix: title,
|
||||
Description: slide.BoardTitle[len(slide.BoardTitle)-1].BoardID,
|
||||
HeaderHeight: HEADER_HEIGHT,
|
||||
ImageID: imageID,
|
||||
ImageLeft: slide.ImageLeft,
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -75,9 +75,19 @@
|
|||
<a:defRPr sz="2400" />
|
||||
</a:lvl1pPr>
|
||||
</a:lstStyle>
|
||||
<a:p> {{if .TitlePrefix}} <a:r>
|
||||
<a:t>{{.TitlePrefix}}</a:t>
|
||||
</a:r> {{end}} <a:r>
|
||||
<a:p>
|
||||
{{range .TitlePrefix}}
|
||||
<a:r>
|
||||
<a:rPr>
|
||||
<a:hlinkClick r:id="{{.RelationshipID}}" invalidUrl=""
|
||||
action="ppaction://hlinksldjump" tgtFrame="" tooltip=""
|
||||
history="1" highlightClick="0" endSnd="0" />
|
||||
</a:rPr>
|
||||
<a:t>{{.Name}}</a:t>
|
||||
</a:r>
|
||||
<a:r><a:t> / </a:t></a:r>
|
||||
{{end}}
|
||||
<a:r>
|
||||
<a:rPr b="1" />
|
||||
<a:t>{{.Title}}</a:t>
|
||||
</a:r>
|
||||
|
|
|
|||
Loading…
Reference in a new issue