diff --git a/d2cli/main.go b/d2cli/main.go
index 9b7339f8a..d2998e0b5 100644
--- a/d2cli/main.go
+++ b/d2cli/main.go
@@ -364,8 +364,9 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
if user, err := user.Current(); err == nil {
username = user.Username
}
- description := "Presentation auto-generated by D2 - https://d2lang.com/"
+ description := "Presentation generated with D2 - https://d2lang.com/"
rootName := getFileName(outputPath)
+ // version must be only numbers to avoid issues with PowerPoint
p := pptx.NewPresentation(rootName, description, rootName, username, version.OnlyNumbers())
err := renderPPTX(ctx, ms, p, plugin, renderOpts, outputPath, page, diagram, nil)
if err != nil {
@@ -838,9 +839,7 @@ func renameExt(fp string, newExt string) string {
func getFileName(path string) string {
ext := filepath.Ext(path)
- trimmedPath := strings.TrimSuffix(path, ext)
- splitPath := strings.Split(trimmedPath, "/")
- return splitPath[len(splitPath)-1]
+ return strings.TrimSuffix(filepath.Base(path), ext)
}
// TODO: remove after removing slog
diff --git a/lib/pptx/pptx.go b/lib/pptx/pptx.go
index 1d0c0f44c..08a23d188 100644
--- a/lib/pptx/pptx.go
+++ b/lib/pptx/pptx.go
@@ -9,7 +9,19 @@ import (
"time"
)
+// Measurements in OOXML are made in English Metric Units (EMUs) where 1 inch = 914,400 EMUs
+// The intent is to have a measurement unit that doesn't require floating points when dealing with centimeters, inches, points (DPI).
// Office Open XML (OOXML) http://officeopenxml.com/prPresentation.php
+// https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
+const SLIDE_WIDTH = 9_144_000
+const SLIDE_HEIGHT = 5_143_500
+const HEADER_HEIGHT = 392_471
+
+const IMAGE_HEIGHT = SLIDE_HEIGHT - HEADER_HEIGHT
+
+// keep the right aspect ratio: SLIDE_WIDTH / SLIDE_HEIGHT = IMAGE_WIDTH / IMAGE_HEIGHT
+const IMAGE_WIDTH = 8_446_273
+const IMAGE_ASPECT_RATIO = float64(IMAGE_WIDTH) / float64(IMAGE_HEIGHT)
//go:embed template.pptx
var pptx_template []byte
@@ -38,26 +50,15 @@ func addFile(zipFile *zip.Writer, filePath, content string) error {
return nil
}
-// https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
-const SLIDE_WIDTH = 9_144_000
-const SLIDE_HEIGHT = 5_143_500
-const HEADER_HEIGHT = 392_471
-
-const IMAGE_HEIGHT = SLIDE_HEIGHT - HEADER_HEIGHT
-
-// keep the right aspect ratio: SLIDE_WIDTH / SLIDE_HEIGHT = IMAGE_WIDTH / IMAGE_HEIGHT
-const IMAGE_WIDTH = 8_446_273
-const IMAGE_ASPECT_RATIO = float64(IMAGE_WIDTH) / float64(IMAGE_HEIGHT)
-
const RELS_SLIDE_XML = ``
-func getRelsSlideXml(imageId string) string {
- return fmt.Sprintf(RELS_SLIDE_XML, imageId, imageId)
+func getRelsSlideXml(imageID string) string {
+ return fmt.Sprintf(RELS_SLIDE_XML, imageID, imageID)
}
const SLIDE_XML = `%s`
-func getSlideXml(boardPath []string, imageId string, top, left, width, height int) string {
+func getSlideXml(boardPath []string, imageID string, top, left, width, height int) string {
var slideTitle string
boardName := boardPath[len(boardPath)-1]
prefixPath := boardPath[:len(boardPath)-1]
@@ -69,7 +70,7 @@ func getSlideXml(boardPath []string, imageId string, top, left, width, height in
}
slideDescription := strings.Join(boardPath, " / ")
top += HEADER_HEIGHT
- return fmt.Sprintf(SLIDE_XML, slideDescription, slideDescription, imageId, left, top, width, height, slideDescription, HEADER_HEIGHT, slideTitle)
+ return fmt.Sprintf(SLIDE_XML, slideDescription, slideDescription, imageID, left, top, width, height, slideDescription, HEADER_HEIGHT, slideTitle)
}
func getPresentationXmlRels(slideFileNames []string) string {
diff --git a/lib/pptx/presentation.go b/lib/pptx/presentation.go
index a79e6253d..fea845fd5 100644
--- a/lib/pptx/presentation.go
+++ b/lib/pptx/presentation.go
@@ -23,7 +23,9 @@ type Presentation struct {
Description string
Subject string
Creator string
- D2Version string
+ // D2Version can't have letters, only numbers (`[0-9]`) and `.`
+ // Otherwise, it may fail to open in PowerPoint
+ D2Version string
Slides []*Slide
}
@@ -60,16 +62,37 @@ func (p *Presentation) AddSlide(pngContent []byte, boardPath []string) error {
// compute the size and position to fit the slide
// if the image is wider than taller and its aspect ratio is, at least, the same as the available image space aspect ratio
// then, set the image width to the available space and compute the height
+ // ┌──────────────────────────────────────────────────┐ ─┬─
+ // │ HEADER │ │
+ // ├──┬────────────────────────────────────────────┬──┤ │ ─┬─
+ // │ │ │ │ │ │
+ // │ │ │ │ SLIDE │
+ // │ │ │ │ HEIGHT │
+ // │ │ │ │ │ IMAGE
+ // │ │ │ │ │ HEIGHT
+ // │ │ │ │ │ │
+ // │ │ │ │ │ │
+ // │ │ │ │ │ │
+ // │ │ │ │ │ │
+ // └──┴────────────────────────────────────────────┴──┘ ─┴─ ─┴─
+ // ├────────────────────SLIDE WIDTH───────────────────┤
+ // ├─────────────────IMAGE WIDTH────────────────┤
if srcWidth/srcHeight >= IMAGE_ASPECT_RATIO {
+ // here, the image aspect ratio is, at least, equal to the slide aspect ratio
+ // so, it makes sense to expand the image horizontally to use as much as space as possible
width = SLIDE_WIDTH
height = int(float64(width) * (srcHeight / srcWidth))
+ // first, try to make the image as wide as the slide
+ // but, if this results in a tall image, use only the
+ // image adjusted width to avoid overlapping with the header
if height > IMAGE_HEIGHT {
- // this would overflow with the title, so we need to adjust to use only IMAGE_WIDTH
width = IMAGE_WIDTH
height = int(float64(width) * (srcHeight / srcWidth))
}
} else {
- // otherwise, this image could overflow the slide height/header
+ // here, the aspect ratio could be 4x3, in which the image is still wider than taller,
+ // but expanding horizontally would result in an overflow
+ // so, we expand to make it fit the available vertical space
height = IMAGE_HEIGHT
width = int(float64(height) * (srcWidth / srcHeight))
}
@@ -106,11 +129,11 @@ func (p *Presentation) SaveTo(filePath string) error {
var slideFileNames []string
for i, slide := range p.Slides {
- imageId := fmt.Sprintf("slide%dImage", i+1)
+ imageID := fmt.Sprintf("slide%dImage", i+1)
slideFileName := fmt.Sprintf("slide%d", i+1)
slideFileNames = append(slideFileNames, slideFileName)
- imageWriter, err := zipWriter.Create(fmt.Sprintf("ppt/media/%s.png", imageId))
+ imageWriter, err := zipWriter.Create(fmt.Sprintf("ppt/media/%s.png", imageID))
if err != nil {
return err
}
@@ -119,7 +142,7 @@ func (p *Presentation) SaveTo(filePath string) error {
return err
}
- err = addFile(zipWriter, fmt.Sprintf("ppt/slides/_rels/%s.xml.rels", slideFileName), getRelsSlideXml(imageId))
+ err = addFile(zipWriter, fmt.Sprintf("ppt/slides/_rels/%s.xml.rels", slideFileName), getRelsSlideXml(imageID))
if err != nil {
return err
}
@@ -127,7 +150,7 @@ func (p *Presentation) SaveTo(filePath string) error {
err = addFile(
zipWriter,
fmt.Sprintf("ppt/slides/%s.xml", slideFileName),
- getSlideXml(slide.BoardPath, imageId, slide.ImageTop, slide.ImageLeft, slide.ImageWidth, slide.ImageHeight),
+ getSlideXml(slide.BoardPath, imageID, slide.ImageTop, slide.ImageLeft, slide.ImageWidth, slide.ImageHeight),
)
if err != nil {
return err