mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +08:00
meta: move filename information out of packages
This seems like the better choice, a lot of the logic is shared between the document types, and it allows generating filenames in a single place. The only downside is that you must pass in the document type as some content types have different typical names depending on the document type (e.g. an 'office document' is the main document.xml, workbook.xml and presentation.xml
This commit is contained in:
parent
f07c57cae7
commit
9090e4f93c
@ -8,7 +8,6 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"baliance.com/gooxml/schema/schemas.openxmlformats.org/package/2006/content_types"
|
||||
@ -52,7 +51,7 @@ func (c ContentTypes) AddDefault(fileExtension string, contentType string) {
|
||||
// AddOverride adds an override content type for a given path name.
|
||||
func (c ContentTypes) AddOverride(path, contentType string) {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
log.Printf("override path %s should start with a '/'", path)
|
||||
path = "/" + path
|
||||
}
|
||||
or := content_types.NewOverride()
|
||||
or.PartNameAttr = path
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"baliance.com/gooxml"
|
||||
"baliance.com/gooxml/schema/schemas.openxmlformats.org/package/2006/relationships"
|
||||
)
|
||||
|
||||
@ -48,6 +49,13 @@ func (r Relationships) FindRIDForN(i int, t string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// AddAutoRelationship adds a relationship with an automatically generated
|
||||
// filename based off of the type. It should be preferred over AddRelationship
|
||||
// to ensure consistent filenames are maintained.
|
||||
func (r Relationships) AddAutoRelationship(dt gooxml.DocType, idx int, ctype string) Relationship {
|
||||
return r.AddRelationship(gooxml.RelativeFilename(dt, ctype, idx), ctype)
|
||||
}
|
||||
|
||||
// AddRelationship adds a relationship.
|
||||
func (r Relationships) AddRelationship(target, ctype string) Relationship {
|
||||
if !strings.HasPrefix(ctype, "http://") {
|
||||
|
124
filenames.go
Normal file
124
filenames.go
Normal file
@ -0,0 +1,124 @@
|
||||
// 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 gooxml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Common filenames used in zip packages.
|
||||
const (
|
||||
ContentTypesFilename = "[Content_Types].xml"
|
||||
BaseRelsFilename = "_rels/.rels"
|
||||
)
|
||||
|
||||
type DocType byte
|
||||
|
||||
const (
|
||||
Unknown DocType = iota
|
||||
DocTypeSpreadsheet
|
||||
DocTypeDocument
|
||||
DocTypePresentation
|
||||
)
|
||||
|
||||
func RelativeFilename(dt DocType, typ string, index int) string {
|
||||
switch typ {
|
||||
case CorePropertiesType:
|
||||
return "docProps/core.xml"
|
||||
case ExtendedPropertiesType:
|
||||
return "docProps/app.xml"
|
||||
case ThumbnailType:
|
||||
return "docProps/thumbnail.jpeg"
|
||||
|
||||
case StylesType:
|
||||
return "styles.xml"
|
||||
case ChartType:
|
||||
return fmt.Sprintf("charts/chart%d.xml", index)
|
||||
case DrawingType:
|
||||
return fmt.Sprintf("drawings/drawing%d.xml", index)
|
||||
|
||||
case ThemeType, ThemeContentType:
|
||||
return fmt.Sprintf("theme/theme%d.xml", index)
|
||||
case OfficeDocumentType:
|
||||
switch dt {
|
||||
case DocTypeSpreadsheet:
|
||||
return "xl/workbook.xml"
|
||||
default:
|
||||
log.Printf("unsupported type %s pair and %v", typ, dt)
|
||||
}
|
||||
|
||||
// SML
|
||||
case WorksheetType, WorksheetContentType:
|
||||
return fmt.Sprintf("worksheets/sheet%d.xml", index)
|
||||
|
||||
case SharedStingsType, SharedStringsContentType:
|
||||
return "sharedStrings.xml"
|
||||
default:
|
||||
log.Printf("unsupported type %s", typ)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func AbsoluteFilename(dt DocType, typ string, index int) string {
|
||||
switch typ {
|
||||
case CorePropertiesType:
|
||||
return "docProps/core.xml"
|
||||
case ExtendedPropertiesType:
|
||||
return "docProps/app.xml"
|
||||
case ThumbnailType:
|
||||
return "docProps/thumbnail.jpeg"
|
||||
|
||||
case OfficeDocumentType:
|
||||
switch dt {
|
||||
case DocTypeSpreadsheet:
|
||||
return "xl/workbook.xml"
|
||||
default:
|
||||
log.Printf("unsupported type %s pair and %v", typ, dt)
|
||||
}
|
||||
|
||||
case ThemeType, ThemeContentType:
|
||||
switch dt {
|
||||
case DocTypeSpreadsheet:
|
||||
return fmt.Sprintf("xl/theme/theme%d.xml", index)
|
||||
default:
|
||||
log.Printf("unsupported type %s pair and %v", typ, dt)
|
||||
}
|
||||
|
||||
case StylesType:
|
||||
switch dt {
|
||||
case DocTypeSpreadsheet:
|
||||
return "xl/styles.xml"
|
||||
}
|
||||
|
||||
case ChartType:
|
||||
switch dt {
|
||||
case DocTypeSpreadsheet:
|
||||
return fmt.Sprintf("xl/charts/chart%d.xml", index)
|
||||
default:
|
||||
log.Printf("unsupported type %s pair and %v", typ, dt)
|
||||
}
|
||||
|
||||
case DrawingType:
|
||||
switch dt {
|
||||
case DocTypeSpreadsheet:
|
||||
return fmt.Sprintf("xl/drawings/drawing%d.xml", index)
|
||||
default:
|
||||
log.Fatalf("unsupported type %s pair and %v", typ, dt)
|
||||
}
|
||||
// SML
|
||||
case WorksheetType, WorksheetContentType:
|
||||
return fmt.Sprintf("xl/worksheets/sheet%d.xml", index)
|
||||
case SharedStingsType, SharedStringsContentType:
|
||||
return "xl/sharedStrings.xml"
|
||||
|
||||
default:
|
||||
log.Fatalf("unsupported type %s", typ)
|
||||
}
|
||||
return ""
|
||||
}
|
@ -26,8 +26,10 @@ const (
|
||||
|
||||
// SML
|
||||
WorksheetType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
||||
SharedStringsContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"
|
||||
WorksheetContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
|
||||
SharedStingsType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"
|
||||
SharedStringsContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"
|
||||
SMLStyleSheetContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"
|
||||
|
||||
// WML
|
||||
HeaderType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
|
||||
|
@ -72,14 +72,15 @@ func (wb *Workbook) AddSheet() Sheet {
|
||||
wb.xwsRels = append(wb.xwsRels, common.NewRelationships())
|
||||
ws.SheetData = sml.NewCT_SheetData()
|
||||
|
||||
dt := gooxml.DocTypeSpreadsheet
|
||||
// update the references
|
||||
rid := wb.wbRels.AddRelationship(fmt.Sprintf("worksheets/sheet%d.xml", len(wb.x.Sheets.Sheet)),
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet")
|
||||
rid := wb.wbRels.AddAutoRelationship(dt, len(wb.x.Sheets.Sheet), gooxml.WorksheetType)
|
||||
rs.IdAttr = rid.ID()
|
||||
|
||||
// add the content type
|
||||
wb.ContentTypes.AddOverride(fmt.Sprintf("/xl/worksheets/sheet%d.xml", len(wb.x.Sheets.Sheet)),
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml")
|
||||
|
||||
wb.ContentTypes.AddOverride(gooxml.AbsoluteFilename(dt, gooxml.WorksheetContentType, len(wb.x.Sheets.Sheet)),
|
||||
gooxml.WorksheetContentType)
|
||||
|
||||
return Sheet{wb, rs, ws}
|
||||
}
|
||||
@ -98,42 +99,49 @@ func (wb *Workbook) SaveToFile(path string) error {
|
||||
func (wb *Workbook) Save(w io.Writer) error {
|
||||
z := zip.NewWriter(w)
|
||||
defer z.Close()
|
||||
dt := gooxml.DocTypeSpreadsheet
|
||||
|
||||
if err := zippkg.MarshalXML(z, zippkg.ContentTypesFilename, wb.ContentTypes.X()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zippkg.MarshalXML(z, zippkg.BaseRelsFilename, wb.Rels.X()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zippkg.MarshalXML(z, zippkg.AppPropsFilename, wb.AppProperties.X()); err != nil {
|
||||
if err := zippkg.MarshalXMLByType(z, dt, gooxml.ExtendedPropertiesType, wb.AppProperties.X()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zippkg.MarshalXML(z, zippkg.CorePropsFilename, wb.CoreProperties.X()); err != nil {
|
||||
if err := zippkg.MarshalXMLByType(z, dt, gooxml.CorePropertiesType, wb.CoreProperties.X()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zippkg.MarshalXML(z, "xl/workbook.xml", wb.x); err != nil {
|
||||
|
||||
workbookFn := gooxml.AbsoluteFilename(dt, gooxml.OfficeDocumentType, 0)
|
||||
if err := zippkg.MarshalXML(z, workbookFn, wb.x); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zippkg.MarshalXML(z, "xl/styles.xml", wb.StyleSheet.X()); err != nil {
|
||||
if err := zippkg.MarshalXML(z, zippkg.RelationsPathFor(workbookFn), wb.wbRels.X()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zippkg.MarshalXML(z, "xl/sharedStrings.xml", wb.SharedStrings.X()); err != nil {
|
||||
|
||||
if err := zippkg.MarshalXMLByType(z, dt, gooxml.StylesType, wb.StyleSheet.X()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zippkg.MarshalXML(z, "xl/_rels/workbook.xml.rels", wb.wbRels.X()); err != nil {
|
||||
|
||||
if err := zippkg.MarshalXMLByType(z, dt, gooxml.SharedStingsType, wb.SharedStrings.X()); err != nil {
|
||||
return err
|
||||
}
|
||||
for i, thm := range wb.themes {
|
||||
if err := zippkg.MarshalXML(z, fmt.Sprintf("xl/theme/theme%d.xml", i+1), thm); err != nil {
|
||||
if err := zippkg.MarshalXMLByTypeIndex(z, dt, gooxml.ThemeType, i+1, thm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i, sheet := range wb.xws {
|
||||
fn := fmt.Sprintf("xl/worksheets/sheet%d.xml", i+1)
|
||||
fn := gooxml.AbsoluteFilename(dt, gooxml.WorksheetType, i+1)
|
||||
zippkg.MarshalXML(z, fn, sheet)
|
||||
zippkg.MarshalXML(z, zippkg.RelationsPathFor(fn), wb.xwsRels[i].X())
|
||||
}
|
||||
if wb.Thumbnail != nil {
|
||||
tn, err := z.Create("docProps/thumbnail.jpeg")
|
||||
fn := gooxml.AbsoluteFilename(dt, gooxml.ThumbnailType, 0)
|
||||
tn, err := z.Create(fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -142,11 +150,11 @@ func (wb *Workbook) Save(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
for i, chart := range wb.charts {
|
||||
fn := fmt.Sprintf("xl/charts/chart%d.xml", i+1)
|
||||
fn := gooxml.AbsoluteFilename(dt, gooxml.ChartType, i+1)
|
||||
zippkg.MarshalXML(z, fn, chart)
|
||||
}
|
||||
for i, drawing := range wb.drawings {
|
||||
fn := fmt.Sprintf("xl/drawings/drawing%d.xml", i+1)
|
||||
fn := gooxml.AbsoluteFilename(dt, gooxml.DrawingType, i+1)
|
||||
zippkg.MarshalXML(z, fn, drawing)
|
||||
zippkg.MarshalXML(z, zippkg.RelationsPathFor(fn), wb.drawingRels[i].X())
|
||||
}
|
||||
@ -204,6 +212,9 @@ func (wb Workbook) SheetCount() int {
|
||||
}
|
||||
|
||||
func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ string, files []*zip.File, rel *relationships.Relationship) error {
|
||||
dt := gooxml.DocTypeSpreadsheet
|
||||
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, 0)
|
||||
|
||||
switch typ {
|
||||
case gooxml.OfficeDocumentType:
|
||||
wb.x = sml.NewWorkbook()
|
||||
@ -228,7 +239,7 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
|
||||
wb.xwsRels = append(wb.xwsRels, wksRel)
|
||||
// fix the relationship target so it points to where we'll save
|
||||
// the worksheet
|
||||
rel.TargetAttr = fmt.Sprintf("worksheets/sheet%d.xml", len(wb.xws))
|
||||
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.xws))
|
||||
|
||||
case gooxml.StylesType:
|
||||
wb.StyleSheet = NewStyleSheet()
|
||||
@ -271,11 +282,13 @@ func (wb *Workbook) onNewRelationship(decMap *zippkg.DecodeMap, target, typ stri
|
||||
drel := common.NewRelationships()
|
||||
decMap.AddTarget(zippkg.RelationsPathFor(target), drel.X())
|
||||
wb.drawingRels = append(wb.drawingRels, drel)
|
||||
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.drawings))
|
||||
|
||||
case gooxml.ChartType:
|
||||
chart := crt.NewChartSpace()
|
||||
decMap.AddTarget(target, chart)
|
||||
wb.charts = append(wb.charts, chart)
|
||||
rel.TargetAttr = gooxml.RelativeFilename(dt, typ, len(wb.charts))
|
||||
|
||||
default:
|
||||
fmt.Println("unsupported relationship", target, typ)
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"baliance.com/gooxml"
|
||||
)
|
||||
|
||||
// XMLHeader is a header that MarshalXML uses to prefix the XML files it creates.
|
||||
@ -19,6 +21,16 @@ const XMLHeader = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` + "\
|
||||
|
||||
var nl = []byte{'\r', '\n'}
|
||||
|
||||
func MarshalXMLByTypeIndex(z *zip.Writer, dt gooxml.DocType, typ string, idx int, v interface{}) error {
|
||||
fn := gooxml.AbsoluteFilename(dt, typ, idx)
|
||||
return MarshalXML(z, fn, v)
|
||||
}
|
||||
|
||||
func MarshalXMLByType(z *zip.Writer, dt gooxml.DocType, typ string, v interface{}) error {
|
||||
fn := gooxml.AbsoluteFilename(dt, typ, 0)
|
||||
return MarshalXML(z, fn, v)
|
||||
}
|
||||
|
||||
// MarshalXML creates a file inside of a zip and marshals an object as xml, prefixing it
|
||||
// with a standard XML header.
|
||||
func MarshalXML(z *zip.Writer, filename string, v interface{}) error {
|
||||
|
Loading…
x
Reference in New Issue
Block a user