2017-08-28 20:56:18 -05:00
|
|
|
// Copyright 2017 Baliance. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by the terms of the Affero GNU General
|
|
|
|
// Public License version 3.0 as published by the Free Software Foundation and
|
|
|
|
// appearing in the file LICENSE included in the packaging of this file. A
|
|
|
|
// commercial license can be purchased by contacting sales@baliance.com.
|
|
|
|
|
|
|
|
package presentation
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
2017-10-03 09:55:27 -05:00
|
|
|
"bytes"
|
|
|
|
"encoding/xml"
|
|
|
|
"errors"
|
2017-08-28 20:56:18 -05:00
|
|
|
"fmt"
|
2017-10-03 09:55:27 -05:00
|
|
|
"image"
|
|
|
|
"image/jpeg"
|
2017-08-28 20:56:18 -05:00
|
|
|
"io"
|
2017-10-03 09:55:27 -05:00
|
|
|
"log"
|
2017-08-28 20:56:18 -05:00
|
|
|
"os"
|
2018-01-16 08:23:18 -06:00
|
|
|
"path"
|
2018-07-08 17:26:24 -05:00
|
|
|
"strings"
|
2017-08-28 20:56:18 -05:00
|
|
|
|
2019-05-04 11:18:06 +03:00
|
|
|
"github.com/unidoc/unioffice"
|
|
|
|
"github.com/unidoc/unioffice/common"
|
|
|
|
"github.com/unidoc/unioffice/measurement"
|
|
|
|
"github.com/unidoc/unioffice/schema/soo/dml"
|
|
|
|
"github.com/unidoc/unioffice/schema/soo/ofc/sharedTypes"
|
|
|
|
"github.com/unidoc/unioffice/schema/soo/pkg/relationships"
|
|
|
|
"github.com/unidoc/unioffice/schema/soo/pml"
|
|
|
|
"github.com/unidoc/unioffice/zippkg"
|
2017-08-28 20:56:18 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Presentation is the a presentation base document.
|
|
|
|
type Presentation struct {
|
|
|
|
common.DocBase
|
|
|
|
x *pml.Presentation
|
|
|
|
prels common.Relationships
|
|
|
|
slides []*pml.Sld
|
|
|
|
slideRels []common.Relationships
|
|
|
|
masters []*pml.SldMaster
|
|
|
|
masterRels []common.Relationships
|
|
|
|
layouts []*pml.SldLayout
|
|
|
|
layoutRels []common.Relationships
|
|
|
|
themes []*dml.Theme
|
2017-10-03 09:55:27 -05:00
|
|
|
themeRels []common.Relationships
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
|
|
|
|
2018-03-13 07:46:34 -05:00
|
|
|
func newEmpty() *Presentation {
|
2017-08-28 20:56:18 -05:00
|
|
|
p := &Presentation{x: pml.NewPresentation()}
|
|
|
|
p.x.SldIdLst = pml.NewCT_SlideIdList()
|
|
|
|
p.x.ConformanceAttr = sharedTypes.ST_ConformanceClassTransitional
|
|
|
|
p.AppProperties = common.NewAppProperties()
|
|
|
|
p.CoreProperties = common.NewCoreProperties()
|
|
|
|
p.ContentTypes = common.NewContentTypes()
|
2018-03-13 07:46:34 -05:00
|
|
|
p.Rels = common.NewRelationships()
|
|
|
|
p.prels = common.NewRelationships()
|
|
|
|
return p
|
|
|
|
}
|
2017-08-28 20:56:18 -05:00
|
|
|
|
2018-03-13 07:46:34 -05:00
|
|
|
// New initializes and reurns a new presentation
|
|
|
|
func New() *Presentation {
|
|
|
|
p := newEmpty()
|
2017-08-28 20:56:18 -05:00
|
|
|
|
2018-03-13 07:46:34 -05:00
|
|
|
p.ContentTypes.AddOverride("/ppt/presentation.xml", "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml")
|
2017-08-28 20:56:18 -05:00
|
|
|
|
|
|
|
p.Rels.AddRelationship("docProps/core.xml", "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties")
|
|
|
|
p.Rels.AddRelationship("docProps/app.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties")
|
|
|
|
p.Rels.AddRelationship("ppt/presentation.xml", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument")
|
|
|
|
|
|
|
|
p.x.SldMasterIdLst = pml.NewCT_SlideMasterIdList()
|
|
|
|
m := pml.NewSldMaster()
|
2017-09-23 08:39:08 -05:00
|
|
|
m.ClrMap.Bg1Attr = dml.ST_ColorSchemeIndexLt1
|
|
|
|
m.ClrMap.Bg2Attr = dml.ST_ColorSchemeIndexLt2
|
|
|
|
m.ClrMap.Tx1Attr = dml.ST_ColorSchemeIndexDk1
|
|
|
|
m.ClrMap.Tx2Attr = dml.ST_ColorSchemeIndexDk2
|
|
|
|
m.ClrMap.Accent1Attr = dml.ST_ColorSchemeIndexAccent1
|
|
|
|
m.ClrMap.Accent2Attr = dml.ST_ColorSchemeIndexAccent2
|
|
|
|
m.ClrMap.Accent3Attr = dml.ST_ColorSchemeIndexAccent3
|
|
|
|
m.ClrMap.Accent4Attr = dml.ST_ColorSchemeIndexAccent4
|
|
|
|
m.ClrMap.Accent5Attr = dml.ST_ColorSchemeIndexAccent5
|
|
|
|
m.ClrMap.Accent6Attr = dml.ST_ColorSchemeIndexAccent6
|
|
|
|
m.ClrMap.HlinkAttr = dml.ST_ColorSchemeIndexHlink
|
|
|
|
m.ClrMap.FolHlinkAttr = dml.ST_ColorSchemeIndexFolHlink
|
2017-08-28 20:56:18 -05:00
|
|
|
|
|
|
|
p.masters = append(p.masters, m)
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
smFn := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideMasterType, 1)
|
|
|
|
p.ContentTypes.AddOverride(smFn, unioffice.SlideMasterContentType)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
mrelID := p.prels.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.OfficeDocumentType,
|
|
|
|
1, unioffice.SlideMasterType)
|
2017-08-28 20:56:18 -05:00
|
|
|
smid := pml.NewCT_SlideMasterIdListEntry()
|
2019-05-04 13:54:29 +00:00
|
|
|
smid.IdAttr = unioffice.Uint32(2147483648)
|
2017-08-28 20:56:18 -05:00
|
|
|
smid.RIdAttr = mrelID.ID()
|
|
|
|
p.x.SldMasterIdLst.SldMasterId = append(p.x.SldMasterIdLst.SldMasterId, smid)
|
|
|
|
mrel := common.NewRelationships()
|
|
|
|
p.masterRels = append(p.masterRels, mrel)
|
|
|
|
|
|
|
|
ls := pml.NewSldLayout()
|
2019-05-04 13:54:29 +00:00
|
|
|
lrid := mrel.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.SlideMasterType, 1, unioffice.SlideLayoutType)
|
|
|
|
slfn := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideLayoutType, 1)
|
|
|
|
p.ContentTypes.AddOverride(slfn, unioffice.SlideLayoutContentType)
|
|
|
|
mrel.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.SlideMasterType, 1, unioffice.ThemeType)
|
2017-08-28 20:56:18 -05:00
|
|
|
p.layouts = append(p.layouts, ls)
|
|
|
|
|
|
|
|
m.SldLayoutIdLst = pml.NewCT_SlideLayoutIdList()
|
|
|
|
lid := pml.NewCT_SlideLayoutIdListEntry()
|
2019-05-04 13:54:29 +00:00
|
|
|
lid.IdAttr = unioffice.Uint32(2147483649)
|
2017-08-28 20:56:18 -05:00
|
|
|
lid.RIdAttr = lrid.ID()
|
|
|
|
m.SldLayoutIdLst.SldLayoutId = append(m.SldLayoutIdLst.SldLayoutId, lid)
|
|
|
|
|
|
|
|
lrel := common.NewRelationships()
|
|
|
|
p.layoutRels = append(p.layoutRels, lrel)
|
2019-05-04 13:54:29 +00:00
|
|
|
lrel.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.SlideType, 1, unioffice.SlideMasterType)
|
2017-08-28 20:56:18 -05:00
|
|
|
p.x.NotesSz.CxAttr = 6858000
|
|
|
|
p.x.NotesSz.CyAttr = 9144000
|
|
|
|
|
|
|
|
thm := dml.NewTheme()
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
thm.NameAttr = unioffice.String("gooxml Theme")
|
2017-08-28 20:56:18 -05:00
|
|
|
thm.ThemeElements.ClrScheme.NameAttr = "Office"
|
|
|
|
thm.ThemeElements.ClrScheme.Dk1.SysClr = dml.NewCT_SystemColor()
|
2019-05-04 13:54:29 +00:00
|
|
|
thm.ThemeElements.ClrScheme.Dk1.SysClr.LastClrAttr = unioffice.String("000000")
|
2017-08-28 20:56:18 -05:00
|
|
|
thm.ThemeElements.ClrScheme.Dk1.SysClr.ValAttr = dml.ST_SystemColorValWindowText
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Lt1.SysClr = dml.NewCT_SystemColor()
|
2019-05-04 13:54:29 +00:00
|
|
|
thm.ThemeElements.ClrScheme.Lt1.SysClr.LastClrAttr = unioffice.String("ffffff")
|
2017-08-28 20:56:18 -05:00
|
|
|
thm.ThemeElements.ClrScheme.Lt1.SysClr.ValAttr = dml.ST_SystemColorValWindow
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Dk2.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Dk2.SrgbClr.ValAttr = "44546a"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Lt2.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Lt2.SrgbClr.ValAttr = "e7e7e6"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Accent1.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Accent1.SrgbClr.ValAttr = "4472c4"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Accent2.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Accent2.SrgbClr.ValAttr = "ed7d31"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Accent3.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Accent3.SrgbClr.ValAttr = "a5a5a5"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Accent4.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Accent4.SrgbClr.ValAttr = "ffc000"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Accent5.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Accent5.SrgbClr.ValAttr = "5b9bd5"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Accent6.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Accent6.SrgbClr.ValAttr = "70ad47"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.Hlink.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.Hlink.SrgbClr.ValAttr = "0563c1"
|
|
|
|
|
|
|
|
thm.ThemeElements.ClrScheme.FolHlink.SrgbClr = dml.NewCT_SRgbColor()
|
|
|
|
thm.ThemeElements.ClrScheme.FolHlink.SrgbClr.ValAttr = "954f72"
|
|
|
|
|
|
|
|
thm.ThemeElements.FontScheme.NameAttr = "Office"
|
|
|
|
thm.ThemeElements.FontScheme.MajorFont.Latin.TypefaceAttr = "Calibri Light"
|
|
|
|
thm.ThemeElements.FontScheme.MinorFont.Latin.TypefaceAttr = "Calibri"
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
thm.ThemeElements.FmtScheme.NameAttr = unioffice.String("Office")
|
2017-08-28 20:56:18 -05:00
|
|
|
// fills
|
|
|
|
fp := dml.NewEG_FillProperties()
|
|
|
|
thm.ThemeElements.FmtScheme.FillStyleLst.EG_FillProperties = append(thm.ThemeElements.FmtScheme.FillStyleLst.EG_FillProperties, fp)
|
|
|
|
fp.SolidFill = &dml.CT_SolidColorFillProperties{
|
|
|
|
SchemeClr: &dml.CT_SchemeColor{ValAttr: dml.ST_SchemeColorValPhClr},
|
|
|
|
}
|
|
|
|
|
|
|
|
// rot fill 0
|
|
|
|
fp = dml.NewEG_FillProperties()
|
|
|
|
thm.ThemeElements.FmtScheme.FillStyleLst.EG_FillProperties = append(thm.ThemeElements.FmtScheme.FillStyleLst.EG_FillProperties, fp)
|
|
|
|
// add it twice so OSX word doesn't choke
|
|
|
|
thm.ThemeElements.FmtScheme.FillStyleLst.EG_FillProperties = append(thm.ThemeElements.FmtScheme.FillStyleLst.EG_FillProperties, fp)
|
2019-05-04 13:54:29 +00:00
|
|
|
fp.GradFill = &dml.CT_GradientFillProperties{RotWithShapeAttr: unioffice.Bool(true),
|
2017-08-28 20:56:18 -05:00
|
|
|
GsLst: &dml.CT_GradientStopList{},
|
|
|
|
Lin: &dml.CT_LinearShadeProperties{}}
|
2019-05-04 13:54:29 +00:00
|
|
|
fp.GradFill.Lin.AngAttr = unioffice.Int32(5400000)
|
|
|
|
fp.GradFill.Lin.ScaledAttr = unioffice.Bool(false)
|
2017-08-28 20:56:18 -05:00
|
|
|
|
|
|
|
gs := dml.NewCT_GradientStop()
|
2019-05-04 13:54:29 +00:00
|
|
|
gs.PosAttr.ST_PositiveFixedPercentageDecimal = unioffice.Int32(0)
|
2017-08-28 20:56:18 -05:00
|
|
|
gs.SchemeClr = &dml.CT_SchemeColor{ValAttr: dml.ST_SchemeColorValPhClr}
|
|
|
|
fp.GradFill.GsLst.Gs = append(fp.GradFill.GsLst.Gs, gs)
|
|
|
|
|
|
|
|
gs = dml.NewCT_GradientStop()
|
2019-05-04 13:54:29 +00:00
|
|
|
gs.PosAttr.ST_PositiveFixedPercentageDecimal = unioffice.Int32(50000)
|
2017-08-28 20:56:18 -05:00
|
|
|
gs.SchemeClr = &dml.CT_SchemeColor{ValAttr: dml.ST_SchemeColorValPhClr}
|
|
|
|
fp.GradFill.GsLst.Gs = append(fp.GradFill.GsLst.Gs, gs)
|
|
|
|
|
|
|
|
thm.ThemeElements.FmtScheme.LnStyleLst = dml.NewCT_LineStyleList()
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
lp := dml.NewCT_LineProperties()
|
2019-05-04 13:54:29 +00:00
|
|
|
lp.WAttr = unioffice.Int32(int32(6350 * (i + 1)))
|
2017-08-28 20:56:18 -05:00
|
|
|
lp.CapAttr = dml.ST_LineCapFlat
|
|
|
|
lp.CmpdAttr = dml.ST_CompoundLineSng
|
|
|
|
lp.AlgnAttr = dml.ST_PenAlignmentCtr
|
|
|
|
thm.ThemeElements.FmtScheme.LnStyleLst.Ln = append(thm.ThemeElements.FmtScheme.LnStyleLst.Ln, lp)
|
|
|
|
}
|
|
|
|
|
|
|
|
thm.ThemeElements.FmtScheme.EffectStyleLst = dml.NewCT_EffectStyleList()
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
ef := dml.NewCT_EffectStyleItem()
|
|
|
|
ef.EffectLst = dml.NewCT_EffectList()
|
|
|
|
thm.ThemeElements.FmtScheme.EffectStyleLst.EffectStyle = append(thm.ThemeElements.FmtScheme.EffectStyleLst.EffectStyle, ef)
|
|
|
|
}
|
|
|
|
|
|
|
|
sf := dml.NewEG_FillProperties()
|
|
|
|
sf.SolidFill = &dml.CT_SolidColorFillProperties{
|
|
|
|
SchemeClr: &dml.CT_SchemeColor{ValAttr: dml.ST_SchemeColorValPhClr},
|
|
|
|
}
|
|
|
|
thm.ThemeElements.FmtScheme.BgFillStyleLst.EG_FillProperties = append(thm.ThemeElements.FmtScheme.BgFillStyleLst.EG_FillProperties,
|
|
|
|
sf)
|
|
|
|
thm.ThemeElements.FmtScheme.BgFillStyleLst.EG_FillProperties = append(thm.ThemeElements.FmtScheme.BgFillStyleLst.EG_FillProperties,
|
|
|
|
sf)
|
|
|
|
thm.ThemeElements.FmtScheme.BgFillStyleLst.EG_FillProperties = append(thm.ThemeElements.FmtScheme.BgFillStyleLst.EG_FillProperties,
|
|
|
|
fp)
|
|
|
|
|
|
|
|
p.themes = append(p.themes, thm)
|
2019-05-04 13:54:29 +00:00
|
|
|
themeFn := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.ThemeType, 1)
|
|
|
|
p.ContentTypes.AddOverride(themeFn, unioffice.ThemeContentType)
|
|
|
|
p.prels.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.OfficeDocumentType, 1, unioffice.ThemeType)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
thmRel := common.NewRelationships()
|
|
|
|
p.themeRels = append(p.themeRels, thmRel)
|
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
// X returns the inner wrapped XML type.
|
|
|
|
func (p *Presentation) X() *pml.Presentation {
|
|
|
|
return p.x
|
|
|
|
}
|
|
|
|
|
2017-11-18 11:43:37 -06:00
|
|
|
func (p *Presentation) nextSlideID() uint32 {
|
|
|
|
id := uint32(256)
|
|
|
|
for _, s := range p.x.SldIdLst.SldId {
|
|
|
|
if s.IdAttr >= id {
|
|
|
|
id = s.IdAttr + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
2017-10-03 09:55:27 -05:00
|
|
|
// AddSlide adds a new slide to the presentation.
|
2017-08-28 20:56:18 -05:00
|
|
|
func (p *Presentation) AddSlide() Slide {
|
|
|
|
sd := pml.NewCT_SlideIdListEntry()
|
2017-11-18 11:43:37 -06:00
|
|
|
sd.IdAttr = p.nextSlideID()
|
2017-08-28 20:56:18 -05:00
|
|
|
p.x.SldIdLst.SldId = append(p.x.SldIdLst.SldId, sd)
|
|
|
|
|
|
|
|
slide := pml.NewSld()
|
|
|
|
slide.CSld.SpTree.NvGrpSpPr.CNvPr.IdAttr = 1
|
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm = dml.NewCT_GroupTransform2D()
|
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.Off = dml.NewCT_Point2D()
|
2019-05-04 13:54:29 +00:00
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.Off.XAttr.ST_CoordinateUnqualified = unioffice.Int64(0)
|
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.Off.YAttr.ST_CoordinateUnqualified = unioffice.Int64(0)
|
2017-08-28 20:56:18 -05:00
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.Ext = dml.NewCT_PositiveSize2D()
|
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.Ext.CxAttr = int64(0 * measurement.Point)
|
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.Ext.CyAttr = int64(0 * measurement.Point)
|
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.ChOff = slide.CSld.SpTree.GrpSpPr.Xfrm.Off
|
|
|
|
slide.CSld.SpTree.GrpSpPr.Xfrm.ChExt = slide.CSld.SpTree.GrpSpPr.Xfrm.Ext
|
|
|
|
|
|
|
|
p.slides = append(p.slides, slide)
|
2019-05-04 13:54:29 +00:00
|
|
|
srelID := p.prels.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.OfficeDocumentType,
|
|
|
|
len(p.slides), unioffice.SlideType)
|
2017-08-28 20:56:18 -05:00
|
|
|
sd.RIdAttr = srelID.ID()
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
slidefn := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideType, len(p.slides))
|
|
|
|
p.ContentTypes.AddOverride(slidefn, unioffice.SlideContentType)
|
2017-08-28 20:56:18 -05:00
|
|
|
|
|
|
|
srel := common.NewRelationships()
|
|
|
|
p.slideRels = append(p.slideRels, srel)
|
2017-10-03 09:55:27 -05:00
|
|
|
// TODO: make the slide layout configurable
|
2019-05-04 13:54:29 +00:00
|
|
|
srel.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.SlideType,
|
|
|
|
len(p.layouts), unioffice.SlideLayoutType)
|
2017-08-28 20:56:18 -05:00
|
|
|
|
2017-11-20 18:24:12 -06:00
|
|
|
return Slide{sd, slide, p}
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
|
|
|
|
2017-10-03 09:55:27 -05:00
|
|
|
// AddSlideWithLayout adds a new slide with content copied from a layout. Normally you should
|
|
|
|
// use AddDefaultSlideWithLayout as it will do some post processing similar to PowerPoint to
|
|
|
|
// clear place holder text, etc.
|
|
|
|
func (p *Presentation) AddSlideWithLayout(l SlideLayout) (Slide, error) {
|
|
|
|
sd := pml.NewCT_SlideIdListEntry()
|
|
|
|
sd.IdAttr = 256
|
|
|
|
for _, id := range p.x.SldIdLst.SldId {
|
|
|
|
if id.IdAttr >= sd.IdAttr {
|
|
|
|
sd.IdAttr = id.IdAttr + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.x.SldIdLst.SldId = append(p.x.SldIdLst.SldId, sd)
|
|
|
|
|
|
|
|
slide := pml.NewSld()
|
|
|
|
|
|
|
|
buf := bytes.Buffer{}
|
|
|
|
enc := xml.NewEncoder(&buf)
|
|
|
|
start := xml.StartElement{Name: xml.Name{Local: "slide"}}
|
|
|
|
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns"}, Value: "http://schemas.openxmlformats.org/presentationml/2006/main"})
|
|
|
|
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:a"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/main"})
|
|
|
|
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:p"}, Value: "http://schemas.openxmlformats.org/presentationml/2006/main"})
|
|
|
|
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:r"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"})
|
|
|
|
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:sh"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"})
|
|
|
|
start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:xml"}, Value: "http://www.w3.org/XML/1998/namespace"})
|
|
|
|
|
|
|
|
if err := l.x.CSld.MarshalXML(enc, start); err != nil {
|
|
|
|
return Slide{}, err
|
|
|
|
}
|
|
|
|
enc.Flush()
|
|
|
|
|
|
|
|
dec := xml.NewDecoder(&buf)
|
|
|
|
slide.CSld = pml.NewCT_CommonSlideData()
|
|
|
|
if err := dec.Decode(slide.CSld); err != nil {
|
|
|
|
return Slide{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// clear layout name on the slide
|
|
|
|
slide.CSld.NameAttr = nil
|
|
|
|
//, chc := range slide.CSld.SpTree.Choice {
|
2019-06-21 21:37:04 +02:00
|
|
|
slide.CSld.SpTree.Choice = removeChoicesWithPics(slide.CSld.SpTree.Choice)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
p.slides = append(p.slides, slide)
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
srelID := p.prels.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.OfficeDocumentType,
|
|
|
|
len(p.slides), unioffice.SlideType)
|
2017-10-03 09:55:27 -05:00
|
|
|
sd.RIdAttr = srelID.ID()
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
slidefn := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideType, len(p.slides))
|
|
|
|
p.ContentTypes.AddOverride(slidefn, unioffice.SlideContentType)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
srel := common.NewRelationships()
|
|
|
|
p.slideRels = append(p.slideRels, srel)
|
|
|
|
for i, lout := range p.layouts {
|
|
|
|
if lout == l.X() {
|
2019-05-04 13:54:29 +00:00
|
|
|
srel.AddAutoRelationship(unioffice.DocTypePresentation, unioffice.SlideType,
|
|
|
|
i+1, unioffice.SlideLayoutType)
|
2017-10-03 09:55:27 -05:00
|
|
|
}
|
|
|
|
}
|
2017-11-20 18:24:12 -06:00
|
|
|
csld := Slide{sd, slide, p}
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
return csld, nil
|
|
|
|
}
|
|
|
|
|
2019-06-21 21:37:04 +02:00
|
|
|
func removeChoicesWithPics(choices []*pml.CT_GroupShapeChoice) []*pml.CT_GroupShapeChoice {
|
|
|
|
var newChoices []*pml.CT_GroupShapeChoice
|
|
|
|
for _, aChoice := range choices {
|
|
|
|
if len(aChoice.Pic) == 0 {
|
|
|
|
newChoices = append(newChoices, aChoice)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newChoices
|
|
|
|
}
|
|
|
|
|
2017-10-03 09:55:27 -05:00
|
|
|
// AddDefaultSlideWithLayout tries to replicate what PowerPoint does when
|
|
|
|
// inserting a slide with a new style by clearing placeholder content and removing
|
|
|
|
// some placeholders. Use AddSlideWithLayout if you need more control.
|
|
|
|
func (p *Presentation) AddDefaultSlideWithLayout(l SlideLayout) (Slide, error) {
|
|
|
|
sld, err := p.AddSlideWithLayout(l)
|
|
|
|
|
|
|
|
for _, ph := range sld.PlaceHolders() {
|
|
|
|
// clear all placeholder content
|
|
|
|
ph.Clear()
|
|
|
|
// and drop some of the placeholders (footer, slide date/time, slide number)
|
|
|
|
switch ph.Type() {
|
|
|
|
case pml.ST_PlaceholderTypeFtr, pml.ST_PlaceholderTypeDt, pml.ST_PlaceholderTypeSldNum:
|
|
|
|
ph.Remove()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sld, err
|
|
|
|
}
|
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
// Save writes the presentation out to a writer in the Zip package format
|
|
|
|
func (p *Presentation) Save(w io.Writer) error {
|
2017-10-03 09:55:27 -05:00
|
|
|
if err := p.x.Validate(); err != nil {
|
|
|
|
log.Printf("validation error in document: %s", err)
|
|
|
|
}
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
dt := unioffice.DocTypePresentation
|
2017-10-03 09:55:27 -05:00
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
z := zip.NewWriter(w)
|
|
|
|
defer z.Close()
|
2019-05-04 13:54:29 +00:00
|
|
|
if err := zippkg.MarshalXML(z, unioffice.BaseRelsFilename, p.Rels.X()); err != nil {
|
2017-08-28 20:56:18 -05:00
|
|
|
return err
|
|
|
|
}
|
2019-05-04 13:54:29 +00:00
|
|
|
if err := zippkg.MarshalXMLByType(z, dt, unioffice.ExtendedPropertiesType, p.AppProperties.X()); err != nil {
|
2017-08-28 20:56:18 -05:00
|
|
|
return err
|
|
|
|
}
|
2019-05-04 13:54:29 +00:00
|
|
|
if err := zippkg.MarshalXMLByType(z, dt, unioffice.CorePropertiesType, p.CoreProperties.X()); err != nil {
|
2017-08-28 20:56:18 -05:00
|
|
|
return err
|
|
|
|
}
|
2017-10-03 09:55:27 -05:00
|
|
|
if p.Thumbnail != nil {
|
|
|
|
tn, err := z.Create("docProps/thumbnail.jpeg")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := jpeg.Encode(tn, p.Thumbnail, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
2017-10-03 09:55:27 -05:00
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
documentFn := unioffice.AbsoluteFilename(dt, unioffice.OfficeDocumentType, 0)
|
2017-10-03 09:55:27 -05:00
|
|
|
if err := zippkg.MarshalXML(z, documentFn, p.x); err != nil {
|
2017-08-28 20:56:18 -05:00
|
|
|
return err
|
|
|
|
}
|
2017-10-03 09:55:27 -05:00
|
|
|
if err := zippkg.MarshalXML(z, zippkg.RelationsPathFor(documentFn), p.prels.X()); err != nil {
|
2017-08-28 20:56:18 -05:00
|
|
|
return err
|
|
|
|
}
|
2017-10-03 09:55:27 -05:00
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
for i, slide := range p.slides {
|
2019-05-04 13:54:29 +00:00
|
|
|
spath := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideType, i+1)
|
2017-08-28 20:56:18 -05:00
|
|
|
zippkg.MarshalXML(z, spath, slide)
|
2017-10-03 09:55:27 -05:00
|
|
|
if !p.slideRels[i].IsEmpty() {
|
|
|
|
rpath := zippkg.RelationsPathFor(spath)
|
|
|
|
zippkg.MarshalXML(z, rpath, p.slideRels[i].X())
|
|
|
|
}
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
|
|
|
for i, m := range p.masters {
|
2019-05-04 13:54:29 +00:00
|
|
|
mpath := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideMasterType, i+1)
|
2017-08-28 20:56:18 -05:00
|
|
|
zippkg.MarshalXML(z, mpath, m)
|
2017-10-03 09:55:27 -05:00
|
|
|
if !p.masterRels[i].IsEmpty() {
|
|
|
|
rpath := zippkg.RelationsPathFor(mpath)
|
|
|
|
zippkg.MarshalXML(z, rpath, p.masterRels[i].X())
|
|
|
|
}
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
|
|
|
for i, l := range p.layouts {
|
2019-05-04 13:54:29 +00:00
|
|
|
mpath := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideLayoutType, i+1)
|
2017-08-28 20:56:18 -05:00
|
|
|
zippkg.MarshalXML(z, mpath, l)
|
2017-10-03 09:55:27 -05:00
|
|
|
if !p.layoutRels[i].IsEmpty() {
|
|
|
|
rpath := zippkg.RelationsPathFor(mpath)
|
|
|
|
zippkg.MarshalXML(z, rpath, p.layoutRels[i].X())
|
|
|
|
}
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
|
|
|
for i, l := range p.themes {
|
2019-05-04 13:54:29 +00:00
|
|
|
mpath := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.ThemeType, i+1)
|
2017-10-03 09:55:27 -05:00
|
|
|
zippkg.MarshalXML(z, mpath, l)
|
|
|
|
if !p.themeRels[i].IsEmpty() {
|
|
|
|
rpath := zippkg.RelationsPathFor(mpath)
|
|
|
|
zippkg.MarshalXML(z, rpath, p.themeRels[i].X())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, img := range p.Images {
|
2019-05-04 13:54:29 +00:00
|
|
|
fn := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.ImageType, i+1)
|
2018-07-08 17:26:24 -05:00
|
|
|
fn = fn[0:len(fn)-3] + strings.ToLower(img.Format())
|
2017-10-03 09:55:27 -05:00
|
|
|
if img.Path() != "" {
|
|
|
|
if err := zippkg.AddFileFromDisk(z, fn, img.Path()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2019-05-04 13:54:29 +00:00
|
|
|
unioffice.Log("unsupported image source: %+v", img)
|
2017-10-03 09:55:27 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
if err := zippkg.MarshalXML(z, unioffice.ContentTypesFilename, p.ContentTypes.X()); err != nil {
|
2017-10-03 09:55:27 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := p.WriteExtraFiles(z); err != nil {
|
|
|
|
return err
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SaveToFile writes the Presentation out to a file.
|
|
|
|
func (p *Presentation) SaveToFile(path string) error {
|
|
|
|
f, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
return p.Save(f)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Presentation) Validate() error {
|
|
|
|
if err := p.x.Validate(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-18 11:43:37 -06:00
|
|
|
for i, s := range p.Slides() {
|
2017-08-28 20:56:18 -05:00
|
|
|
if err := s.ValidateWithPath(fmt.Sprintf("Slide[%d]", i)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-11-18 11:43:37 -06:00
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
for i, sm := range p.masters {
|
|
|
|
if err := sm.ValidateWithPath(fmt.Sprintf("SlideMaster[%d]", i)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for i, sl := range p.layouts {
|
2017-09-03 11:05:27 -05:00
|
|
|
if err := sl.ValidateWithPath(fmt.Sprintf("SlideLayout[%d]", i)); err != nil {
|
2017-08-28 20:56:18 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
// SlideMasters returns the slide masters defined in the presentation.
|
|
|
|
func (p *Presentation) SlideMasters() []SlideMaster {
|
|
|
|
ret := []SlideMaster{}
|
|
|
|
for i, m := range p.masters {
|
|
|
|
|
|
|
|
ret = append(ret, SlideMaster{p, p.masterRels[i], m})
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// SlideLayouts returns the slide layouts defined in the presentation.
|
|
|
|
func (p *Presentation) SlideLayouts() []SlideLayout {
|
|
|
|
ret := []SlideLayout{}
|
|
|
|
for _, l := range p.layouts {
|
|
|
|
ret = append(ret, SlideLayout{l})
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Presentation) onNewRelationship(decMap *zippkg.DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship, src zippkg.Target) error {
|
2019-05-04 13:54:29 +00:00
|
|
|
dt := unioffice.DocTypePresentation
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
switch typ {
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.OfficeDocumentType:
|
2017-10-03 09:55:27 -05:00
|
|
|
p.x = pml.NewPresentation()
|
|
|
|
decMap.AddTarget(target, p.x, typ, 0)
|
|
|
|
decMap.AddTarget(zippkg.RelationsPathFor(target), p.prels.X(), typ, 0)
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, 0)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.CorePropertiesType:
|
2017-10-03 09:55:27 -05:00
|
|
|
decMap.AddTarget(target, p.CoreProperties.X(), typ, 0)
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, 0)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.ExtendedPropertiesType:
|
2017-10-03 09:55:27 -05:00
|
|
|
decMap.AddTarget(target, p.AppProperties.X(), typ, 0)
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, 0)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.SlideType:
|
2017-10-03 09:55:27 -05:00
|
|
|
sld := pml.NewSld()
|
|
|
|
p.slides = append(p.slides, sld)
|
|
|
|
decMap.AddTarget(target, sld, typ, uint32(len(p.slides)))
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, len(p.slides))
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
slRel := common.NewRelationships()
|
|
|
|
decMap.AddTarget(zippkg.RelationsPathFor(target), slRel.X(), typ, 0)
|
|
|
|
p.slideRels = append(p.slideRels, slRel)
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.SlideMasterType:
|
2017-10-03 09:55:27 -05:00
|
|
|
sm := pml.NewSldMaster()
|
|
|
|
if !decMap.AddTarget(target, sm, typ, uint32(len(p.masters)+1)) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
p.masters = append(p.masters, sm)
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, len(p.masters))
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
// look for master rels
|
|
|
|
smRel := common.NewRelationships()
|
|
|
|
decMap.AddTarget(zippkg.RelationsPathFor(target), smRel.X(), typ, 0)
|
|
|
|
p.masterRels = append(p.masterRels, smRel)
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.SlideLayoutType:
|
2017-10-03 09:55:27 -05:00
|
|
|
sl := pml.NewSldLayout()
|
|
|
|
if !decMap.AddTarget(target, sl, typ, uint32(len(p.layouts)+1)) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
p.layouts = append(p.layouts, sl)
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, len(p.layouts))
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
// look for layout rels
|
|
|
|
slRel := common.NewRelationships()
|
|
|
|
decMap.AddTarget(zippkg.RelationsPathFor(target), slRel.X(), typ, 0)
|
|
|
|
p.layoutRels = append(p.layoutRels, slRel)
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.ThumbnailType:
|
2017-10-03 09:55:27 -05:00
|
|
|
// read our thumbnail
|
|
|
|
for i, f := range files {
|
|
|
|
if f == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if f.Name == target {
|
|
|
|
rc, err := f.Open()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error reading thumbnail: %s", err)
|
|
|
|
}
|
|
|
|
p.Thumbnail, _, err = image.Decode(rc)
|
|
|
|
rc.Close()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error decoding thumbnail: %s", err)
|
|
|
|
}
|
|
|
|
files[i] = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.ThemeType:
|
2017-10-03 09:55:27 -05:00
|
|
|
thm := dml.NewTheme()
|
|
|
|
if !decMap.AddTarget(target, thm, typ, uint32(len(p.themes)+1)) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
p.themes = append(p.themes, thm)
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, len(p.themes))
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
// look for theme rels
|
|
|
|
thmRel := common.NewRelationships()
|
|
|
|
decMap.AddTarget(zippkg.RelationsPathFor(target), thmRel.X(), typ, 0)
|
|
|
|
p.themeRels = append(p.themeRels, thmRel)
|
|
|
|
|
2019-05-04 13:54:29 +00:00
|
|
|
case unioffice.ImageType:
|
2018-01-16 08:23:18 -06:00
|
|
|
// we use path.Clean instead of filepath.Clean to ensure we
|
|
|
|
// end up with forward separators
|
|
|
|
target = path.Clean(target)
|
2017-10-03 09:55:27 -05:00
|
|
|
for i, f := range files {
|
|
|
|
if f == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if f.Name == target {
|
|
|
|
path, err := zippkg.ExtractToDiskTmp(f, p.TmpPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
img, err := common.ImageFromFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
iref := common.MakeImageRef(img, &p.DocBase, p.prels)
|
|
|
|
p.Images = append(p.Images, iref)
|
|
|
|
files[i] = nil
|
|
|
|
decMap.RecordIndex(target, len(p.Images))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx := decMap.IndexFor(target)
|
2019-05-04 13:54:29 +00:00
|
|
|
rel.TargetAttr = unioffice.RelativeFilename(dt, src.Typ, typ, idx)
|
2017-10-03 09:55:27 -05:00
|
|
|
|
|
|
|
default:
|
2019-05-04 13:54:29 +00:00
|
|
|
unioffice.Log("unsupported relationship type: %s tgt: %s", typ, target)
|
2017-10-03 09:55:27 -05:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Slides returns the slides in the presentation.
|
|
|
|
func (p *Presentation) Slides() []Slide {
|
|
|
|
ret := []Slide{}
|
|
|
|
for i, v := range p.slides {
|
2017-11-20 18:24:12 -06:00
|
|
|
ret = append(ret, Slide{p.x.SldIdLst.SldId[i], v, p})
|
2017-10-03 09:55:27 -05:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveSlide removes a slide from a presentation.
|
|
|
|
func (p *Presentation) RemoveSlide(s Slide) error {
|
|
|
|
removed := false
|
|
|
|
slideIdx := 0
|
|
|
|
for i, v := range p.slides {
|
|
|
|
if v == s.x {
|
|
|
|
if p.x.SldIdLst.SldId[i] != s.sid {
|
|
|
|
return errors.New("inconsistency in slides and ID list")
|
|
|
|
}
|
|
|
|
copy(p.slides[i:], p.slides[i+1:])
|
|
|
|
p.slides = p.slides[0 : len(p.slides)-1]
|
|
|
|
|
|
|
|
copy(p.slideRels[i:], p.slideRels[i+1:])
|
|
|
|
p.slideRels = p.slideRels[0 : len(p.slideRels)-1]
|
|
|
|
|
|
|
|
copy(p.x.SldIdLst.SldId[i:], p.x.SldIdLst.SldId[i+1:])
|
|
|
|
p.x.SldIdLst.SldId = p.x.SldIdLst.SldId[0 : len(p.x.SldIdLst.SldId)-1]
|
|
|
|
|
|
|
|
removed = true
|
|
|
|
slideIdx = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !removed {
|
|
|
|
return errors.New("unable to find slide")
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove it from content types
|
2019-05-04 13:54:29 +00:00
|
|
|
fn := unioffice.AbsoluteFilename(unioffice.DocTypePresentation, unioffice.SlideType, slideIdx+1)
|
2017-10-03 09:55:27 -05:00
|
|
|
p.ContentTypes.RemoveOverride(fn)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLayoutByName retrieves a slide layout given a layout name.
|
|
|
|
func (p *Presentation) GetLayoutByName(name string) (SlideLayout, error) {
|
|
|
|
for _, l := range p.layouts {
|
|
|
|
if l.CSld.NameAttr != nil && name == *l.CSld.NameAttr {
|
|
|
|
return SlideLayout{l}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SlideLayout{}, errors.New("unable to find layout with that name")
|
|
|
|
}
|
2017-11-20 18:24:12 -06:00
|
|
|
|
|
|
|
// AddImage adds an image to the document package, returning a reference that
|
|
|
|
// can be used to add the image to a run and place it in the document contents.
|
|
|
|
func (p *Presentation) AddImage(i common.Image) (common.ImageRef, error) {
|
|
|
|
r := common.MakeImageRef(i, &p.DocBase, p.prels)
|
2019-03-29 17:26:52 +01:00
|
|
|
if i.Data == nil && i.Path == "" {
|
|
|
|
return r, errors.New("image must have data or a path")
|
2017-11-20 18:24:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if i.Format == "" {
|
|
|
|
return r, errors.New("image must have a valid format")
|
|
|
|
}
|
|
|
|
if i.Size.X == 0 || i.Size.Y == 0 {
|
|
|
|
return r, errors.New("image must have a valid size")
|
|
|
|
}
|
|
|
|
|
|
|
|
p.Images = append(p.Images, r)
|
|
|
|
fn := fmt.Sprintf("media/image%d.%s", len(p.Images), i.Format)
|
2019-05-04 13:54:29 +00:00
|
|
|
p.prels.AddRelationship(fn, unioffice.ImageType)
|
2017-11-20 18:24:12 -06:00
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetImageByRelID returns an ImageRef with the associated relation ID in the
|
|
|
|
// document.
|
|
|
|
func (p *Presentation) GetImageByRelID(relID string) (common.ImageRef, bool) {
|
|
|
|
for _, img := range p.Images {
|
|
|
|
if img.RelID() == relID {
|
|
|
|
return img, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return common.ImageRef{}, false
|
|
|
|
}
|