From 6b1e592cbc7b1412da5f6d0badeaf1083117c762 Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 26 Dec 2021 14:55:53 +0800 Subject: [PATCH] This closes #1095, support to set and get document application properties --- README.md | 2 +- README_zh.md | 2 +- calc.go | 1 + docProps.go | 100 +++++++++++++++++++++++++++++++++++++++++++++++ docProps_test.go | 45 +++++++++++++++++++++ test/Book1.xlsx | Bin 20753 -> 20738 bytes xmlApp.go | 58 ++++++++++++++++----------- xmlDrawing.go | 56 +++++++++++++------------- 8 files changed, 212 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 1245c62e..a0d2d3dd 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Excelize is a library written in pure Go providing a set of functions that allow go get github.com/xuri/excelize ``` -- If your packages are managed using [Go Modules](https://blog.golang.org/using-go-modules), please install with following command. +- If your packages are managed using [Go Modules](https://go.dev/blog/using-go-modules), please install with following command. ```bash go get github.com/xuri/excelize/v2 diff --git a/README_zh.md b/README_zh.md index b946bb33..919e954b 100644 --- a/README_zh.md +++ b/README_zh.md @@ -23,7 +23,7 @@ Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基 go get github.com/xuri/excelize ``` -- 如果您使用 [Go Modules](https://blog.golang.org/using-go-modules) 管理软件包,请使用下面的命令来安装最新版本。 +- 如果您使用 [Go Modules](https://go.dev/blog/using-go-modules) 管理软件包,请使用下面的命令来安装最新版本。 ```bash go get github.com/xuri/excelize/v2 diff --git a/calc.go b/calc.go index 682435ae..b096af94 100644 --- a/calc.go +++ b/calc.go @@ -626,6 +626,7 @@ type formulaFuncs struct { // WEIBULL // WEIBULL.DIST // XIRR +// XLOOKUP // XNPV // XOR // YEAR diff --git a/docProps.go b/docProps.go index bf294f29..c8ab27ca 100644 --- a/docProps.go +++ b/docProps.go @@ -19,6 +19,106 @@ import ( "reflect" ) +// SetAppProps provides a function to set document application properties. The +// properties that can be set are: +// +// Property | Description +// -------------------+-------------------------------------------------------------------------- +// Application | The name of the application that created this document. +// | +// ScaleCrop | Indicates the display mode of the document thumbnail. Set this element +// | to TRUE to enable scaling of the document thumbnail to the display. Set +// | this element to FALSE to enable cropping of the document thumbnail to +// | show only sections that will fit the display. +// | +// DocSecurity | Security level of a document as a numeric value. Document security is +// | defined as: +// | 1 - Document is password protected. +// | 2 - Document is recommended to be opened as read-only. +// | 3 - Document is enforced to be opened as read-only. +// | 4 - Document is locked for annotation. +// | +// Company | The name of a company associated with the document. +// | +// LinksUpToDate | Indicates whether hyperlinks in a document are up-to-date. Set this +// | element to TRUE to indicate that hyperlinks are updated. Set this +// | element to FALSE to indicate that hyperlinks are outdated. +// | +// HyperlinksChanged | Specifies that one or more hyperlinks in this part were updated +// | exclusively in this part by a producer. The next producer to open this +// | document shall update the hyperlink relationships with the new +// | hyperlinks specified in this part. +// | +// AppVersion | Specifies the version of the application which produced this document. +// | The content of this element shall be of the form XX.YYYY where X and Y +// | represent numerical values, or the document shall be considered +// | non-conformant. +// +// For example: +// +// err := f.SetAppProps(&excelize.AppProperties{ +// Application: "Microsoft Excel", +// ScaleCrop: true, +// DocSecurity: 3, +// Company: "Company Name", +// LinksUpToDate: true, +// HyperlinksChanged: true, +// AppVersion: "16.0000", +// }) +// +func (f *File) SetAppProps(appProperties *AppProperties) (err error) { + var ( + app *xlsxProperties + fields []string + output []byte + immutable, mutable reflect.Value + field string + ) + app = new(xlsxProperties) + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/app.xml")))). + Decode(app); err != nil && err != io.EOF { + err = fmt.Errorf("xml decode error: %s", err) + return + } + fields = []string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"} + immutable, mutable = reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem() + for _, field = range fields { + immutableField := immutable.FieldByName(field) + switch immutableField.Kind() { + case reflect.Bool: + mutable.FieldByName(field).SetBool(immutableField.Bool()) + case reflect.Int: + mutable.FieldByName(field).SetInt(immutableField.Int()) + default: + mutable.FieldByName(field).SetString(immutableField.String()) + } + } + app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value + output, err = xml.Marshal(app) + f.saveFileList("docProps/app.xml", output) + return +} + +// GetAppProps provides a function to get document application properties. +func (f *File) GetAppProps() (ret *AppProperties, err error) { + var app = new(xlsxProperties) + if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML("docProps/app.xml")))). + Decode(app); err != nil && err != io.EOF { + err = fmt.Errorf("xml decode error: %s", err) + return + } + ret, err = &AppProperties{ + Application: app.Application, + ScaleCrop: app.ScaleCrop, + DocSecurity: app.DocSecurity, + Company: app.Company, + LinksUpToDate: app.LinksUpToDate, + HyperlinksChanged: app.HyperlinksChanged, + AppVersion: app.AppVersion, + }, nil + return +} + // SetDocProps provides a function to set document core properties. The // properties that can be set are: // diff --git a/docProps_test.go b/docProps_test.go index df1b6c6e..a5c35f70 100644 --- a/docProps_test.go +++ b/docProps_test.go @@ -20,6 +20,51 @@ import ( var MacintoshCyrillicCharset = []byte{0x8F, 0xF0, 0xE8, 0xE2, 0xE5, 0xF2, 0x20, 0xEC, 0xE8, 0xF0} +func TestSetAppProps(t *testing.T) { + f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) + if !assert.NoError(t, err) { + t.FailNow() + } + assert.NoError(t, f.SetAppProps(&AppProperties{ + Application: "Microsoft Excel", + ScaleCrop: true, + DocSecurity: 3, + Company: "Company Name", + LinksUpToDate: true, + HyperlinksChanged: true, + AppVersion: "16.0000", + })) + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetAppProps.xlsx"))) + f.Pkg.Store("docProps/app.xml", nil) + assert.NoError(t, f.SetAppProps(&AppProperties{})) + assert.NoError(t, f.Close()) + + // Test unsupported charset + f = NewFile() + f.Pkg.Store("docProps/app.xml", MacintoshCyrillicCharset) + assert.EqualError(t, f.SetAppProps(&AppProperties{}), "xml decode error: XML syntax error on line 1: invalid UTF-8") +} + +func TestGetAppProps(t *testing.T) { + f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) + if !assert.NoError(t, err) { + t.FailNow() + } + props, err := f.GetAppProps() + assert.NoError(t, err) + assert.Equal(t, props.Application, "Microsoft Macintosh Excel") + f.Pkg.Store("docProps/app.xml", nil) + _, err = f.GetAppProps() + assert.NoError(t, err) + assert.NoError(t, f.Close()) + + // Test unsupported charset + f = NewFile() + f.Pkg.Store("docProps/app.xml", MacintoshCyrillicCharset) + _, err = f.GetAppProps() + assert.EqualError(t, err, "xml decode error: XML syntax error on line 1: invalid UTF-8") +} + func TestSetDocProps(t *testing.T) { f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) if !assert.NoError(t, err) { diff --git a/test/Book1.xlsx b/test/Book1.xlsx index 64c9e70977cf2fbbd8ba08a29cd005f16d3d79b3..6a497e33afb45a5764a323326bd1f2f57480184d 100644 GIT binary patch delta 918 zcmbQZh_PuABVT|wGm8iV2L}g(ZO*LVjeM+3EFi{aaVB|22+NB3Ae8l=r4Gt!WV3>@ z_OlB^Sd;g&t4{vS@gA&cvVws8WCI`8%|cvUY!G7<1Vq>&EDLchs1^%xh0SdeTu`2Z zfYj!7lF_U%6Qnk)%d4JEmz+Fb#}DY2M(wXH!OqS&Lf*^&SV~CgF9TAubB4d&0F{0kZYpk z!6~Kz513i@=&Zl+h*hNfWkALfZjGfg0v@QjeRs%wz?&h~QJOF*IC6cYPjr0n@gIHl zR~^zL&6fYOVV-_CV2c>*x9q~^i_acBuz$F1l}h{6r)$}S|L7Hr+q}5xK{MAY zip>zv^1HQj{^TvkN?2Z%d1RQ^<7Dy% zXXVZ9Zu22=Fj>pX781ePUZxPn5-%$V=+LVXXIq>iP&{82g(;r1Jcs zQXBlC9{A{Q$qULW@IaVs9sqTBeSj3xWar5b14Jip2oRJ9sp8wY{cRmH1H&(F1_lWR QWZ*veL4YdjF>8~Iq7SU`-;;!N_45SA75K`84#OC6Ne$Yup) z?PnK;uqN+kSDpNu<2_i@WCa2F&AeRvY!D+91Vq>&EDLchsB#N&h0SdeTu`2ZfYj!7 zlF_U%-BO#?39))+S%Rn6h$BWGJ0D*H*dTzJH$kWVe$k%;&fl7f*R8qPk$_Lnm>|UlIGh%nNwL ze07f3Vnz4p4;e=L_Dk<=UZN=XY3T;LPR(1-SMVIP6MhuQ*;SRJ^Ww#t-e8j>YqdL; z)~{K_I-zbBU#InXhsR-QM$avaitQXu7G*!QznQjIjAvQfRj!g91*|tUEK9#=+vzO} z3U@dmc&z39)?)j^g}o`_`RylURUJ0h1~8Z93`r^W%i#sc%q?ej@7@JYAQY1nHOeEZ&_ zTVAg?On8?3_?P|p><16iY$gle^^d;qKJt9~u{rYTe*)ApYoC4O5AbH^2-@7BXT`|C zAj-nPfSRy2FLZ`R(d32B%9|_P=0l=nvb2{iB+g^JOd*V^URDssWiM+8L&)0&!ie(r zY99^ z_8su`g{TqqbAm9!{ahi81%6P)k6;WHe{+abygyWGp+D3okNhopL9T>{#$=5Es0RuH xq?i^tPhRgVr5)hS$Rxsm$a;J`x4*4pW?=Zm&A=eRfDFPWUkuP@lkx(I0RU#zb=m*` diff --git a/xmlApp.go b/xmlApp.go index fdb60084..32264066 100644 --- a/xmlApp.go +++ b/xmlApp.go @@ -13,38 +13,50 @@ package excelize import "encoding/xml" +// AppProperties directly maps the document application properties. +type AppProperties struct { + Application string + ScaleCrop bool + DocSecurity int + Company string + LinksUpToDate bool + HyperlinksChanged bool + AppVersion string +} + // xlsxProperties specifies to an OOXML document properties such as the // template used, the number of pages and words, and the application name and // version. type xlsxProperties struct { XMLName xml.Name `xml:"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties Properties"` - Template string - Manager string - Company string - Pages int - Words int - Characters int - PresentationFormat string - Lines int - Paragraphs int - Slides int - Notes int - TotalTime int - HiddenSlides int - MMClips int - ScaleCrop bool + Vt string `xml:"xmlns:vt,attr"` + Template string `xml:",omitempty"` + Manager string `xml:",omitempty"` + Company string `xml:",omitempty"` + Pages int `xml:",omitempty"` + Words int `xml:",omitempty"` + Characters int `xml:",omitempty"` + PresentationFormat string `xml:",omitempty"` + Lines int `xml:",omitempty"` + Paragraphs int `xml:",omitempty"` + Slides int `xml:",omitempty"` + Notes int `xml:",omitempty"` + TotalTime int `xml:",omitempty"` + HiddenSlides int `xml:",omitempty"` + MMClips int `xml:",omitempty"` + ScaleCrop bool `xml:",omitempty"` HeadingPairs *xlsxVectorVariant TitlesOfParts *xlsxVectorLpstr - LinksUpToDate bool - CharactersWithSpaces int - SharedDoc bool - HyperlinkBase string + LinksUpToDate bool `xml:",omitempty"` + CharactersWithSpaces int `xml:",omitempty"` + SharedDoc bool `xml:",omitempty"` + HyperlinkBase string `xml:",omitempty"` HLinks *xlsxVectorVariant - HyperlinksChanged bool + HyperlinksChanged bool `xml:",omitempty"` DigSig *xlsxDigSig - Application string - AppVersion string - DocSecurity int + Application string `xml:",omitempty"` + AppVersion string `xml:",omitempty"` + DocSecurity int `xml:",omitempty"` } // xlsxVectorVariant specifies the set of hyperlinks that were in this diff --git a/xmlDrawing.go b/xmlDrawing.go index dabb34a7..16905547 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -19,37 +19,39 @@ import ( // Source relationship and namespace list, associated prefixes and schema in which it was // introduced. var ( - SourceRelationship = xml.Attr{Name: xml.Name{Local: "r", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"} - SourceRelationshipCompatibility = xml.Attr{Name: xml.Name{Local: "mc", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/markup-compatibility/2006"} - SourceRelationshipChart20070802 = xml.Attr{Name: xml.Name{Local: "c14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"} - SourceRelationshipChart2014 = xml.Attr{Name: xml.Name{Local: "c16", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2014/chart"} - SourceRelationshipChart201506 = xml.Attr{Name: xml.Name{Local: "c16r2", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2015/06/chart"} - NameSpaceSpreadSheet = xml.Attr{Name: xml.Name{Local: "xmlns"}, Value: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"} - NameSpaceSpreadSheetX14 = xml.Attr{Name: xml.Name{Local: "x14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"} - NameSpaceDrawingML = xml.Attr{Name: xml.Name{Local: "a", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/main"} - NameSpaceDrawingMLChart = xml.Attr{Name: xml.Name{Local: "c", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/chart"} - NameSpaceDrawingMLSpreadSheet = xml.Attr{Name: xml.Name{Local: "xdr", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"} - NameSpaceSpreadSheetX15 = xml.Attr{Name: xml.Name{Local: "x15", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"} - NameSpaceSpreadSheetExcel2006Main = xml.Attr{Name: xml.Name{Local: "xne", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/excel/2006/main"} - NameSpaceMacExcel2008Main = xml.Attr{Name: xml.Name{Local: "mx", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/mac/excel/2008/main"} + SourceRelationship = xml.Attr{Name: xml.Name{Local: "r", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"} + SourceRelationshipCompatibility = xml.Attr{Name: xml.Name{Local: "mc", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/markup-compatibility/2006"} + SourceRelationshipChart20070802 = xml.Attr{Name: xml.Name{Local: "c14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"} + SourceRelationshipChart2014 = xml.Attr{Name: xml.Name{Local: "c16", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2014/chart"} + SourceRelationshipChart201506 = xml.Attr{Name: xml.Name{Local: "c16r2", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/drawing/2015/06/chart"} + NameSpaceSpreadSheet = xml.Attr{Name: xml.Name{Local: "xmlns"}, Value: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"} + NameSpaceSpreadSheetX14 = xml.Attr{Name: xml.Name{Local: "x14", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"} + NameSpaceDrawingML = xml.Attr{Name: xml.Name{Local: "a", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/main"} + NameSpaceDrawingMLChart = xml.Attr{Name: xml.Name{Local: "c", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/chart"} + NameSpaceDrawingMLSpreadSheet = xml.Attr{Name: xml.Name{Local: "xdr", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"} + NameSpaceSpreadSheetX15 = xml.Attr{Name: xml.Name{Local: "x15", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"} + NameSpaceSpreadSheetExcel2006Main = xml.Attr{Name: xml.Name{Local: "xne", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/excel/2006/main"} + NameSpaceMacExcel2008Main = xml.Attr{Name: xml.Name{Local: "mx", Space: "xmlns"}, Value: "http://schemas.microsoft.com/office/mac/excel/2008/main"} + NameSpaceDocumentPropertiesVariantTypes = xml.Attr{Name: xml.Name{Local: "vt", Space: "xmlns"}, Value: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"} ) // Source relationship and namespace. const ( - SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" - SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" - SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" - SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" - SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" - SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" - SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" - SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" - SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" - SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet" - SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet" - SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" - SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" - SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" + SourceRelationshipOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" + SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" + SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" + SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" + SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" + SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" + SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" + SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" + SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" + SourceRelationshipChartsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet" + SourceRelationshipDialogsheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet" + SourceRelationshipPivotTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" + SourceRelationshipPivotCache = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" + SourceRelationshipSharedStrings = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" + SourceRelationshipVBAProject = "http://schemas.microsoft.com/office/2006/relationships/vbaProject" NameSpaceXML = "http://www.w3.org/XML/1998/namespace" NameSpaceXMLSchemaInstance = "http://www.w3.org/2001/XMLSchema-instance"