Compare commits

...

23 Commits

Author SHA1 Message Date
hly-717 b19e5940b8
Fix GetStyle function can not get VertAlign format (#2079)
- Fix redundant cols element generated by stream writer
- Update dependencies module
- Update docs for the GetCellRichText function
- Move TestSetCellIntFunc function to cell_test.go
2025-01-25 10:01:27 +08:00
MengZhongYuan 0e0e2dadcf
This close #2075, add new function SetColStyle for streaming writer to support set columns style (#2076)
- Add new exported error variable ErrStreamSetColStyle
- Fix cell default style doesn't override by none-zero row style when set row by stream writer
- Update unit tests

Signed-off-by: mengzhongyuan <mengzhongyuan@bytedance.com>
2025-01-24 17:45:25 +08:00
gypsy1234 7e614c5104
This closes #2072, support adjust data validations cross multiple worksheets (#2073) 2025-01-22 18:54:04 +08:00
xxf0512 a45d47d57c Fix: int overflow of function 2025-01-22 09:52:58 +08:00
xuri b936343814
This closes #2068, breaking changes: SetCellInt function required int64 data type parameter
- Update unit tests
2025-01-21 09:13:23 +08:00
ZhuHaiCheng e9efc47316
Correct comments of the internal function setSheetOutlineProps (#2066) 2025-01-14 19:19:16 +08:00
xuri af422e1700
This closes #1954 and closes #2051, fix get pivot tables panic in some case 2025-01-05 09:37:31 +08:00
Arpelicy 4b4d4df76b
This closes #2061, fix border styles missing after saved workbook (#2064)
- Using form template for GitHub issues
- Typo fix for comments of the getSupportedLanguageInfo function
2025-01-04 11:17:56 +08:00
xuri caf22e4974
This closes #2059, support delete one cell anchor image
- Fix delete wrong images in some case which caused by image reference detection issue
- Update unit tests
2025-01-02 07:32:49 +08:00
xuri 3f6ecffcca
This closes #2052, support to sets the format of the chart series data label
- Add new field DataLabel in the ChartSeries data type
2024-12-29 12:30:28 +08:00
xuri 9934bf5c86
This closes #2046, add new function AddIgnoredErrors support to ignored error for a range of cells
- Add new exported IgnoredErrorsType enumeration
- Change the type of DataValidationType, DataValidationErrorStyle, DataValidationOperator, PictureInsertType from int to byte
2024-12-21 15:11:17 +08:00
xuri 5ef4a360c1
This closes #2048, fix missing vertical and horizontal border styles
- Update dependencies module
2024-12-20 09:43:44 +08:00
xuri 8e0490927e
Update unit test for internal function unzipToTemp
- Move security markdown into .github directory
- Fix incorrect docs of the AddChart function
- Update the CodeQL config
2024-12-14 09:45:36 +08:00
xuri b53bad3541
Breaking changes: Go 1.20 and later required
- Update dependencies module
2024-12-12 16:43:48 +08:00
xuri 3ca60f8d23
This closes #2033, support set gap width and overlap for column and bar chart
- Add new fields GapWidth and Overlap in the Chart data type
- Update unit tests
- Update dependencies modules
2024-12-08 11:39:54 +08:00
Eng Zer Jun c93618856a
This closes #2029, use a faster deepcopy library (#2030)
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2024-11-22 21:56:38 +08:00
xuri 5f446f25f0
This closes #2025, support set chart axis text direction and rotation
- Add new field Alignment in the ChartAxis data type
- Update unit tests
- Update doc of the AddHeaderFooterImage function
2024-11-15 22:03:10 +08:00
Ilia Mirkin 30d3561d0e
Rename SetLegacyDrawingHF to AddHeaderFooterImage (#2023)
- Add new exported HeaderFooterImagePositionType enumeration
- An error will be return if the image format is unsupported
- Rename exported data type HeaderFooterGraphics to HeaderFooterImageOptions
- Support add and update exist header and footer images
- Changes the VML data ID to sheet ID
- Update unit tests
- Update dependencies modules
2024-11-09 18:36:42 +08:00
Ilia Mirkin d2be5cdf8e
The SetPageLayout function support set page order of page layout (#2022)
- Add new fields PageOrder for PageLayoutOptions
- Add a new exported error variable ErrPageSetupAdjustTo
- An error will be return if the option value of the SetPageLayout function is invalid
- Updated unit tests
2024-11-08 16:59:07 +08:00
Ilia Mirkin b7375bc6d4
This closes #1395, add new function SetLegacyDrawingHF support to set graphics in a header/footer (#2018) 2024-11-04 10:39:55 +08:00
xuri 0d5d1c53b2
This closes #2015, fix a v2.9.0 regression bug introduced by commit 7715c1462a
- Fix corrupted workbook generated by open the workbook generated by stream writer
- Update unit tests
2024-10-25 08:52:59 +08:00
xuri af190c7fdc
This closes #2014, fix redundant none type pattern fill generated
- Simplify unit tests
2024-10-23 22:07:25 +08:00
wushiling50 d1937a0cde
This closes #1885, add new CultureNameJaJP, CultureNameKoKR and CultureNameZhTW enumeration values (#1895)
- Support apply number format for the Japanese calendar years, the Korean Danki calendar and the Republic of China year
- Update unit tests

Signed-off-by: wushiling50 <2531010934@qq.com>
2024-10-21 09:36:04 +08:00
83 changed files with 1410 additions and 496 deletions

View File

@ -1,44 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
<!--
If you are reporting a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->
**Description**
<!--
Briefly describe the problem you are having in a few paragraphs.
-->
**Steps to reproduce the issue:**
1.
2.
3.
**Describe the results you received:**
**Describe the results you expected:**
**Output of `go version`:**
```text
(paste your output here)
```
**Excelize version or commit ID:**
```text
(paste here)
```
**Environment details (OS, Microsoft Excel™ version, physical, etc.):**

81
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,81 @@
name: Bug report
description: Create a report to help us improve
body:
- type: markdown
attributes:
value: |
If you are reporting a new issue, make sure that we do not have any duplicates already open. You can ensure this by searching the issue list for this repository. If there is a duplicate, please close your issue and add a comment to the existing issue instead.
- type: textarea
id: description
attributes:
label: Description
description: Briefly describe the problem you are having in a few paragraphs.
validations:
required: true
- type: textarea
id: reproduction-steps
attributes:
label: Steps to reproduce the issue
description: Explain how to cause the issue in the provided reproduction.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: received
attributes:
label: Describe the results you received
validations:
required: true
- type: textarea
id: expected
attributes:
label: Describe the results you expected
validations:
required: true
- type: input
id: go-version
attributes:
label: Go version
description: |
Output of `go version`:
placeholder: e.g. 1.23.4
validations:
required: true
- type: input
id: excelize-version
attributes:
label: Excelize version or commit ID
description: |
Which version of Excelize are you using?
placeholder: e.g. 2.9.0
validations:
required: true
- type: textarea
id: env
attributes:
label: Environment
description: Environment details (OS, Microsoft Excel&trade; version, physical, etc.)
render: shell
validations:
required: true
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
required: true
- label: The provided reproduction is a minimal reproducible example of the bug.
required: true

View File

@ -1,44 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
<!--
If you are reporting a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->
**Description**
<!--
Briefly describe the problem you are having in a few paragraphs.
-->
**Steps to reproduce the issue:**
1.
2.
3.
**Describe the results you received:**
**Describe the results you expected:**
**Output of `go version`:**
```text
(paste your output here)
```
**Excelize version or commit ID:**
```text
(paste here)
```
**Environment details (OS, Microsoft Excel™ version, physical, etc.):**

View File

@ -0,0 +1,30 @@
name: Feature request
description: Suggest an idea for this project
body:
- type: markdown
attributes:
value: |
If you are reporting a new issue, make sure that we do not have any duplicates already open. You can ensure this by searching the issue list for this repository. If there is a duplicate, please close your issue and add a comment to the existing issue instead.
- type: textarea
id: description
attributes:
label: Description
description: Describe the feature that you would like added
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Any other context or screenshots about the feature request here?
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Check that there isn't already an issue that requests the same feature to avoid creating a duplicate.
required: true

View File

View File

@ -11,7 +11,7 @@ on:
jobs: jobs:
analyze: analyze:
name: Analyze name: Analyze
runs-on: ubuntu-latest runs-on: ubuntu-24.04
strategy: strategy:
fail-fast: false fail-fast: false

View File

@ -5,8 +5,8 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.18.x, 1.19.x, 1.20.x, '>=1.21.1', 1.22.x, 1.23.x] go-version: [1.20.x, '>=1.21.1', 1.22.x, 1.23.x]
os: [ubuntu-latest, macos-13, windows-latest] os: [ubuntu-24.04, macos-13, windows-latest]
targetplatform: [x86, x64] targetplatform: [x86, x64]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -32,10 +32,10 @@ jobs:
run: env GO111MODULE=on go test -v -timeout 30m -race ./... -coverprofile='coverage.txt' -covermode=atomic run: env GO111MODULE=on go test -v -timeout 30m -race ./... -coverprofile='coverage.txt' -covermode=atomic
- name: Codecov - name: Codecov
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
env: env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with: with:
file: coverage.txt files: coverage.txt
flags: unittests flags: unittests
name: codecov-umbrella name: codecov-umbrella

View File

@ -1,6 +1,6 @@
BSD 3-Clause License BSD 3-Clause License
Copyright (c) 2016-2024 The excelize Authors. Copyright (c) 2016-2025 The excelize Authors.
Copyright (c) 2011-2017 Geoffrey J. Teale Copyright (c) 2011-2017 Geoffrey J. Teale
All rights reserved. All rights reserved.

View File

@ -13,7 +13,7 @@
## Introduction ## Introduction
Excelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and writing spreadsheet documents generated by Microsoft Excel&trade; 2007 and later. Supports complex components by high compatibility, and provided streaming API for generating or reading data from a worksheet with huge amounts of data. This library needs Go version 1.18 or later. There are some [incompatible changes](https://github.com/golang/go/issues/61881) in the Go 1.21.0, the Excelize library can not working with that version normally, if you are using the Go 1.21.x, please upgrade to the Go 1.21.1 and later version. The full docs can be seen using go's built-in documentation tool, or online at [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) and [docs reference](https://xuri.me/excelize/). Excelize is a library written in pure Go providing a set of functions that allow you to write to and read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and writing spreadsheet documents generated by Microsoft Excel&trade; 2007 and later. Supports complex components by high compatibility, and provided streaming API for generating or reading data from a worksheet with huge amounts of data. This library needs Go version 1.20 or later. There are some [incompatible changes](https://github.com/golang/go/issues/61881) in the Go 1.21.0, the Excelize library can not working with that version normally, if you are using the Go 1.21.x, please upgrade to the Go 1.21.1 and later version. The full docs can be seen using go's built-in documentation tool, or online at [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) and [docs reference](https://xuri.me/excelize/).
## Basic Usage ## Basic Usage

View File

@ -13,7 +13,7 @@
## 简介 ## 简介
Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel&trade; 2007 及以上版本创建的电子表格文档。支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写函数,用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.18 或更高版本请注意Go 1.21.0 中存在[不兼容的更改](https://github.com/golang/go/issues/61881),导致 Excelize 基础库无法在该版本上正常工作,如果您使用的是 Go 1.21.x请升级到 Go 1.21.1 及更高版本。完整的使用文档请访问 [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) 或查看 [参考文档](https://xuri.me/excelize/)。 Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel&trade; 2007 及以上版本创建的电子表格文档。支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写函数,用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.20 或更高版本请注意Go 1.21.0 中存在[不兼容的更改](https://github.com/golang/go/issues/61881),导致 Excelize 基础库无法在该版本上正常工作,如果您使用的是 Go 1.21.x请升级到 Go 1.21.1 及更高版本。完整的使用文档请访问 [go.dev](https://pkg.go.dev/github.com/xuri/excelize/v2) 或查看 [参考文档](https://xuri.me/excelize/)。
## 快速上手 ## 快速上手

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -969,7 +969,7 @@ func (f *File) adjustDataValidations(ws *xlsxWorksheet, sheet string, dir adjust
return err return err
} }
if worksheet.DataValidations == nil { if worksheet.DataValidations == nil {
return nil continue
} }
for i := 0; i < len(worksheet.DataValidations.DataValidation); i++ { for i := 0; i < len(worksheet.DataValidations.DataValidation); i++ {
dv := worksheet.DataValidations.DataValidation[i] dv := worksheet.DataValidations.DataValidation[i]

View File

@ -1170,6 +1170,25 @@ func TestAdjustDataValidations(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, formula, dvs[0].Formula1) assert.Equal(t, formula, dvs[0].Formula1)
}) })
t.Run("no_data_validations_on_first_sheet", func(t *testing.T) {
f := NewFile()
// Add Sheet2 and set a data validation
_, err = f.NewSheet("Sheet2")
assert.NoError(t, err)
dv := NewDataValidation(true)
dv.Sqref = "C5:D6"
assert.NoError(t, f.AddDataValidation("Sheet2", dv))
// Adjust Sheet2 by removing a column
assert.NoError(t, f.RemoveCol("Sheet2", "A"))
// Verify that data validations on Sheet2 are adjusted correctly
dvs, err = f.GetDataValidations("Sheet2")
assert.NoError(t, err)
assert.Equal(t, "B5:C6", dvs[0].Sqref) // Adjusted range
})
} }
func TestAdjustDrawings(t *testing.T) { func TestAdjustDrawings(t *testing.T) {

11
calc.go
View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -13629,7 +13629,9 @@ func (fn *formulaFuncs) DBCS(argsList *list.List) formulaArg {
if arg.Type == ArgError { if arg.Type == ArgError {
return arg return arg
} }
if fn.f.options.CultureInfo == CultureNameZhCN { if fn.f.options.CultureInfo == CultureNameJaJP ||
fn.f.options.CultureInfo == CultureNameZhCN ||
fn.f.options.CultureInfo == CultureNameZhTW {
var chars []string var chars []string
for _, r := range arg.Value() { for _, r := range arg.Value() {
code := r code := r
@ -16378,7 +16380,10 @@ func (fn *formulaFuncs) DOLLAR(argsList *list.List) formulaArg {
symbol := map[CultureName]string{ symbol := map[CultureName]string{
CultureNameUnknown: "$", CultureNameUnknown: "$",
CultureNameEnUS: "$", CultureNameEnUS: "$",
CultureNameJaJP: "¥",
CultureNameKoKR: "\u20a9",
CultureNameZhCN: "¥", CultureNameZhCN: "¥",
CultureNameZhTW: "NT$",
}[fn.f.options.CultureInfo] }[fn.f.options.CultureInfo]
numFmtCode := fmt.Sprintf("%s#,##0%s%s;(%s#,##0%s%s)", numFmtCode := fmt.Sprintf("%s#,##0%s%s;(%s#,##0%s%s)",
symbol, dot, strings.Repeat("0", decimals), symbol, dot, strings.Repeat("0", decimals)) symbol, dot, strings.Repeat("0", decimals), symbol, dot, strings.Repeat("0", decimals))

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

47
cell.go
View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -207,15 +207,15 @@ func (f *File) setCellIntFunc(sheet, cell string, value interface{}) error {
var err error var err error
switch v := value.(type) { switch v := value.(type) {
case int: case int:
err = f.SetCellInt(sheet, cell, v) err = f.SetCellInt(sheet, cell, int64(v))
case int8: case int8:
err = f.SetCellInt(sheet, cell, int(v)) err = f.SetCellInt(sheet, cell, int64(v))
case int16: case int16:
err = f.SetCellInt(sheet, cell, int(v)) err = f.SetCellInt(sheet, cell, int64(v))
case int32: case int32:
err = f.SetCellInt(sheet, cell, int(v)) err = f.SetCellInt(sheet, cell, int64(v))
case int64: case int64:
err = f.SetCellInt(sheet, cell, int(v)) err = f.SetCellInt(sheet, cell, v)
case uint: case uint:
err = f.SetCellUint(sheet, cell, uint64(v)) err = f.SetCellUint(sheet, cell, uint64(v))
case uint8: case uint8:
@ -288,7 +288,7 @@ func setCellDuration(value time.Duration) (t string, v string) {
// SetCellInt provides a function to set int type value of a cell by given // SetCellInt provides a function to set int type value of a cell by given
// worksheet name, cell reference and cell value. // worksheet name, cell reference and cell value.
func (f *File) SetCellInt(sheet, cell string, value int) error { func (f *File) SetCellInt(sheet, cell string, value int64) error {
f.mu.Lock() f.mu.Lock()
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
@ -309,8 +309,8 @@ func (f *File) SetCellInt(sheet, cell string, value int) error {
} }
// setCellInt prepares cell type and string type cell value by a given integer. // setCellInt prepares cell type and string type cell value by a given integer.
func setCellInt(value int) (t string, v string) { func setCellInt(value int64) (t string, v string) {
v = strconv.Itoa(value) v = strconv.FormatInt(value, 10)
return return
} }
@ -1101,7 +1101,7 @@ func getCellRichText(si *xlsxSI) (runs []RichTextRun) {
} }
// GetCellRichText provides a function to get rich text of cell by given // GetCellRichText provides a function to get rich text of cell by given
// worksheet. // worksheet and cell reference.
func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err error) { func (f *File) GetCellRichText(sheet, cell string) (runs []RichTextRun, err error) {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
@ -1164,7 +1164,7 @@ func newRpr(fnt *Font) *xlsxRPr {
// newFont create font format by given run properties for the rich text. // newFont create font format by given run properties for the rich text.
func newFont(rPr *xlsxRPr) *Font { func newFont(rPr *xlsxRPr) *Font {
font := Font{Underline: "none"} var font Font
font.Bold = rPr.B != nil font.Bold = rPr.B != nil
font.Italic = rPr.I != nil font.Italic = rPr.I != nil
if rPr.U != nil { if rPr.U != nil {
@ -1179,6 +1179,9 @@ func newFont(rPr *xlsxRPr) *Font {
if rPr.Sz != nil && rPr.Sz.Val != nil { if rPr.Sz != nil && rPr.Sz.Val != nil {
font.Size = *rPr.Sz.Val font.Size = *rPr.Sz.Val
} }
if rPr.VertAlign != nil && rPr.VertAlign.Val != nil {
font.VertAlign = *rPr.VertAlign.Val
}
font.Strike = rPr.Strike != nil font.Strike = rPr.Strike != nil
if rPr.Color != nil { if rPr.Color != nil {
font.Color = strings.TrimPrefix(rPr.Color.RGB, "FF") font.Color = strings.TrimPrefix(rPr.Color.RGB, "FF")
@ -1214,8 +1217,8 @@ func setRichText(runs []RichTextRun) ([]xlsxR, error) {
} }
// SetCellRichText provides a function to set cell with rich text by given // SetCellRichText provides a function to set cell with rich text by given
// worksheet. For example, set rich text on the A1 cell of the worksheet named // worksheet name, cell reference and rich text runs. For example, set rich text
// Sheet1: // on the A1 cell of the worksheet named Sheet1:
// //
// package main // package main
// //
@ -1245,7 +1248,7 @@ func setRichText(runs []RichTextRun) ([]xlsxR, error) {
// Text: "bold", // Text: "bold",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Bold: true, // Bold: true,
// Color: "2354e8", // Color: "2354E8",
// Family: "Times New Roman", // Family: "Times New Roman",
// }, // },
// }, // },
@ -1259,7 +1262,7 @@ func setRichText(runs []RichTextRun) ([]xlsxR, error) {
// Text: "italic ", // Text: "italic ",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Bold: true, // Bold: true,
// Color: "e83723", // Color: "E83723",
// Italic: true, // Italic: true,
// Family: "Times New Roman", // Family: "Times New Roman",
// }, // },
@ -1268,7 +1271,7 @@ func setRichText(runs []RichTextRun) ([]xlsxR, error) {
// Text: "text with color and font-family,", // Text: "text with color and font-family,",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Bold: true, // Bold: true,
// Color: "2354e8", // Color: "2354E8",
// Family: "Times New Roman", // Family: "Times New Roman",
// }, // },
// }, // },
@ -1276,20 +1279,20 @@ func setRichText(runs []RichTextRun) ([]xlsxR, error) {
// Text: "\r\nlarge text with ", // Text: "\r\nlarge text with ",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Size: 14, // Size: 14,
// Color: "ad23e8", // Color: "AD23E8",
// }, // },
// }, // },
// { // {
// Text: "strike", // Text: "strike",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Color: "e89923", // Color: "E89923",
// Strike: true, // Strike: true,
// }, // },
// }, // },
// { // {
// Text: " superscript", // Text: " superscript",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Color: "dbc21f", // Color: "DBC21F",
// VertAlign: "superscript", // VertAlign: "superscript",
// }, // },
// }, // },
@ -1297,14 +1300,14 @@ func setRichText(runs []RichTextRun) ([]xlsxR, error) {
// Text: " and ", // Text: " and ",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Size: 14, // Size: 14,
// Color: "ad23e8", // Color: "AD23E8",
// VertAlign: "baseline", // VertAlign: "baseline",
// }, // },
// }, // },
// { // {
// Text: "underline", // Text: "underline",
// Font: &excelize.Font{ // Font: &excelize.Font{
// Color: "23e833", // Color: "23E833",
// Underline: "single", // Underline: "single",
// }, // },
// }, // },

View File

@ -856,7 +856,7 @@ func TestSetCellRichText(t *testing.T) {
Text: "bold", Text: "bold",
Font: &Font{ Font: &Font{
Bold: true, Bold: true,
Color: "2354e8", Color: "2354E8",
ColorIndexed: 0, ColorIndexed: 0,
Family: "Times New Roman", Family: "Times New Roman",
}, },
@ -871,7 +871,7 @@ func TestSetCellRichText(t *testing.T) {
Text: "italic ", Text: "italic ",
Font: &Font{ Font: &Font{
Bold: true, Bold: true,
Color: "e83723", Color: "E83723",
Italic: true, Italic: true,
Family: "Times New Roman", Family: "Times New Roman",
}, },
@ -880,7 +880,7 @@ func TestSetCellRichText(t *testing.T) {
Text: "text with color and font-family, ", Text: "text with color and font-family, ",
Font: &Font{ Font: &Font{
Bold: true, Bold: true,
Color: "2354e8", Color: "2354E8",
Family: "Times New Roman", Family: "Times New Roman",
}, },
}, },
@ -888,20 +888,20 @@ func TestSetCellRichText(t *testing.T) {
Text: "\r\nlarge text with ", Text: "\r\nlarge text with ",
Font: &Font{ Font: &Font{
Size: 14, Size: 14,
Color: "ad23e8", Color: "AD23E8",
}, },
}, },
{ {
Text: "strike", Text: "strike",
Font: &Font{ Font: &Font{
Color: "e89923", Color: "E89923",
Strike: true, Strike: true,
}, },
}, },
{ {
Text: " superscript", Text: " superscript",
Font: &Font{ Font: &Font{
Color: "dbc21f", Color: "DBC21F",
VertAlign: "superscript", VertAlign: "superscript",
}, },
}, },
@ -909,14 +909,14 @@ func TestSetCellRichText(t *testing.T) {
Text: " and ", Text: " and ",
Font: &Font{ Font: &Font{
Size: 14, Size: 14,
Color: "ad23e8", Color: "AD23E8",
VertAlign: "BASELINE", VertAlign: "baseline",
}, },
}, },
{ {
Text: "underline", Text: "underline",
Font: &Font{ Font: &Font{
Color: "23e833", Color: "23E833",
Underline: "single", Underline: "single",
}, },
}, },
@ -937,6 +937,11 @@ func TestSetCellRichText(t *testing.T) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style)) assert.NoError(t, f.SetCellStyle("Sheet1", "A1", "A1", style))
runs, err := f.GetCellRichText("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, richTextRun, runs)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellRichText.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellRichText.xlsx")))
// Test set cell rich text on not exists worksheet // Test set cell rich text on not exists worksheet
assert.EqualError(t, f.SetCellRichText("SheetN", "A1", richTextRun), "sheet SheetN does not exist") assert.EqualError(t, f.SetCellRichText("SheetN", "A1", richTextRun), "sheet SheetN does not exist")
@ -1153,6 +1158,29 @@ func TestSharedStringsError(t *testing.T) {
}) })
} }
func TestSetCellIntFunc(t *testing.T) {
cases := []struct {
val interface{}
target string
}{
{val: 128, target: "128"},
{val: int8(-128), target: "-128"},
{val: int16(-32768), target: "-32768"},
{val: int32(-2147483648), target: "-2147483648"},
{val: int64(-9223372036854775808), target: "-9223372036854775808"},
{val: uint(128), target: "128"},
{val: uint8(255), target: "255"},
{val: uint16(65535), target: "65535"},
{val: uint32(4294967295), target: "4294967295"},
{val: uint64(18446744073709551615), target: "18446744073709551615"},
}
for _, c := range cases {
cell := &xlsxC{}
setCellIntFunc(cell, c.val)
assert.Equal(t, c.target, cell.V)
}
}
func TestSIString(t *testing.T) { func TestSIString(t *testing.T) {
assert.Empty(t, xlsxSI{}.String()) assert.Empty(t, xlsxSI{}.String())
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -486,6 +486,42 @@ var (
Line: "standard", Line: "standard",
Line3D: "standard", Line3D: "standard",
} }
barColChartTypes = []ChartType{
Bar,
BarStacked,
BarPercentStacked,
Bar3DClustered,
Bar3DStacked,
Bar3DPercentStacked,
Bar3DConeClustered,
Bar3DConeStacked,
Bar3DConePercentStacked,
Bar3DPyramidClustered,
Bar3DPyramidStacked,
Bar3DPyramidPercentStacked,
Bar3DCylinderClustered,
Bar3DCylinderStacked,
Bar3DCylinderPercentStacked,
Col,
ColStacked,
ColPercentStacked,
Col3D,
Col3DClustered,
Col3DStacked,
Col3DPercentStacked,
Col3DCone,
Col3DConeStacked,
Col3DConeClustered,
Col3DConePercentStacked,
Col3DPyramid,
Col3DPyramidClustered,
Col3DPyramidStacked,
Col3DPyramidPercentStacked,
Col3DCylinder,
Col3DCylinderClustered,
Col3DCylinderStacked,
Col3DCylinderPercentStacked,
}
orientation = map[bool]string{ orientation = map[bool]string{
true: "maxMin", true: "maxMin",
false: "minMax", false: "minMax",
@ -712,6 +748,7 @@ func (opts *Chart) parseTitle() {
// Fill // Fill
// Line // Line
// Marker // Marker
// DataLabel
// DataLabelPosition // DataLabelPosition
// //
// Name: Set the name for the series. The name is displayed in the chart legend // Name: Set the name for the series. The name is displayed in the chart legend
@ -755,6 +792,8 @@ func (opts *Chart) parseTitle() {
// x // x
// auto // auto
// //
// DataLabel: This sets the format of the chart series data label.
//
// DataLabelPosition: This sets the position of the chart series data label. // DataLabelPosition: This sets the position of the chart series data label.
// //
// Set properties of the chart legend. The options that can be set are: // Set properties of the chart legend. The options that can be set are:
@ -850,6 +889,7 @@ func (opts *Chart) parseTitle() {
// ReverseOrder // ReverseOrder
// Maximum // Maximum
// Minimum // Minimum
// Alignment
// Font // Font
// NumFmt // NumFmt
// Title // Title
@ -864,6 +904,7 @@ func (opts *Chart) parseTitle() {
// ReverseOrder // ReverseOrder
// Maximum // Maximum
// Minimum // Minimum
// Alignment
// Font // Font
// LogBase // LogBase
// NumFmt // NumFmt
@ -896,6 +937,24 @@ func (opts *Chart) parseTitle() {
// Minimum: Specifies that the fixed minimum, 0 is auto. The 'Minimum' property // Minimum: Specifies that the fixed minimum, 0 is auto. The 'Minimum' property
// is optional. The default value is auto. // is optional. The default value is auto.
// //
// Alignment: Specifies that the alignment of the horizontal and vertical axis.
// The properties of alignment that can be set are:
//
// TextRotation
// Vertical
//
// The value of 'TextRotation' that can be set from -90 to 90.
//
// The value of 'Vertical' that can be set are:
//
// horz
// vert
// vert270
// wordArtVert
// eaVert
// mongolianVert
// wordArtVertRtl
//
// Font: Specifies that the font of the horizontal and vertical axis. The // Font: Specifies that the font of the horizontal and vertical axis. The
// properties of font that can be set are: // properties of font that can be set are:
// //
@ -929,6 +988,14 @@ func (opts *Chart) parseTitle() {
// 'HoleSize' property. The 'HoleSize' property is optional. The default width // 'HoleSize' property. The 'HoleSize' property is optional. The default width
// is 75, and the value should be great than 0 and less or equal than 90. // is 75, and the value should be great than 0 and less or equal than 90.
// //
// Set the gap with of the column and bar series chart by 'GapWidth' property.
// The 'GapWidth' property is optional. The default width is 150, and the value
// should be great or equal than 0 and less or equal than 500.
//
// Set series overlap of the column and bar series chart by 'Overlap' property.
// The 'Overlap' property is optional. The default width is 0, and the value
// should be great or equal than -100 and less or equal than 100.
//
// combo: Specifies the create a chart that combines two or more chart types in // combo: Specifies the create a chart that combines two or more chart types in
// a single chart. For example, create a clustered column - line chart with // a single chart. For example, create a clustered column - line chart with
// data Sheet1!$E$1:$L$15: // data Sheet1!$E$1:$L$15:

View File

@ -124,6 +124,11 @@ func TestDeleteDrawing(t *testing.T) {
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
f, err = OpenFile(filepath.Join("test", "Book1.xlsx")) f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
assert.NoError(t, err) assert.NoError(t, err)
f.Drawings.Store(path, &xlsxWsDr{OneCellAnchor: []*xdrCellAnchor{{
GraphicFrame: string(MacintoshCyrillicCharset),
}}})
_, err = f.deleteDrawing(0, 0, path, "Chart")
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
f.Drawings.Store(path, &xlsxWsDr{TwoCellAnchor: []*xdrCellAnchor{{ f.Drawings.Store(path, &xlsxWsDr{TwoCellAnchor: []*xdrCellAnchor{{
GraphicFrame: string(MacintoshCyrillicCharset), GraphicFrame: string(MacintoshCyrillicCharset),
}}}) }}})
@ -215,10 +220,10 @@ func TestAddChart(t *testing.T) {
opts *Chart opts *Chart
}{ }{
{sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: []RichTextRun{{Text: "2D Column Chart"}}, PlotArea: plotArea, Border: ChartLine{Type: ChartLineNone}, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Family: "Times New Roman", Size: 15, Strike: true, Color: "000000"}, Title: []RichTextRun{{Text: "Primary Horizontal Axis Title"}}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}, Title: []RichTextRun{{Text: "Primary Vertical Axis Title", Font: &Font{Color: "777777", Bold: true, Italic: true, Size: 12}}}}}}, {sheetName: "Sheet1", cell: "P1", opts: &Chart{Type: Col, Series: series, Format: format, Legend: ChartLegend{Position: "none", ShowLegendKey: true}, Title: []RichTextRun{{Text: "2D Column Chart"}}, PlotArea: plotArea, Border: ChartLine{Type: ChartLineNone}, ShowBlanksAs: "zero", XAxis: ChartAxis{Font: Font{Bold: true, Italic: true, Underline: "dbl", Family: "Times New Roman", Size: 15, Strike: true, Color: "000000"}, Title: []RichTextRun{{Text: "Primary Horizontal Axis Title"}}}, YAxis: ChartAxis{Font: Font{Bold: false, Italic: false, Underline: "sng", Color: "777777"}, Title: []RichTextRun{{Text: "Primary Vertical Axis Title", Font: &Font{Color: "777777", Bold: true, Italic: true, Size: 12}}}}}},
{sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked Column Chart"}}, PlotArea: plotArea, Fill: Fill{Type: "pattern", Pattern: 1}, Border: ChartLine{Type: ChartLineAutomatic}, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X1", opts: &Chart{Type: ColStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "2D Stacked Column Chart"}}, PlotArea: plotArea, Fill: Fill{Type: "pattern", Pattern: 1}, Border: ChartLine{Type: ChartLineAutomatic}, ShowBlanksAs: "zero", GapWidth: uintPtr(10), Overlap: intPtr(100)}},
{sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "100% Stacked Column Chart"}}, PlotArea: plotArea, Fill: Fill{Type: "pattern", Color: []string{"EEEEEE"}, Pattern: 1}, Border: ChartLine{Type: ChartLineSolid, Width: 2}, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P16", opts: &Chart{Type: ColPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "100% Stacked Column Chart"}}, PlotArea: plotArea, Fill: Fill{Type: "pattern", Color: []string{"EEEEEE"}, Pattern: 1}, Border: ChartLine{Type: ChartLineSolid, Width: 2}, ShowBlanksAs: "zero", XAxis: ChartAxis{Alignment: Alignment{Vertical: "wordArtVertRtl", TextRotation: 0}}}},
{sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Clustered Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X16", opts: &Chart{Type: Col3DClustered, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Clustered Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "P30", opts: &Chart{Type: Col3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P30", opts: &Chart{Type: Col3DStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Alignment: Alignment{Vertical: "vert", TextRotation: 0}}}},
{sheetName: "Sheet1", cell: "X30", opts: &Chart{Type: Col3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D 100% Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "X30", opts: &Chart{Type: Col3DPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D 100% Stacked Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "X45", opts: &Chart{Type: Radar, Series: series, Format: format, Legend: ChartLegend{Position: "top_right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Radar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "span"}}, {sheetName: "Sheet1", cell: "X45", opts: &Chart{Type: Radar, Series: series, Format: format, Legend: ChartLegend{Position: "top_right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Radar Chart"}}, PlotArea: plotArea, ShowBlanksAs: "span"}},
{sheetName: "Sheet1", cell: "AF1", opts: &Chart{Type: Col3DConeStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cone Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AF1", opts: &Chart{Type: Col3DConeStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cone Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
@ -233,7 +238,7 @@ func TestAddChart(t *testing.T) {
{sheetName: "Sheet1", cell: "AV16", opts: &Chart{Type: Col3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AV16", opts: &Chart{Type: Col3DCylinderClustered, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Clustered Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AV30", opts: &Chart{Type: Col3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AV30", opts: &Chart{Type: Col3DCylinderPercentStacked, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Percent Stacked Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "AV45", opts: &Chart{Type: Col3DCylinder, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "AV45", opts: &Chart{Type: Col3DCylinder, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Cylinder Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet1", cell: "P45", opts: &Chart{Type: Col3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet1", cell: "P45", opts: &Chart{Type: Col3D, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "3D Column Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{Alignment: Alignment{Vertical: "vert270", TextRotation: 0}}}},
{sheetName: "Sheet2", cell: "P1", opts: &Chart{Type: Line3D, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Line Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}}}, {sheetName: "Sheet2", cell: "P1", opts: &Chart{Type: Line3D, Series: series2, Format: format, Legend: ChartLegend{Position: "top", ShowLegendKey: false}, Title: []RichTextRun{{Text: "3D Line Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, TickLabelSkip: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}, YAxis: ChartAxis{MajorGridLines: true, MinorGridLines: true, MajorUnit: 1, NumFmt: ChartNumFmt{CustomNumFmt: "General"}}}},
{sheetName: "Sheet2", cell: "X1", opts: &Chart{Type: Scatter, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Scatter Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}}, {sheetName: "Sheet2", cell: "X1", opts: &Chart{Type: Scatter, Series: series, Format: format, Legend: ChartLegend{Position: "bottom", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Scatter Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "P16", opts: &Chart{Type: Doughnut, Series: series3, Format: format, Legend: ChartLegend{Position: "right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Doughnut Chart"}}, PlotArea: ChartPlotArea{ShowBubbleSize: false, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: "zero", HoleSize: 30}}, {sheetName: "Sheet2", cell: "P16", opts: &Chart{Type: Doughnut, Series: series3, Format: format, Legend: ChartLegend{Position: "right", ShowLegendKey: false}, Title: []RichTextRun{{Text: "Doughnut Chart"}}, PlotArea: ChartPlotArea{ShowBubbleSize: false, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: false, ShowVal: false}, ShowBlanksAs: "zero", HoleSize: 30}},

46
col.go
View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -18,7 +18,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/mohae/deepcopy" "github.com/tiendc/go-deepcopy"
) )
// Define the default cell size and EMU unit of measurement. // Define the default cell size and EMU unit of measurement.
@ -450,6 +450,21 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error {
} }
s.mu.Unlock() s.mu.Unlock()
ws.mu.Lock() ws.mu.Lock()
ws.setColStyle(minVal, maxVal, styleID)
ws.mu.Unlock()
if rows := len(ws.SheetData.Row); rows > 0 {
for col := minVal; col <= maxVal; col++ {
from, _ := CoordinatesToCellName(col, 1)
to, _ := CoordinatesToCellName(col, rows)
err = f.SetCellStyle(sheet, from, to, styleID)
}
}
return err
}
// setColStyle provides a function to set the style of a single column or
// multiple columns.
func (ws *xlsxWorksheet) setColStyle(minVal, maxVal, styleID int) {
if ws.Cols == nil { if ws.Cols == nil {
ws.Cols = &xlsxCols{} ws.Cols = &xlsxCols{}
} }
@ -472,15 +487,6 @@ func (f *File) SetColStyle(sheet, columns string, styleID int) error {
fc.Width = c.Width fc.Width = c.Width
return fc return fc
}) })
ws.mu.Unlock()
if rows := len(ws.SheetData.Row); rows > 0 {
for col := minVal; col <= maxVal; col++ {
from, _ := CoordinatesToCellName(col, 1)
to, _ := CoordinatesToCellName(col, rows)
err = f.SetCellStyle(sheet, from, to, styleID)
}
}
return err
} }
// SetColWidth provides a function to set the width of a single column or // SetColWidth provides a function to set the width of a single column or
@ -504,6 +510,13 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
f.mu.Unlock() f.mu.Unlock()
ws.mu.Lock() ws.mu.Lock()
defer ws.mu.Unlock() defer ws.mu.Unlock()
ws.setColWidth(minVal, maxVal, width)
return err
}
// setColWidth provides a function to set the width of a single column or
// multiple columns.
func (ws *xlsxWorksheet) setColWidth(minVal, maxVal int, width float64) {
col := xlsxCol{ col := xlsxCol{
Min: minVal, Min: minVal,
Max: maxVal, Max: maxVal,
@ -514,7 +527,7 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
cols := xlsxCols{} cols := xlsxCols{}
cols.Col = append(cols.Col, col) cols.Col = append(cols.Col, col)
ws.Cols = &cols ws.Cols = &cols
return err return
} }
ws.Cols.Col = flatCols(col, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol { ws.Cols.Col = flatCols(col, ws.Cols.Col, func(fc, c xlsxCol) xlsxCol {
fc.BestFit = c.BestFit fc.BestFit = c.BestFit
@ -525,7 +538,6 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
fc.Style = c.Style fc.Style = c.Style
return fc return fc
}) })
return err
} }
// flatCols provides a method for the column's operation functions to flatten // flatCols provides a method for the column's operation functions to flatten
@ -533,7 +545,8 @@ func (f *File) SetColWidth(sheet, startCol, endCol string, width float64) error
func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol) []xlsxCol { func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol) []xlsxCol {
var fc []xlsxCol var fc []xlsxCol
for i := col.Min; i <= col.Max; i++ { for i := col.Min; i <= col.Max; i++ {
c := deepcopy.Copy(col).(xlsxCol) var c xlsxCol
deepcopy.Copy(&c, col)
c.Min, c.Max = i, i c.Min, c.Max = i, i
fc = append(fc, c) fc = append(fc, c)
} }
@ -551,7 +564,8 @@ func flatCols(col xlsxCol, cols []xlsxCol, replacer func(fc, c xlsxCol) xlsxCol)
fc[idx] = replacer(fc[idx], column) fc[idx] = replacer(fc[idx], column)
continue continue
} }
c := deepcopy.Copy(column).(xlsxCol) var c xlsxCol
deepcopy.Copy(&c, column)
c.Min, c.Max = i, i c.Min, c.Max = i, i
fc = append(fc, c) fc = append(fc, c)
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -20,7 +20,7 @@ import (
) )
// DataValidationType defined the type of data validation. // DataValidationType defined the type of data validation.
type DataValidationType int type DataValidationType byte
// Data validation types. // Data validation types.
const ( const (
@ -36,7 +36,7 @@ const (
) )
// DataValidationErrorStyle defined the style of data validation error alert. // DataValidationErrorStyle defined the style of data validation error alert.
type DataValidationErrorStyle int type DataValidationErrorStyle byte
// Data validation error styles. // Data validation error styles.
const ( const (
@ -54,7 +54,7 @@ const (
) )
// DataValidationOperator operator enum. // DataValidationOperator operator enum.
type DataValidationOperator int type DataValidationOperator byte
// Data validation operators. // Data validation operators.
const ( const (

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -213,17 +213,15 @@ func (f *File) drawBaseChart(pa *cPlotArea, opts *Chart) *cPlotArea {
Ser: f.drawChartSeries(opts), Ser: f.drawChartSeries(opts),
Shape: f.drawChartShape(opts), Shape: f.drawChartShape(opts),
DLbls: f.drawChartDLbls(opts), DLbls: f.drawChartDLbls(opts),
GapWidth: f.drawChartGapWidth(opts),
AxID: f.genAxID(opts), AxID: f.genAxID(opts),
Overlap: &attrValInt{Val: intPtr(100)}, Overlap: f.drawChartOverlap(opts),
}, },
} }
var ok bool var ok bool
if *c[0].BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok { if *c[0].BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok {
c[0].BarDir = nil c[0].BarDir = nil
} }
if *c[0].Overlap.Val, ok = plotAreaChartOverlap[opts.Type]; !ok {
c[0].Overlap = nil
}
catAx := f.drawPlotAreaCatAx(pa, opts) catAx := f.drawPlotAreaCatAx(pa, opts)
valAx := f.drawPlotAreaValAx(pa, opts) valAx := f.drawPlotAreaValAx(pa, opts)
charts := map[ChartType]*cPlotArea{ charts := map[ChartType]*cPlotArea{
@ -697,6 +695,35 @@ func (f *File) drawBubbleChart(pa *cPlotArea, opts *Chart) *cPlotArea {
return plotArea return plotArea
} }
// drawChartGapWidth provides a function to draw the c:gapWidth element by given
// format sets.
func (f *File) drawChartGapWidth(opts *Chart) *attrValInt {
for _, t := range barColChartTypes {
if t == opts.Type && opts.GapWidth != nil && *opts.GapWidth != 150 && *opts.GapWidth <= 500 {
return &attrValInt{intPtr(int(*opts.GapWidth))}
}
}
return nil
}
// drawChartOverlap provides a function to draw the c:overlap element by given
// format sets.
func (f *File) drawChartOverlap(opts *Chart) *attrValInt {
var val *attrValInt
if _, ok := plotAreaChartOverlap[opts.Type]; ok {
val = &attrValInt{intPtr(100)}
}
if opts.Overlap != nil && -100 <= *opts.Overlap && *opts.Overlap <= 100 {
val = &attrValInt{intPtr(*opts.Overlap)}
}
for _, t := range barColChartTypes {
if t == opts.Type {
return val
}
}
return nil
}
// drawChartShape provides a function to draw the c:shape element by given // drawChartShape provides a function to draw the c:shape element by given
// format sets. // format sets.
func (f *File) drawChartShape(opts *Chart) *attrValString { func (f *File) drawChartShape(opts *Chart) *attrValString {
@ -1001,6 +1028,10 @@ func (f *File) drawChartSeriesDLbls(i int, opts *Chart) *cDLbls {
dLbls.DLblPos = &attrValString{Val: stringPtr(chartDataLabelsPositionTypes[opts.Series[i].DataLabelPosition])} dLbls.DLblPos = &attrValString{Val: stringPtr(chartDataLabelsPositionTypes[opts.Series[i].DataLabelPosition])}
} }
} }
dLbl := opts.Series[i].DataLabel
dLbls.SpPr = f.drawShapeFill(dLbl.Fill, dLbls.SpPr)
dLbls.TxPr = &cTxPr{BodyPr: aBodyPr{}, P: aP{PPr: &aPPr{DefRPr: aRPr{}}}}
drawChartFont(&dLbl.Font, &dLbls.TxPr.P.PPr.DefRPr)
return dLbls return dLbls
} }
@ -1256,6 +1287,12 @@ func (f *File) drawPlotAreaTxPr(opts *ChartAxis) *cTxPr {
} }
if opts != nil { if opts != nil {
drawChartFont(&opts.Font, &cTxPr.P.PPr.DefRPr) drawChartFont(&opts.Font, &cTxPr.P.PPr.DefRPr)
if -90 <= opts.Alignment.TextRotation && opts.Alignment.TextRotation <= 90 {
cTxPr.BodyPr.Rot = opts.Alignment.TextRotation * 60000
}
if idx := inStrSlice(supportedDrawingTextVerticalType, opts.Alignment.Vertical, true); idx != -1 {
cTxPr.BodyPr.Vert = supportedDrawingTextVerticalType[idx]
}
} }
return cTxPr return cTxPr
} }
@ -1447,13 +1484,14 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, opts *GraphicOpt
// deleteDrawing provides a function to delete the chart graphic frame and // deleteDrawing provides a function to delete the chart graphic frame and
// returns deleted embed relationships ID (for unique picture cell anchor) by // returns deleted embed relationships ID (for unique picture cell anchor) by
// given coordinates and graphic type. // given coordinates and graphic type.
func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (string, error) { func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) ([]string, error) {
var ( var (
err error err error
rID string rID string
rIDs []string delRID, refRID []string
rIDMaps = map[string]int{}
wsDr *xlsxWsDr wsDr *xlsxWsDr
deTwoCellAnchor *decodeCellAnchor deCellAnchor *decodeCellAnchor
) )
xdrCellAnchorFuncs := map[string]func(anchor *xdrCellAnchor) bool{ xdrCellAnchorFuncs := map[string]func(anchor *xdrCellAnchor) bool{
"Chart": func(anchor *xdrCellAnchor) bool { return anchor.Pic == nil }, "Chart": func(anchor *xdrCellAnchor) bool { return anchor.Pic == nil },
@ -1465,54 +1503,70 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (stri
} }
onAnchorCell := func(c, r int) bool { return c == col && r == row } onAnchorCell := func(c, r int) bool { return c == col && r == row }
if wsDr, _, err = f.drawingParser(drawingXML); err != nil { if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
return rID, err return delRID, err
} }
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ { deleteCellAnchor := func(ca []*xdrCellAnchor) ([]*xdrCellAnchor, error) {
if err = nil; wsDr.TwoCellAnchor[idx].From != nil && xdrCellAnchorFuncs[drawingType](wsDr.TwoCellAnchor[idx]) { for idx := 0; idx < len(ca); idx++ {
if onAnchorCell(wsDr.TwoCellAnchor[idx].From.Col, wsDr.TwoCellAnchor[idx].From.Row) { if err = nil; ca[idx].From != nil && xdrCellAnchorFuncs[drawingType](ca[idx]) {
rID, _ = extractEmbedRID(wsDr.TwoCellAnchor[idx].Pic, nil, rIDs) rID = extractEmbedRID(ca[idx].Pic, nil)
wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...) rIDMaps[rID]++
if onAnchorCell(ca[idx].From.Col, ca[idx].From.Row) {
refRID = append(refRID, rID)
ca = append(ca[:idx], ca[idx+1:]...)
idx-- idx--
rIDMaps[rID]--
}
continue continue
} }
_, rIDs = extractEmbedRID(wsDr.TwoCellAnchor[idx].Pic, nil, rIDs) deCellAnchor = new(decodeCellAnchor)
if err = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + ca[idx].GraphicFrame + "</decodeCellAnchor>")).
Decode(deCellAnchor); err != nil && err != io.EOF {
return ca, err
} }
} if err = nil; deCellAnchor.From != nil && decodeCellAnchorFuncs[drawingType](deCellAnchor) {
for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ { rID = extractEmbedRID(nil, deCellAnchor.Pic)
deTwoCellAnchor = new(decodeCellAnchor) rIDMaps[rID]++
if err = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeCellAnchor>")). if onAnchorCell(deCellAnchor.From.Col, deCellAnchor.From.Row) {
Decode(deTwoCellAnchor); err != nil && err != io.EOF { refRID = append(refRID, rID)
return rID, err ca = append(ca[:idx], ca[idx+1:]...)
}
if err = nil; deTwoCellAnchor.From != nil && decodeCellAnchorFuncs[drawingType](deTwoCellAnchor) {
if onAnchorCell(deTwoCellAnchor.From.Col, deTwoCellAnchor.From.Row) {
rID, _ = extractEmbedRID(nil, deTwoCellAnchor.Pic, rIDs)
wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
idx-- idx--
continue rIDMaps[rID]--
}
_, rIDs = extractEmbedRID(nil, deTwoCellAnchor.Pic, rIDs)
} }
} }
if inStrSlice(rIDs, rID, true) != -1 { }
rID = "" return ca, err
}
if wsDr.OneCellAnchor, err = deleteCellAnchor(wsDr.OneCellAnchor); err != nil {
return delRID, err
}
if wsDr.TwoCellAnchor, err = deleteCellAnchor(wsDr.TwoCellAnchor); err != nil {
return delRID, err
} }
f.Drawings.Store(drawingXML, wsDr) f.Drawings.Store(drawingXML, wsDr)
return rID, err return getUnusedCellAnchorRID(delRID, refRID, rIDMaps), err
} }
// extractEmbedRID returns embed relationship ID and all relationship ID lists // extractEmbedRID returns embed relationship ID by giving cell anchor.
// for giving cell anchor. func extractEmbedRID(pic *xlsxPic, decodePic *decodePic) string {
func extractEmbedRID(pic *xlsxPic, decodePic *decodePic, rIDs []string) (string, []string) { var rID string
if pic != nil { if pic != nil {
rIDs = append(rIDs, pic.BlipFill.Blip.Embed) rID = pic.BlipFill.Blip.Embed
return pic.BlipFill.Blip.Embed, rIDs
} }
if decodePic != nil { if decodePic != nil {
rIDs = append(rIDs, decodePic.BlipFill.Blip.Embed) rID = decodePic.BlipFill.Blip.Embed
return decodePic.BlipFill.Blip.Embed, rIDs
} }
return "", rIDs return rID
}
// getUnusedCellAnchorRID returns relationship ID lists in the cell anchor which
// for remove.
func getUnusedCellAnchorRID(delRID, refRID []string, rIDMaps map[string]int) []string {
for _, rID := range refRID {
if rIDMaps[rID] == 0 && inStrSlice(delRID, rID, false) == -1 {
delRID = append(delRID, rID)
}
}
return delRID
} }
// deleteDrawingRels provides a function to delete relationships in // deleteDrawingRels provides a function to delete relationships in

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -88,6 +88,9 @@ var (
// ErrOutlineLevel defined the error message on receive an invalid outline // ErrOutlineLevel defined the error message on receive an invalid outline
// level number. // level number.
ErrOutlineLevel = errors.New("invalid outline level") ErrOutlineLevel = errors.New("invalid outline level")
// ErrPageSetupAdjustTo defined the error message for receiving a page setup
// adjust to value exceeds limit.
ErrPageSetupAdjustTo = errors.New("adjust to value must be between 10 and 400")
// ErrParameterInvalid defined the error message on receive the invalid // ErrParameterInvalid defined the error message on receive the invalid
// parameter. // parameter.
ErrParameterInvalid = errors.New("parameter is invalid") ErrParameterInvalid = errors.New("parameter is invalid")
@ -132,6 +135,9 @@ var (
// ErrSparklineType defined the error message on receive the invalid // ErrSparklineType defined the error message on receive the invalid
// sparkline Type parameters. // sparkline Type parameters.
ErrSparklineType = errors.New("parameter 'Type' must be 'line', 'column' or 'win_loss'") ErrSparklineType = errors.New("parameter 'Type' must be 'line', 'column' or 'win_loss'")
// ErrStreamSetColStyle defined the error message on set column style in
// stream writing mode.
ErrStreamSetColStyle = errors.New("must call the SetColStyle function before the SetRow function")
// ErrStreamSetColWidth defined the error message on set column width in // ErrStreamSetColWidth defined the error message on set column width in
// stream writing mode. // stream writing mode.
ErrStreamSetColWidth = errors.New("must call the SetColWidth function before the SetRow function") ErrStreamSetColWidth = errors.New("must call the SetColWidth function before the SetRow function")
@ -249,6 +255,12 @@ func newInvalidNameError(name string) error {
return fmt.Errorf("invalid name %q, the name should be starts with a letter or underscore, can not include a space or character, and can not conflict with an existing name in the workbook", name) return fmt.Errorf("invalid name %q, the name should be starts with a letter or underscore, can not include a space or character, and can not conflict with an existing name in the workbook", name)
} }
// newInvalidPageLayoutValueError defined the error message on receiving the invalid
// page layout options value.
func newInvalidPageLayoutValueError(name, value, msg string) error {
return fmt.Errorf("invalid %s value %q, acceptable value should be one of %s", name, value, msg)
}
// newInvalidRowNumberError defined the error message on receiving the invalid // newInvalidRowNumberError defined the error message on receiving the invalid
// row number. // row number.
func newInvalidRowNumberError(row int) error { func newInvalidRowNumberError(row int) error {

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
// //
// See https://xuri.me/excelize for more information about this package. // See https://xuri.me/excelize for more information about this package.
package excelize package excelize

View File

@ -628,7 +628,7 @@ func TestWriteArrayFormula(t *testing.T) {
valCell := cell(1, i+firstResLine) valCell := cell(1, i+firstResLine)
assocCell := cell(2, i+firstResLine) assocCell := cell(2, i+firstResLine)
assert.NoError(t, f.SetCellInt("Sheet1", valCell, values[i])) assert.NoError(t, f.SetCellInt("Sheet1", valCell, int64(values[i])))
assert.NoError(t, f.SetCellStr("Sheet1", assocCell, sample[assoc[i]])) assert.NoError(t, f.SetCellStr("Sheet1", assocCell, sample[assoc[i]]))
} }
@ -642,8 +642,8 @@ func TestWriteArrayFormula(t *testing.T) {
stdevCell := cell(i+2, 4) stdevCell := cell(i+2, 4)
calcStdevCell := cell(i+2, 5) calcStdevCell := cell(i+2, 5)
assert.NoError(t, f.SetCellInt("Sheet1", calcAvgCell, average(i))) assert.NoError(t, f.SetCellInt("Sheet1", calcAvgCell, int64(average(i))))
assert.NoError(t, f.SetCellInt("Sheet1", calcStdevCell, stdev(i))) assert.NoError(t, f.SetCellInt("Sheet1", calcStdevCell, int64(stdev(i))))
// Average can be done with AVERAGEIF // Average can be done with AVERAGEIF
assert.NoError(t, f.SetCellFormula("Sheet1", avgCell, fmt.Sprintf("ROUND(AVERAGEIF(%s,%s,%s),0)", assocRange, nameCell, valRange))) assert.NoError(t, f.SetCellFormula("Sheet1", avgCell, fmt.Sprintf("ROUND(AVERAGEIF(%s,%s,%s),0)", assocRange, nameCell, valRange)))
@ -870,11 +870,17 @@ func TestSetCellStyleCurrencyNumberFormat(t *testing.T) {
} }
func TestSetCellStyleLangNumberFormat(t *testing.T) { func TestSetCellStyleLangNumberFormat(t *testing.T) {
rawCellValues := [][]string{{"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}} rawCellValues := make([][]string, 42)
for i := 0; i < 42; i++ {
rawCellValues[i] = []string{"45162"}
}
for lang, expected := range map[CultureName][][]string{ for lang, expected := range map[CultureName][][]string{
CultureNameUnknown: rawCellValues, CultureNameUnknown: rawCellValues,
CultureNameEnUS: {{"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"0:00:00"}, {"0:00:00"}, {"0:00:00"}, {"0:00:00"}, {"45162"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}}, CultureNameEnUS: {{"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"0:00:00"}, {"0:00:00"}, {"0:00:00"}, {"0:00:00"}, {"45162"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"8/24/23"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameJaJP: {{"R5.8.24"}, {"令和5年8月24日"}, {"令和5年8月24日"}, {"8/24/23"}, {"2023年8月24日"}, {"0時00分"}, {"0時00分00秒"}, {"2023年8月"}, {"8月24日"}, {"R5.8.24"}, {"R5.8.24"}, {"令和5年8月24日"}, {"2023年8月"}, {"8月24日"}, {"令和5年8月24日"}, {"2023年8月"}, {"8月24日"}, {"R5.8.24"}, {"令和5年8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameKoKR: {{"4356年 08月 24日"}, {"08-24"}, {"08-24"}, {"08-24-56"}, {"4356년 08월 24일"}, {"0시 00분"}, {"0시 00분 00초"}, {"4356-08-24"}, {"4356-08-24"}, {"4356年 08月 24日"}, {"4356年 08月 24日"}, {"08-24"}, {"4356-08-24"}, {"4356-08-24"}, {"08-24"}, {"4356-08-24"}, {"4356-08-24"}, {"4356年 08月 24日"}, {"08-24"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameZhCN: {{"2023年8月"}, {"8月24日"}, {"8月24日"}, {"8/24/23"}, {"2023年8月24日"}, {"0时00分"}, {"0时00分00秒"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"2023年8月"}, {"8月24日"}, {"2023年8月"}, {"8月24日"}, {"8月24日"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}}, CultureNameZhCN: {{"2023年8月"}, {"8月24日"}, {"8月24日"}, {"8/24/23"}, {"2023年8月24日"}, {"0时00分"}, {"0时00分00秒"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"2023年8月"}, {"8月24日"}, {"2023年8月"}, {"8月24日"}, {"8月24日"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameZhTW: {{"112/8/24"}, {"112年8月24日"}, {"112年8月24日"}, {"8/24/23"}, {"2023年8月24日"}, {"00時00分"}, {"00時00分00秒"}, {"上午12時00分"}, {"上午12時00分00秒"}, {"112/8/24"}, {"112/8/24"}, {"112年8月24日"}, {"上午12時00分"}, {"上午12時00分00秒"}, {"112年8月24日"}, {"上午12時00分"}, {"上午12時00分00秒"}, {"112/8/24"}, {"112年8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
} { } {
f, err := prepareTestBook5(Options{CultureInfo: lang}) f, err := prepareTestBook5(Options{CultureInfo: lang})
assert.NoError(t, err) assert.NoError(t, err)
@ -886,7 +892,10 @@ func TestSetCellStyleLangNumberFormat(t *testing.T) {
// Test apply language number format code with date and time pattern // Test apply language number format code with date and time pattern
for lang, expected := range map[CultureName][][]string{ for lang, expected := range map[CultureName][][]string{
CultureNameEnUS: {{"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"00:00:00"}, {"00:00:00"}, {"00:00:00"}, {"00:00:00"}, {"45162"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}}, CultureNameEnUS: {{"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"00:00:00"}, {"00:00:00"}, {"00:00:00"}, {"00:00:00"}, {"45162"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"2023-8-24"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameJaJP: {{"R5.8.24"}, {"令和5年8月24日"}, {"令和5年8月24日"}, {"2023-8-24"}, {"2023年8月24日"}, {"00:00:00"}, {"00:00:00"}, {"2023年8月"}, {"8月24日"}, {"R5.8.24"}, {"R5.8.24"}, {"令和5年8月24日"}, {"2023年8月"}, {"8月24日"}, {"令和5年8月24日"}, {"2023年8月"}, {"8月24日"}, {"R5.8.24"}, {"令和5年8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameKoKR: {{"4356年 08月 24日"}, {"08-24"}, {"08-24"}, {"4356-8-24"}, {"4356년 08월 24일"}, {"00:00:00"}, {"00:00:00"}, {"4356-08-24"}, {"4356-08-24"}, {"4356年 08月 24日"}, {"4356年 08月 24日"}, {"08-24"}, {"4356-08-24"}, {"4356-08-24"}, {"08-24"}, {"4356-08-24"}, {"4356-08-24"}, {"4356年 08月 24日"}, {"08-24"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameZhCN: {{"2023年8月"}, {"8月24日"}, {"8月24日"}, {"2023-8-24"}, {"2023年8月24日"}, {"00:00:00"}, {"00:00:00"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"2023年8月"}, {"8月24日"}, {"2023年8月"}, {"8月24日"}, {"8月24日"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}}, CultureNameZhCN: {{"2023年8月"}, {"8月24日"}, {"8月24日"}, {"2023-8-24"}, {"2023年8月24日"}, {"00:00:00"}, {"00:00:00"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"2023年8月"}, {"8月24日"}, {"2023年8月"}, {"8月24日"}, {"8月24日"}, {"上午12时00分"}, {"上午12时00分00秒"}, {"2023年8月"}, {"8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
CultureNameZhTW: {{"112/8/24"}, {"112年8月24日"}, {"112年8月24日"}, {"2023-8-24"}, {"2023年8月24日"}, {"00:00:00"}, {"00:00:00"}, {"上午12時00分"}, {"上午12時00分00秒"}, {"112/8/24"}, {"112/8/24"}, {"112年8月24日"}, {"上午12時00分"}, {"上午12時00分00秒"}, {"112年8月24日"}, {"上午12時00分"}, {"上午12時00分00秒"}, {"112/8/24"}, {"112年8月24日"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}, {"45162"}},
} { } {
f, err := prepareTestBook5(Options{CultureInfo: lang, ShortDatePattern: "yyyy-M-d", LongTimePattern: "hh:mm:ss"}) f, err := prepareTestBook5(Options{CultureInfo: lang, ShortDatePattern: "yyyy-M-d", LongTimePattern: "hh:mm:ss"})
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

16
go.mod
View File

@ -1,17 +1,17 @@
module github.com/xuri/excelize/v2 module github.com/xuri/excelize/v2
go 1.18 go 1.20
require ( require (
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/richardlehane/mscfb v1.0.4 github.com/richardlehane/mscfb v1.0.4
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d github.com/tiendc/go-deepcopy v1.2.0
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6
golang.org/x/crypto v0.28.0 github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71
golang.org/x/crypto v0.32.0
golang.org/x/image v0.18.0 golang.org/x/image v0.18.0
golang.org/x/net v0.30.0 golang.org/x/net v0.34.0
golang.org/x/text v0.19.0 golang.org/x/text v0.21.0
) )
require ( require (

28
go.sum
View File

@ -1,7 +1,5 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
@ -9,20 +7,22 @@ github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY= github.com/tiendc/go-deepcopy v1.2.0 h1:6vCCs+qdLQHzFqY1fcPirsAWOmrLbuccilfp8UzD1Qo=
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/tiendc/go-deepcopy v1.2.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A= github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6 h1:8m6DWBG+dlFNbx5ynvrE7NgI+Y7OlZVMVTpayoW+rCc=
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= github.com/xuri/efp v0.0.0-20241211021726-c4e992084aa6/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71 h1:hOh7aVDrvGJRxzXrQbDY8E+02oaI//5cHL+97oYpEPw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= github.com/xuri/nfp v0.0.0-20250111060730-82a408b9aa71/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

13
lib.go
View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -652,11 +652,16 @@ func getRootElement(d *xml.Decoder) []xml.Attr {
case xml.StartElement: case xml.StartElement:
tokenIdx++ tokenIdx++
if tokenIdx == 1 { if tokenIdx == 1 {
var ns bool
for i := 0; i < len(startElement.Attr); i++ { for i := 0; i < len(startElement.Attr); i++ {
if startElement.Attr[i].Value == NameSpaceSpreadSheet.Value { if startElement.Attr[i].Value == NameSpaceSpreadSheet.Value &&
startElement.Attr[i] = NameSpaceSpreadSheet startElement.Attr[i].Name == NameSpaceSpreadSheet.Name {
ns = true
} }
} }
if !ns {
startElement.Attr = append(startElement.Attr, NameSpaceSpreadSheet)
}
return startElement.Attr return startElement.Attr
} }
} }

View File

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"runtime"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -289,6 +288,10 @@ func TestBytesReplace(t *testing.T) {
func TestGetRootElement(t *testing.T) { func TestGetRootElement(t *testing.T) {
assert.Len(t, getRootElement(xml.NewDecoder(strings.NewReader(""))), 0) assert.Len(t, getRootElement(xml.NewDecoder(strings.NewReader(""))), 0)
// Test get workbook root element which all workbook XML namespace has prefix
f := NewFile()
d := f.xmlNewDecoder(bytes.NewReader([]byte(`<x:workbook xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"></x:workbook>`)))
assert.Len(t, getRootElement(d), 3)
} }
func TestSetIgnorableNameSpace(t *testing.T) { func TestSetIgnorableNameSpace(t *testing.T) {
@ -358,9 +361,6 @@ func TestReadBytes(t *testing.T) {
} }
func TestUnzipToTemp(t *testing.T) { func TestUnzipToTemp(t *testing.T) {
if ver := runtime.Version(); strings.HasPrefix(ver, "go1.19") || strings.HasPrefix(ver, "go1.2") {
t.Skip()
}
os.Setenv("TMPDIR", "test") os.Setenv("TMPDIR", "test")
defer os.Unsetenv("TMPDIR") defer os.Unsetenv("TMPDIR")
assert.NoError(t, os.Chmod(os.TempDir(), 0o444)) assert.NoError(t, os.Chmod(os.TempDir(), 0o444))
@ -378,7 +378,7 @@ func TestUnzipToTemp(t *testing.T) {
"\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" + "\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" +
"00000000PK\x01\x0200000000" + "00000000PK\x01\x0200000000" +
"0000000000000000\v\x00\x00\x00" + "0000000000000000\v\x00\x00\x00" +
"\x00\x0000PK\x05\x06000000\x05\x000000" + "\x00\x0000PK\x05\x06000000\x05\x00\xfd\x00\x00\x00" +
"\v\x00\x00\x00\x00\x00") "\v\x00\x00\x00\x00\x00")
z, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) z, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

178
numfmt.go
View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -57,7 +57,10 @@ type CultureName byte
const ( const (
CultureNameUnknown CultureName = iota CultureNameUnknown CultureName = iota
CultureNameEnUS CultureNameEnUS
CultureNameJaJP
CultureNameKoKR
CultureNameZhCN CultureNameZhCN
CultureNameZhTW
) )
var ( var (
@ -791,7 +794,7 @@ var (
31748: {tags: []string{"zh-Hant"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2}, 31748: {tags: []string{"zh-Hant"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},
3076: {tags: []string{"zh-HK"}, localMonth: localMonthsNameChinese2, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2}, 3076: {tags: []string{"zh-HK"}, localMonth: localMonthsNameChinese2, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},
5124: {tags: []string{"zh-MO"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2}, 5124: {tags: []string{"zh-MO"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2},
1028: {tags: []string{"zh-TW"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2}, 1028: {tags: []string{"zh-TW"}, localMonth: localMonthsNameChinese3, apFmt: nfp.AmPm[2], weekdayNames: weekdayNamesChinese, weekdayNamesAbbr: weekdayNamesChineseAbbr2, useGannen: true},
9: {tags: []string{"en"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr}, 9: {tags: []string{"en"}, localMonth: localMonthsNameEnglish, apFmt: nfp.AmPm[0], weekdayNames: weekdayNamesEnglish, weekdayNamesAbbr: weekdayNamesEnglishAbbr},
4096: {tags: []string{ 4096: {tags: []string{
"aa", "aa-DJ", "aa-ER", "aa-ER", "aa-NA", "agq", "agq-CM", "ak", "ak-GH", "sq-ML", "aa", "aa-DJ", "aa-ER", "aa-ER", "aa-NA", "agq", "agq-CM", "ak", "ak-GH", "sq-ML",
@ -1168,6 +1171,10 @@ var (
"JA-JP-X-GANNEN": {tags: []string{"ja-JP"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr}, "JA-JP-X-GANNEN": {tags: []string{"ja-JP"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr},
"JA-JP-X-GANNEN,80": {tags: []string{"ja-JP"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr, useGannen: true}, "JA-JP-X-GANNEN,80": {tags: []string{"ja-JP"}, localMonth: localMonthsNameChinese3, apFmt: apFmtJapanese, weekdayNames: weekdayNamesJapanese, weekdayNamesAbbr: weekdayNamesJapaneseAbbr, useGannen: true},
} }
// republicOfChinaYear defined start time of the Republic of China
republicOfChinaYear = time.Date(1912, time.January, 1, 0, 0, 0, 0, time.UTC)
// republicOfChinaEraName defined the Republic of China era name for the Republic of China calendar.
republicOfChinaEraName = []string{"\u4e2d\u83ef\u6c11\u570b", "\u6c11\u570b", "\u524d"}
// japaneseEraYears list the Japanese era name periods. // japaneseEraYears list the Japanese era name periods.
japaneseEraYears = []time.Time{ japaneseEraYears = []time.Time{
time.Date(1868, time.August, 8, 0, 0, 0, 0, time.UTC), time.Date(1868, time.August, 8, 0, 0, 0, 0, time.UTC),
@ -4634,9 +4641,27 @@ var (
return r.Replace(s) return r.Replace(s)
}, },
} }
// langNumFmtFunc defines functions to apply language number format code.
langNumFmtFunc = map[CultureName]func(f *File, numFmtID int) string{
CultureNameEnUS: func(f *File, numFmtID int) string {
return f.langNumFmtFuncEnUS(numFmtID)
},
CultureNameJaJP: func(f *File, numFmtID int) string {
return f.langNumFmtFuncJaJP(numFmtID)
},
CultureNameKoKR: func(f *File, numFmtID int) string {
return f.langNumFmtFuncKoKR(numFmtID)
},
CultureNameZhCN: func(f *File, numFmtID int) string {
return f.langNumFmtFuncZhCN(numFmtID)
},
CultureNameZhTW: func(f *File, numFmtID int) string {
return f.langNumFmtFuncZhTW(numFmtID)
},
}
) )
// getSupportedLanguageInfo returns language infomation by giving language code. // getSupportedLanguageInfo returns language information by giving language code.
// This function does not support different calendar type of the language // This function does not support different calendar type of the language
// currently. For example: the hexadecimal language code 3010429 (fa-IR,301) // currently. For example: the hexadecimal language code 3010429 (fa-IR,301)
// will be convert to 0429 (fa-IR). // will be convert to 0429 (fa-IR).
@ -4694,6 +4719,54 @@ func (f *File) langNumFmtFuncEnUS(numFmtID int) string {
return "" return ""
} }
// langNumFmtFuncJaJP returns number format code by given date and time pattern
// for country code ja-jp.
func (f *File) langNumFmtFuncJaJP(numFmtID int) string {
if numFmtID == 30 && f.options.ShortDatePattern != "" {
return f.options.ShortDatePattern
}
if (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != "" {
return f.options.LongTimePattern
}
return langNumFmt["ja-jp"][numFmtID]
}
// langNumFmtFuncKoKR returns number format code by given date and time pattern
// for country code ko-kr.
func (f *File) langNumFmtFuncKoKR(numFmtID int) string {
if numFmtID == 30 && f.options.ShortDatePattern != "" {
return f.options.ShortDatePattern
}
if (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != "" {
return f.options.LongTimePattern
}
return langNumFmt["ko-kr"][numFmtID]
}
// langNumFmtFuncZhCN returns number format code by given date and time pattern
// for country code zh-cn.
func (f *File) langNumFmtFuncZhCN(numFmtID int) string {
if numFmtID == 30 && f.options.ShortDatePattern != "" {
return f.options.ShortDatePattern
}
if (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != "" {
return f.options.LongTimePattern
}
return langNumFmt["zh-cn"][numFmtID]
}
// langNumFmtFuncZhTW returns number format code by given date and time pattern
// for country code zh-tw.
func (f *File) langNumFmtFuncZhTW(numFmtID int) string {
if numFmtID == 30 && f.options.ShortDatePattern != "" {
return f.options.ShortDatePattern
}
if (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != "" {
return f.options.LongTimePattern
}
return langNumFmt["zh-tw"][numFmtID]
}
// checkDateTimePattern check and validate date and time options field value. // checkDateTimePattern check and validate date and time options field value.
func (f *File) checkDateTimePattern() error { func (f *File) checkDateTimePattern() error {
for _, pattern := range []string{f.options.LongDatePattern, f.options.LongTimePattern, f.options.ShortDatePattern} { for _, pattern := range []string{f.options.LongDatePattern, f.options.LongTimePattern, f.options.ShortDatePattern} {
@ -4770,18 +4843,6 @@ func (f *File) extractNumFmtDecimal(fmtCode string) int {
return -1 return -1
} }
// langNumFmtFuncZhCN returns number format code by given date and time pattern
// for country code zh-cn.
func (f *File) langNumFmtFuncZhCN(numFmtID int) string {
if numFmtID == 30 && f.options.ShortDatePattern != "" {
return f.options.ShortDatePattern
}
if (32 <= numFmtID && numFmtID <= 33) && f.options.LongTimePattern != "" {
return f.options.LongTimePattern
}
return langNumFmt["zh-cn"][numFmtID]
}
// getBuiltInNumFmtCode convert number format index to number format code with // getBuiltInNumFmtCode convert number format index to number format code with
// specified locale and language. // specified locale and language.
func (f *File) getBuiltInNumFmtCode(numFmtID int) (string, bool) { func (f *File) getBuiltInNumFmtCode(numFmtID int) (string, bool) {
@ -4789,11 +4850,8 @@ func (f *File) getBuiltInNumFmtCode(numFmtID int) (string, bool) {
return fmtCode, true return fmtCode, true
} }
if isLangNumFmt(numFmtID) { if isLangNumFmt(numFmtID) {
if f.options.CultureInfo == CultureNameEnUS { if fn, ok := langNumFmtFunc[f.options.CultureInfo]; ok {
return f.langNumFmtFuncEnUS(numFmtID), true return fn(f, numFmtID), true
}
if f.options.CultureInfo == CultureNameZhCN {
return f.langNumFmtFuncZhCN(numFmtID), true
} }
} }
return "", false return "", false
@ -6912,23 +6970,13 @@ func eraYear(t time.Time) (int, int) {
return i, year return i, year
} }
// yearsHandler will be handling years in the date and times types tokens for a // japaneseYearHandler handling the Japanease calendar years.
// number format expression. func (nf *numberFormat) japaneseYearHandler(token nfp.Token, langInfo languageInfo) {
func (nf *numberFormat) yearsHandler(token nfp.Token) {
if strings.Contains(strings.ToUpper(token.TValue), "Y") {
if len(token.TValue) <= 2 {
nf.result += strconv.Itoa(nf.t.Year())[2:]
return
}
nf.result += strconv.Itoa(nf.t.Year())
return
}
if strings.Contains(strings.ToUpper(token.TValue), "G") { if strings.Contains(strings.ToUpper(token.TValue), "G") {
i, year := eraYear(nf.t) i, year := eraYear(nf.t)
if year == -1 { if year == -1 {
return return
} }
langInfo, _ := getSupportedLanguageInfo(nf.localCode)
nf.useGannen = langInfo.useGannen nf.useGannen = langInfo.useGannen
switch len(token.TValue) { switch len(token.TValue) {
case 1: case 1:
@ -6939,7 +6987,6 @@ func (nf *numberFormat) yearsHandler(token nfp.Token) {
default: default:
nf.result += japaneseEraNames[i] nf.result += japaneseEraNames[i]
} }
return
} }
if strings.Contains(strings.ToUpper(token.TValue), "E") { if strings.Contains(strings.ToUpper(token.TValue), "E") {
_, year := eraYear(nf.t) _, year := eraYear(nf.t)
@ -6961,6 +7008,69 @@ func (nf *numberFormat) yearsHandler(token nfp.Token) {
} }
} }
// republicOfChinaYearHandler handling the Republic of China calendar years.
func (nf *numberFormat) republicOfChinaYearHandler(token nfp.Token, langInfo languageInfo) {
if strings.Contains(strings.ToUpper(token.TValue), "G") {
year := nf.t.Year() - republicOfChinaYear.Year() + 1
if year == 1 {
nf.useGannen = langInfo.useGannen
}
var name string
if name = republicOfChinaEraName[0]; len(token.TValue) < 3 {
name = republicOfChinaEraName[1]
}
if year < 0 {
name += republicOfChinaEraName[2]
}
nf.result += name
}
if strings.Contains(strings.ToUpper(token.TValue), "E") {
year := nf.t.Year() - republicOfChinaYear.Year() + 1
if year < 0 {
year = republicOfChinaYear.Year() - nf.t.Year()
}
if year == 1 && nf.useGannen {
nf.result += "\u5143"
return
}
if len(token.TValue) == 1 && !nf.useGannen {
nf.result += strconv.Itoa(year)
}
}
}
// yearsHandler will be handling years in the date and times types tokens for a
// number format expression.
func (nf *numberFormat) yearsHandler(token nfp.Token) {
langInfo, _ := getSupportedLanguageInfo(nf.localCode)
if strings.Contains(strings.ToUpper(token.TValue), "Y") {
year := nf.t.Year()
if nf.opts != nil && nf.opts.CultureInfo == CultureNameKoKR {
year += 2333
}
if len(token.TValue) <= 2 {
nf.result += strconv.Itoa(year)[2:]
return
}
nf.result += strconv.Itoa(year)
return
}
if inStrSlice(langInfo.tags, "zh-TW", false) != -1 ||
nf.opts != nil && nf.opts.CultureInfo == CultureNameZhTW {
nf.republicOfChinaYearHandler(token, langInfo)
return
}
if inStrSlice(langInfo.tags, "ja-JP", false) != -1 ||
nf.opts != nil && nf.opts.CultureInfo == CultureNameJaJP {
nf.japaneseYearHandler(token, langInfo)
return
}
if strings.Contains(strings.ToUpper(token.TValue), "E") {
nf.result += strconv.Itoa(nf.t.Year())
return
}
}
// daysHandler will be handling days in the date and times types tokens for a // daysHandler will be handling days in the date and times types tokens for a
// number format expression. // number format expression.
func (nf *numberFormat) daysHandler(token nfp.Token) { func (nf *numberFormat) daysHandler(token nfp.Token) {

View File

@ -289,6 +289,20 @@ func TestNumFmt(t *testing.T) {
{"43543.503206018519", "[$-401]mmmm dd yyyy h:mm AM/PM aaa", "\u0645\u0627\u0631\u0633 19 2019 12:04 \u0645 \u0627\u0644\u062B\u0644\u0627\u062B\u0627\u0621"}, {"43543.503206018519", "[$-401]mmmm dd yyyy h:mm AM/PM aaa", "\u0645\u0627\u0631\u0633 19 2019 12:04 \u0645 \u0627\u0644\u062B\u0644\u0627\u062B\u0627\u0621"},
{"43543.503206018519", "[$-401]mmmmm dd yyyy h:mm AM/PM ddd", "\u0645 19 2019 12:04 \u0645 \u0627\u0644\u062B\u0644\u0627\u062B\u0627\u0621"}, {"43543.503206018519", "[$-401]mmmmm dd yyyy h:mm AM/PM ddd", "\u0645 19 2019 12:04 \u0645 \u0627\u0644\u062B\u0644\u0627\u062B\u0627\u0621"},
{"43543.503206018519", "[$-401]mmmmmm dd yyyy h:mm AM/PM dddd", "\u0645\u0627\u0631\u0633 19 2019 12:04 \u0645 \u0627\u0644\u062B\u0644\u0627\u062B\u0627\u0621"}, {"43543.503206018519", "[$-401]mmmmmm dd yyyy h:mm AM/PM dddd", "\u0645\u0627\u0631\u0633 19 2019 12:04 \u0645 \u0627\u0644\u062B\u0644\u0627\u062B\u0627\u0621"},
{"43466.189571759256", "[$-404]g\"年\"m\"月\"d\"日\";@", "\u6c11\u570b\u5e74\u0031\u6708\u0031\u65e5"},
{"43466.189571759256", "[$-404]e\"年\"m\"月\"d\"日\";@", "\u0031\u0030\u0038\u5e74\u0031\u6708\u0031\u65e5"},
{"43466.189571759256", "[$-404]ge\"年\"m\"月\"d\"日\";@", "\u6c11\u570b\u0031\u0030\u0038\u5e74\u0031\u6708\u0031\u65e5"},
{"43466.189571759256", "[$-404]gge\"年\"m\"月\"d\"日\";@", "\u6c11\u570b\u0031\u0030\u0038\u5e74\u0031\u6708\u0031\u65e5"},
{"43466.189571759256", "[$-404]ggge\"年\"m\"月\"d\"日\";@", "\u4e2d\u83ef\u6c11\u570b\u0031\u0030\u0038\u5e74\u0031\u6708\u0031\u65e5"},
{"43466.189571759256", "[$-404]gggge\"年\"m\"月\"d\"日\";@", "\u4e2d\u83ef\u6c11\u570b\u0031\u0030\u0038\u5e74\u0031\u6708\u0031\u65e5"},
{"4385.5083333333332", "[$-404]ge\"年\"m\"月\"d\"日\";@", "\u6c11\u570b\u5143\u5e74\u0031\u6708\u0032\u65e5"},
{"4385.5083333333332", "[$-404]gge\"年\"m\"月\"d\"日\";@", "\u6c11\u570b\u5143\u5e74\u0031\u6708\u0032\u65e5"},
{"4385.5083333333332", "[$-404]ggge\"年\"m\"月\"d\"日\";@", "\u4e2d\u83ef\u6c11\u570b\u5143\u5e74\u0031\u6708\u0032\u65e5"},
{"4385.5083333333332", "[$-404]gggge\"年\"m\"月\"d\"日\";@", "\u4e2d\u83ef\u6c11\u570b\u5143\u5e74\u0031\u6708\u0032\u65e5"},
{"123", "[$-404]ge\"年\"m\"月\"d\"日\";@", "\u6c11\u570b\u524d\u0031\u0032\u5e74\u0035\u6708\u0032\u65e5"},
{"123", "[$-404]gge\"年\"m\"月\"d\"日\";@", "\u6c11\u570b\u524d\u0031\u0032\u5e74\u0035\u6708\u0032\u65e5"},
{"123", "[$-404]ggge\"年\"m\"月\"d\"日\";@", "\u4e2d\u83ef\u6c11\u570b\u524d\u0031\u0032\u5e74\u0035\u6708\u0032\u65e5"},
{"123", "[$-404]gggge\"年\"m\"月\"d\"日\";@", "\u4e2d\u83ef\u6c11\u570b\u524d\u0031\u0032\u5e74\u0035\u6708\u0032\u65e5"},
{"44562.189571759256", "[$-1010401]mmm dd yyyy h:mm AM/PM", "\u064A\u0646\u0627\u064A\u0631 01 2022 4:32 \u0635"}, {"44562.189571759256", "[$-1010401]mmm dd yyyy h:mm AM/PM", "\u064A\u0646\u0627\u064A\u0631 01 2022 4:32 \u0635"},
{"44562.189571759256", "[$-1010401]mmmm dd yyyy h:mm AM/PM", "\u064A\u0646\u0627\u064A\u0631 01 2022 4:32 \u0635"}, {"44562.189571759256", "[$-1010401]mmmm dd yyyy h:mm AM/PM", "\u064A\u0646\u0627\u064A\u0631 01 2022 4:32 \u0635"},
{"44562.189571759256", "[$-1010401]mmmmm dd yyyy h:mm AM/PM", "\u064A 01 2022 4:32 \u0635"}, {"44562.189571759256", "[$-1010401]mmmmm dd yyyy h:mm AM/PM", "\u064A 01 2022 4:32 \u0635"},
@ -2722,6 +2736,9 @@ func TestNumFmt(t *testing.T) {
{"44835.18957170139", "[$-41E]mmmmm dd yyyy h:mm AM/PM aaa", "\u0e15 01 2022 4:32 AM \u0E2A."}, {"44835.18957170139", "[$-41E]mmmmm dd yyyy h:mm AM/PM aaa", "\u0e15 01 2022 4:32 AM \u0E2A."},
{"44866.18957170139", "[$-41E]mmmmm dd yyyy h:mm AM/PM ddd", "\u0e1e 01 2022 4:32 AM \u0E2D."}, {"44866.18957170139", "[$-41E]mmmmm dd yyyy h:mm AM/PM ddd", "\u0e1e 01 2022 4:32 AM \u0E2D."},
{"44896.18957170139", "[$-41E]mmmmm dd yyyy h:mm AM/PM dddd", "\u0e18 01 2022 4:32 AM \u0E1E\u0E24\u0E2B\u0E31\u0E2A\u0E1A\u0E14\u0E35"}, {"44896.18957170139", "[$-41E]mmmmm dd yyyy h:mm AM/PM dddd", "\u0e18 01 2022 4:32 AM \u0E1E\u0E24\u0E2B\u0E31\u0E2A\u0E1A\u0E14\u0E35"},
{"100", "g\"年\"m\"月\"d\"日\";@", "年4月9日"},
{"100", "e\"年\"m\"月\"d\"日\";@", "1900年4月9日"},
{"100", "ge\"年\"m\"月\"d\"日\";@", "1900年4月9日"},
{"100", "[$-411]ge\"年\"m\"月\"d\"日\";@", "1900年4月9日"}, {"100", "[$-411]ge\"年\"m\"月\"d\"日\";@", "1900年4月9日"},
{"43709", "[$-411]ge\"年\"m\"月\"d\"日\";@", "R1年9月1日"}, {"43709", "[$-411]ge\"年\"m\"月\"d\"日\";@", "R1年9月1日"},
{"43709", "[$-411]gge\"年\"m\"月\"d\"日\";@", "\u4EE41年9月1日"}, {"43709", "[$-411]gge\"年\"m\"月\"d\"日\";@", "\u4EE41年9月1日"},

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -25,7 +25,7 @@ import (
// PictureInsertType defines the type of the picture has been inserted into the // PictureInsertType defines the type of the picture has been inserted into the
// worksheet. // worksheet.
type PictureInsertType int type PictureInsertType byte
// Insert picture types. // Insert picture types.
const ( const (
@ -295,6 +295,16 @@ func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
} }
} }
// addSheetLegacyDrawingHF provides a function to add legacy drawing
// header/footer element to xl/worksheets/sheet%d.xml by given
// worksheet name and relationship index.
func (f *File) addSheetLegacyDrawingHF(sheet string, rID int) {
ws, _ := f.workSheetReader(sheet)
ws.LegacyDrawingHF = &xlsxLegacyDrawingHF{
RID: "rId" + strconv.Itoa(rID),
}
}
// addSheetDrawing provides a function to add drawing element to // addSheetDrawing provides a function to add drawing element to
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index. // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
func (f *File) addSheetDrawing(sheet string, rID int) { func (f *File) addSheetDrawing(sheet string, rID int) {
@ -556,10 +566,11 @@ func (f *File) DeletePicture(sheet, cell string) error {
} }
drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl") drawingXML := strings.ReplaceAll(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl")
drawingRels := "xl/drawings/_rels/" + filepath.Base(drawingXML) + ".rels" drawingRels := "xl/drawings/_rels/" + filepath.Base(drawingXML) + ".rels"
rID, err := f.deleteDrawing(col, row, drawingXML, "Pic") rIDs, err := f.deleteDrawing(col, row, drawingXML, "Pic")
if err != nil { if err != nil {
return err return err
} }
for _, rID := range rIDs {
rels := f.getDrawingRelationships(drawingRels, rID) rels := f.getDrawingRelationships(drawingRels, rID)
if rels == nil { if rels == nil {
return err return err
@ -567,12 +578,15 @@ func (f *File) DeletePicture(sheet, cell string) error {
var used bool var used bool
checkPicRef := func(k, v interface{}) bool { checkPicRef := func(k, v interface{}) bool {
if strings.Contains(k.(string), "xl/drawings/_rels/drawing") { if strings.Contains(k.(string), "xl/drawings/_rels/drawing") {
if k.(string) == drawingRels {
return true
}
r, err := f.relsReader(k.(string)) r, err := f.relsReader(k.(string))
if err != nil { if err != nil {
return true return true
} }
for _, rel := range r.Relationships { for _, rel := range r.Relationships {
if rel.ID != rels.ID && rel.Type == SourceRelationshipImage && if rel.Type == SourceRelationshipImage &&
filepath.Base(rel.Target) == filepath.Base(rels.Target) { filepath.Base(rel.Target) == filepath.Base(rels.Target) {
used = true used = true
} }
@ -586,6 +600,7 @@ func (f *File) DeletePicture(sheet, cell string) error {
f.Pkg.Delete(strings.Replace(rels.Target, "../", "xl/", -1)) f.Pkg.Delete(strings.Replace(rels.Target, "../", "xl/", -1))
} }
f.deleteDrawingRels(drawingRels, rID) f.deleteDrawingRels(drawingRels, rID)
}
return err return err
} }

View File

@ -334,9 +334,9 @@ func TestDeletePicture(t *testing.T) {
f, err = OpenFile(filepath.Join("test", "TestDeletePicture.xlsx")) f, err = OpenFile(filepath.Join("test", "TestDeletePicture.xlsx"))
assert.NoError(t, err) assert.NoError(t, err)
// Test delete same picture on different worksheet, the images should be removed // Test delete same picture on different worksheet, the images should be removed
assert.NoError(t, f.DeletePicture("Sheet1", "F10")) assert.NoError(t, f.DeletePicture("Sheet1", "F20"))
assert.NoError(t, f.DeletePicture("Sheet2", "F1"))
assert.NoError(t, f.DeletePicture("Sheet1", "I20")) assert.NoError(t, f.DeletePicture("Sheet1", "I20"))
assert.NoError(t, f.DeletePicture("Sheet2", "F1"))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeletePicture2.xlsx"))) assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeletePicture2.xlsx")))
// Test delete picture on not exists worksheet // Test delete picture on not exists worksheet
@ -364,6 +364,14 @@ func TestDeletePicture(t *testing.T) {
assert.NoError(t, f.DeletePicture("Sheet2", "F1")) assert.NoError(t, f.DeletePicture("Sheet2", "F1"))
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
f, err = OpenFile(filepath.Join("test", "TestDeletePicture.xlsx"))
assert.NoError(t, err)
// Test delete picture without drawing relationships
f.Relationships.Delete("xl/drawings/_rels/drawing1.xml.rels")
f.Pkg.Delete("xl/drawings/_rels/drawing1.xml.rels")
assert.NoError(t, f.DeletePicture("Sheet1", "I20"))
assert.NoError(t, f.Close())
f = NewFile() f = NewFile()
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), nil)) assert.NoError(t, f.AddPicture("Sheet1", "A1", filepath.Join("test", "images", "excel.jpg"), nil))

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -899,14 +899,27 @@ func (f *File) getPivotTable(sheet, pivotTableXML, pivotCacheRels string) (Pivot
opts.ShowLastColumn = si.ShowLastColumn opts.ShowLastColumn = si.ShowLastColumn
opts.PivotTableStyleName = si.Name opts.PivotTableStyleName = si.Name
} }
order, err := f.getTableFieldsOrder(&opts) if err = f.getPivotTableDataRange(&opts); err != nil {
if err != nil {
return opts, err return opts, err
} }
f.extractPivotTableFields(order, pt, &opts) f.extractPivotTableFields(pc.getPivotCacheFieldsName(), pt, &opts)
return opts, err return opts, err
} }
// getPivotCacheFieldsName returns pivot table fields name list by order from
// pivot cache fields.
func (pc *xlsxPivotCacheDefinition) getPivotCacheFieldsName() []string {
var order []string
if pc.CacheFields != nil {
for _, cf := range pc.CacheFields.CacheField {
if cf != nil {
order = append(order, cf.Name)
}
}
}
return order
}
// pivotTableReader provides a function to get the pointer to the structure // pivotTableReader provides a function to get the pointer to the structure
// after deserialization of xl/pivotTables/pivotTable%d.xml. // after deserialization of xl/pivotTables/pivotTable%d.xml.
func (f *File) pivotTableReader(path string) (*xlsxPivotTableDefinition, error) { func (f *File) pivotTableReader(path string) (*xlsxPivotTableDefinition, error) {

View File

@ -516,7 +516,7 @@ func TestDeleteWorkbookPivotCache(t *testing.T) {
f := NewFile() f := NewFile()
// Test delete workbook pivot table cache with unsupported workbook charset // Test delete workbook pivot table cache with unsupported workbook charset
f.WorkBook = nil f.WorkBook = nil
f.Pkg.Store("xl/workbook.xml", MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
assert.EqualError(t, f.deleteWorkbookPivotCache(PivotTableOptions{pivotCacheXML: "pivotCache/pivotCacheDefinition1.xml"}), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.deleteWorkbookPivotCache(PivotTableOptions{pivotCacheXML: "pivotCache/pivotCacheDefinition1.xml"}), "XML syntax error on line 1: invalid UTF-8")
// Test delete workbook pivot table cache with unsupported workbook relationships charset // Test delete workbook pivot table cache with unsupported workbook relationships charset

14
rows.go
View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -20,7 +20,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/mohae/deepcopy" "github.com/tiendc/go-deepcopy"
) )
// duplicateHelperFunc defines functions to duplicate helper. // duplicateHelperFunc defines functions to duplicate helper.
@ -653,7 +653,7 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
for i, r := range ws.SheetData.Row { for i, r := range ws.SheetData.Row {
if r.R == row { if r.R == row {
rowCopy = deepcopy.Copy(ws.SheetData.Row[i]).(xlsxRow) deepcopy.Copy(&rowCopy, ws.SheetData.Row[i])
ok = true ok = true
break break
} }
@ -729,7 +729,8 @@ func (f *File) duplicateConditionalFormat(ws *xlsxWorksheet, sheet string, row,
} }
} }
if len(SQRef) > 0 { if len(SQRef) > 0 {
cfCopy := deepcopy.Copy(*cf).(xlsxConditionalFormatting) var cfCopy xlsxConditionalFormatting
deepcopy.Copy(&cfCopy, *cf)
cfCopy.SQRef = strings.Join(SQRef, " ") cfCopy.SQRef = strings.Join(SQRef, " ")
cfs = append(cfs, &cfCopy) cfs = append(cfs, &cfCopy)
} }
@ -759,7 +760,8 @@ func (f *File) duplicateDataValidations(ws *xlsxWorksheet, sheet string, row, ro
} }
} }
if len(SQRef) > 0 { if len(SQRef) > 0 {
dvCopy := deepcopy.Copy(*dv).(xlsxDataValidation) var dvCopy xlsxDataValidation
deepcopy.Copy(&dvCopy, *dv)
dvCopy.Sqref = strings.Join(SQRef, " ") dvCopy.Sqref = strings.Join(SQRef, " ")
dvs = append(dvs, &dvCopy) dvs = append(dvs, &dvCopy)
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -27,7 +27,23 @@ import (
"unicode/utf16" "unicode/utf16"
"unicode/utf8" "unicode/utf8"
"github.com/mohae/deepcopy" "github.com/tiendc/go-deepcopy"
)
// IgnoredErrorsType is the type of ignored errors.
type IgnoredErrorsType byte
// Ignored errors types enumeration.
const (
IgnoredErrorsEvalError = iota
IgnoredErrorsTwoDigitTextYear
IgnoredErrorsNumberStoredAsText
IgnoredErrorsFormula
IgnoredErrorsFormulaRange
IgnoredErrorsUnlockedFormula
IgnoredErrorsEmptyCellReference
IgnoredErrorsListDataValidation
IgnoredErrorsCalculatedColumn
) )
// NewSheet provides the function to create a new sheet by given a worksheet // NewSheet provides the function to create a new sheet by given a worksheet
@ -124,7 +140,8 @@ func (f *File) mergeExpandedCols(ws *xlsxWorksheet) {
Width: ws.Cols.Col[i-1].Width, Width: ws.Cols.Col[i-1].Width,
}, ws.Cols.Col[i]); i++ { }, ws.Cols.Col[i]); i++ {
} }
column := deepcopy.Copy(ws.Cols.Col[left]).(xlsxCol) var column xlsxCol
deepcopy.Copy(&column, ws.Cols.Col[left])
if left < i-1 { if left < i-1 {
column.Max = ws.Cols.Col[i-1].Min column.Max = ws.Cols.Col[i-1].Min
} }
@ -750,7 +767,8 @@ func (f *File) copySheet(from, to int) error {
if err != nil { if err != nil {
return err return err
} }
worksheet := deepcopy.Copy(sheet).(*xlsxWorksheet) worksheet := &xlsxWorksheet{}
deepcopy.Copy(worksheet, sheet)
toSheetID := strconv.Itoa(f.getSheetID(f.GetSheetName(to))) toSheetID := strconv.Itoa(f.getSheetID(f.GetSheetName(to)))
sheetXMLPath := "xl/worksheets/sheet" + toSheetID + ".xml" sheetXMLPath := "xl/worksheets/sheet" + toSheetID + ".xml"
if len(worksheet.SheetViews.SheetView) > 0 { if len(worksheet.SheetViews.SheetView) > 0 {
@ -1239,7 +1257,7 @@ func attrValToBool(name string, attrs []xml.Attr) (val bool, err error) {
// | // |
// &F | Current workbook's file name // &F | Current workbook's file name
// | // |
// &G | Drawing object as background (Not support currently) // &G | Drawing object as background (Use AddHeaderFooterImage)
// | // |
// &H | Shadow text format // &H | Shadow text format
// | // |
@ -1609,8 +1627,7 @@ func (f *File) SetPageLayout(sheet string, opts *PageLayoutOptions) error {
if opts == nil { if opts == nil {
return err return err
} }
ws.setPageSetUp(opts) return ws.setPageSetUp(opts)
return err
} }
// newPageSetUp initialize page setup settings for the worksheet if which not // newPageSetUp initialize page setup settings for the worksheet if which not
@ -1622,12 +1639,15 @@ func (ws *xlsxWorksheet) newPageSetUp() {
} }
// setPageSetUp set page setup settings for the worksheet by given options. // setPageSetUp set page setup settings for the worksheet by given options.
func (ws *xlsxWorksheet) setPageSetUp(opts *PageLayoutOptions) { func (ws *xlsxWorksheet) setPageSetUp(opts *PageLayoutOptions) error {
if opts.Size != nil { if opts.Size != nil {
ws.newPageSetUp() ws.newPageSetUp()
ws.PageSetUp.PaperSize = opts.Size ws.PageSetUp.PaperSize = opts.Size
} }
if opts.Orientation != nil && (*opts.Orientation == "portrait" || *opts.Orientation == "landscape") { if opts.Orientation != nil {
if inStrSlice(supportedPageOrientation, *opts.Orientation, true) == -1 {
return newInvalidPageLayoutValueError("Orientation", *opts.Orientation, strings.Join(supportedPageOrientation, ", "))
}
ws.newPageSetUp() ws.newPageSetUp()
ws.PageSetUp.Orientation = *opts.Orientation ws.PageSetUp.Orientation = *opts.Orientation
} }
@ -1636,7 +1656,10 @@ func (ws *xlsxWorksheet) setPageSetUp(opts *PageLayoutOptions) {
ws.PageSetUp.FirstPageNumber = strconv.Itoa(int(*opts.FirstPageNumber)) ws.PageSetUp.FirstPageNumber = strconv.Itoa(int(*opts.FirstPageNumber))
ws.PageSetUp.UseFirstPageNumber = true ws.PageSetUp.UseFirstPageNumber = true
} }
if opts.AdjustTo != nil && 10 <= *opts.AdjustTo && *opts.AdjustTo <= 400 { if opts.AdjustTo != nil {
if *opts.AdjustTo < 10 || 400 < *opts.AdjustTo {
return ErrPageSetupAdjustTo
}
ws.newPageSetUp() ws.newPageSetUp()
ws.PageSetUp.Scale = int(*opts.AdjustTo) ws.PageSetUp.Scale = int(*opts.AdjustTo)
} }
@ -1652,13 +1675,21 @@ func (ws *xlsxWorksheet) setPageSetUp(opts *PageLayoutOptions) {
ws.newPageSetUp() ws.newPageSetUp()
ws.PageSetUp.BlackAndWhite = *opts.BlackAndWhite ws.PageSetUp.BlackAndWhite = *opts.BlackAndWhite
} }
if opts.PageOrder != nil {
if inStrSlice(supportedPageOrder, *opts.PageOrder, true) == -1 {
return newInvalidPageLayoutValueError("PageOrder", *opts.PageOrder, strings.Join(supportedPageOrder, ", "))
}
ws.newPageSetUp()
ws.PageSetUp.PageOrder = *opts.PageOrder
}
return nil
} }
// GetPageLayout provides a function to gets worksheet page layout. // GetPageLayout provides a function to gets worksheet page layout.
func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) { func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {
opts := PageLayoutOptions{ opts := PageLayoutOptions{
Size: intPtr(0), Size: intPtr(0),
Orientation: stringPtr("portrait"), Orientation: stringPtr(supportedPageOrientation[0]),
FirstPageNumber: uintPtr(1), FirstPageNumber: uintPtr(1),
AdjustTo: uintPtr(100), AdjustTo: uintPtr(100),
} }
@ -1686,6 +1717,9 @@ func (f *File) GetPageLayout(sheet string) (PageLayoutOptions, error) {
opts.FitToWidth = ws.PageSetUp.FitToWidth opts.FitToWidth = ws.PageSetUp.FitToWidth
} }
opts.BlackAndWhite = boolPtr(ws.PageSetUp.BlackAndWhite) opts.BlackAndWhite = boolPtr(ws.PageSetUp.BlackAndWhite)
if ws.PageSetUp.PageOrder != "" {
opts.PageOrder = stringPtr(ws.PageSetUp.PageOrder)
}
} }
return opts, err return opts, err
} }
@ -2008,7 +2042,7 @@ func (f *File) relsReader(path string) (*xlsxRelationships, error) {
// fillSheetData ensures there are enough rows, and columns in the chosen // fillSheetData ensures there are enough rows, and columns in the chosen
// row to accept data. Missing rows are backfilled and given their row number // row to accept data. Missing rows are backfilled and given their row number
// Uses the last populated row as a hint for the size of the next row to add // Uses the last populated row as a hint for the size of the next row to add
func (ws *xlsxWorksheet) prepareSheetXML(col int, row int) { func (ws *xlsxWorksheet) prepareSheetXML(col, row int) {
rowCount := len(ws.SheetData.Row) rowCount := len(ws.SheetData.Row)
sizeHint := 0 sizeHint := 0
var ht *float64 var ht *float64
@ -2054,7 +2088,7 @@ func (ws *xlsxWorksheet) makeContiguousColumns(fromRow, toRow, colCount int) {
// of used cells in the worksheet. The range reference is set using the A1 // of used cells in the worksheet. The range reference is set using the A1
// reference style(e.g., "A1:D5"). Passing an empty range reference will remove // reference style(e.g., "A1:D5"). Passing an empty range reference will remove
// the used range of the worksheet. // the used range of the worksheet.
func (f *File) SetSheetDimension(sheet string, rangeRef string) error { func (f *File) SetSheetDimension(sheet, rangeRef string) error {
ws, err := f.workSheetReader(sheet) ws, err := f.workSheetReader(sheet)
if err != nil { if err != nil {
return err return err
@ -2097,3 +2131,35 @@ func (f *File) GetSheetDimension(sheet string) (string, error) {
} }
return ref, err return ref, err
} }
// AddIgnoredErrors provides the method to ignored error for a range of cells.
func (f *File) AddIgnoredErrors(sheet, rangeRef string, ignoredErrorsType IgnoredErrorsType) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if rangeRef == "" {
return ErrParameterInvalid
}
if ws.IgnoredErrors == nil {
ws.IgnoredErrors = &xlsxIgnoredErrors{}
}
ie := map[IgnoredErrorsType]xlsxIgnoredError{
IgnoredErrorsEvalError: {Sqref: rangeRef, EvalError: true},
IgnoredErrorsTwoDigitTextYear: {Sqref: rangeRef, TwoDigitTextYear: true},
IgnoredErrorsNumberStoredAsText: {Sqref: rangeRef, NumberStoredAsText: true},
IgnoredErrorsFormula: {Sqref: rangeRef, Formula: true},
IgnoredErrorsFormulaRange: {Sqref: rangeRef, FormulaRange: true},
IgnoredErrorsUnlockedFormula: {Sqref: rangeRef, UnlockedFormula: true},
IgnoredErrorsEmptyCellReference: {Sqref: rangeRef, EmptyCellReference: true},
IgnoredErrorsListDataValidation: {Sqref: rangeRef, ListDataValidation: true},
IgnoredErrorsCalculatedColumn: {Sqref: rangeRef, CalculatedColumn: true},
}[ignoredErrorsType]
for _, val := range ws.IgnoredErrors.IgnoredError {
if reflect.DeepEqual(val, ie) {
return err
}
}
ws.IgnoredErrors.IgnoredError = append(ws.IgnoredErrors.IgnoredError, ie)
return err
}

View File

@ -210,6 +210,7 @@ func TestSetPageLayout(t *testing.T) {
FitToHeight: intPtr(2), FitToHeight: intPtr(2),
FitToWidth: intPtr(2), FitToWidth: intPtr(2),
BlackAndWhite: boolPtr(true), BlackAndWhite: boolPtr(true),
PageOrder: stringPtr("overThenDown"),
} }
assert.NoError(t, f.SetPageLayout("Sheet1", &expected)) assert.NoError(t, f.SetPageLayout("Sheet1", &expected))
opts, err := f.GetPageLayout("Sheet1") opts, err := f.GetPageLayout("Sheet1")
@ -219,6 +220,16 @@ func TestSetPageLayout(t *testing.T) {
assert.EqualError(t, f.SetPageLayout("SheetN", nil), "sheet SheetN does not exist") assert.EqualError(t, f.SetPageLayout("SheetN", nil), "sheet SheetN does not exist")
// Test set page layout with invalid sheet name // Test set page layout with invalid sheet name
assert.EqualError(t, f.SetPageLayout("Sheet:1", nil), ErrSheetNameInvalid.Error()) assert.EqualError(t, f.SetPageLayout("Sheet:1", nil), ErrSheetNameInvalid.Error())
// Test set page layout with invalid parameters
assert.EqualError(t, f.SetPageLayout("Sheet1", &PageLayoutOptions{
AdjustTo: uintPtr(5),
}), "adjust to value must be between 10 and 400")
assert.EqualError(t, f.SetPageLayout("Sheet1", &PageLayoutOptions{
Orientation: stringPtr("x"),
}), "invalid Orientation value \"x\", acceptable value should be one of portrait, landscape")
assert.EqualError(t, f.SetPageLayout("Sheet1", &PageLayoutOptions{
PageOrder: stringPtr("x"),
}), "invalid PageOrder value \"x\", acceptable value should be one of overThenDown, downThenOver")
} }
func TestGetPageLayout(t *testing.T) { func TestGetPageLayout(t *testing.T) {
@ -580,7 +591,7 @@ func TestMoveSheet(t *testing.T) {
// Test move sheet with unsupported workbook charset // Test move sheet with unsupported workbook charset
f.WorkBook = nil f.WorkBook = nil
f.Pkg.Store("xl/workbook.xml", MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
assert.EqualError(t, f.MoveSheet("Sheet2", "Sheet1"), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.MoveSheet("Sheet2", "Sheet1"), "XML syntax error on line 1: invalid UTF-8")
} }
@ -664,7 +675,7 @@ func BenchmarkNewSheet(b *testing.B) {
func newSheetWithSet() { func newSheetWithSet() {
file := NewFile() file := NewFile()
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
_ = file.SetCellInt("Sheet1", "A"+strconv.Itoa(i+1), i) _ = file.SetCellInt("Sheet1", "A"+strconv.Itoa(i+1), int64(i))
} }
file = nil file = nil
} }
@ -680,7 +691,7 @@ func BenchmarkFile_SaveAs(b *testing.B) {
func newSheetWithSave() { func newSheetWithSave() {
file := NewFile() file := NewFile()
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
_ = file.SetCellInt("Sheet1", "A"+strconv.Itoa(i+1), i) _ = file.SetCellInt("Sheet1", "A"+strconv.Itoa(i+1), int64(i))
} }
_ = file.Save() _ = file.Save()
} }
@ -810,3 +821,23 @@ func TestSheetDimension(t *testing.T) {
assert.Empty(t, dimension) assert.Empty(t, dimension)
assert.EqualError(t, err, "sheet SheetN does not exist") assert.EqualError(t, err, "sheet SheetN does not exist")
} }
func TestAddIgnoredErrors(t *testing.T) {
f := NewFile()
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsEvalError))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsEvalError))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsTwoDigitTextYear))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsNumberStoredAsText))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsFormula))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsFormulaRange))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsUnlockedFormula))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsEmptyCellReference))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsListDataValidation))
assert.NoError(t, f.AddIgnoredErrors("Sheet1", "A1", IgnoredErrorsCalculatedColumn))
assert.Equal(t, ErrSheetNotExist{"SheetN"}, f.AddIgnoredErrors("SheetN", "A1", IgnoredErrorsEvalError))
assert.Equal(t, ErrParameterInvalid, f.AddIgnoredErrors("Sheet1", "", IgnoredErrorsEvalError))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddIgnoredErrors.xlsx")))
assert.NoError(t, f.Close())
}

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -87,7 +87,7 @@ func (ws *xlsxWorksheet) prepareSheetPr() {
} }
} }
// setSheetOutlinePr set worksheet outline properties by given options. // setSheetOutlineProps set worksheet outline properties by given options.
func (ws *xlsxWorksheet) setSheetOutlineProps(opts *SheetPropsOptions) { func (ws *xlsxWorksheet) setSheetOutlineProps(opts *SheetPropsOptions) {
prepareOutlinePr := func(ws *xlsxWorksheet) { prepareOutlinePr := func(ws *xlsxWorksheet) {
ws.prepareSheetPr() ws.prepareSheetPr()

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -597,7 +597,7 @@ func TestAddWorkbookSlicerCache(t *testing.T) {
// Test add a workbook slicer cache with unsupported charset workbook // Test add a workbook slicer cache with unsupported charset workbook
f := NewFile() f := NewFile()
f.WorkBook = nil f.WorkBook = nil
f.Pkg.Store("xl/workbook.xml", MacintoshCyrillicCharset) f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
assert.EqualError(t, f.addWorkbookSlicerCache(1, ExtURISlicerCachesX15), "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, f.addWorkbookSlicerCache(1, ExtURISlicerCachesX15), "XML syntax error on line 1: invalid UTF-8")
assert.NoError(t, f.Close()) assert.NoError(t, f.Close())
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -29,7 +29,6 @@ type StreamWriter struct {
Sheet string Sheet string
SheetID int SheetID int
sheetWritten bool sheetWritten bool
cols strings.Builder
worksheet *xlsxWorksheet worksheet *xlsxWorksheet
rawData bufferedWriter rawData bufferedWriter
rows int rows int
@ -413,16 +412,18 @@ func (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpt
if err != nil { if err != nil {
return err return err
} }
c := xlsxC{R: ref, S: options.StyleID} c := xlsxC{R: ref, S: sw.worksheet.prepareCellStyle(col, row, options.StyleID)}
var s int
if v, ok := val.(Cell); ok { if v, ok := val.(Cell); ok {
c.S = v.StyleID s, val = v.StyleID, v.Value
val = v.Value
setCellFormula(&c, v.Formula) setCellFormula(&c, v.Formula)
} else if v, ok := val.(*Cell); ok && v != nil { } else if v, ok := val.(*Cell); ok && v != nil {
c.S = v.StyleID s, val = v.StyleID, v.Value
val = v.Value
setCellFormula(&c, v.Formula) setCellFormula(&c, v.Formula)
} }
if s > 0 {
c.S = s
}
if err = sw.setCellValFunc(&c, val); err != nil { if err = sw.setCellValFunc(&c, val); err != nil {
_, _ = sw.rawData.WriteString(`</row>`) _, _ = sw.rawData.WriteString(`</row>`)
return err return err
@ -433,6 +434,33 @@ func (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpt
return sw.rawData.Sync() return sw.rawData.Sync()
} }
// SetColStyle provides a function to set the style of a single column or
// multiple columns for the StreamWriter. Note that you must call
// the 'SetColStyle' function before the 'SetRow' function. For example set
// style of column H on Sheet1:
//
// err := sw.SetColStyle(8, 8, style)
func (sw *StreamWriter) SetColStyle(minVal, maxVal, styleID int) error {
if sw.sheetWritten {
return ErrStreamSetColStyle
}
if minVal < MinColumns || minVal > MaxColumns || maxVal < MinColumns || maxVal > MaxColumns {
return ErrColumnNumber
}
if maxVal < minVal {
minVal, maxVal = maxVal, minVal
}
s, err := sw.file.stylesReader()
if err != nil {
return err
}
if styleID < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= styleID {
return newInvalidStyleID(styleID)
}
sw.worksheet.setColStyle(minVal, maxVal, styleID)
return nil
}
// SetColWidth provides a function to set the width of a single column or // SetColWidth provides a function to set the width of a single column or
// multiple columns for the StreamWriter. Note that you must call // multiple columns for the StreamWriter. Note that you must call
// the 'SetColWidth' function before the 'SetRow' function. For example set // the 'SetColWidth' function before the 'SetRow' function. For example set
@ -452,14 +480,7 @@ func (sw *StreamWriter) SetColWidth(minVal, maxVal int, width float64) error {
if minVal > maxVal { if minVal > maxVal {
minVal, maxVal = maxVal, minVal minVal, maxVal = maxVal, minVal
} }
sw.worksheet.setColWidth(minVal, maxVal, width)
sw.cols.WriteString(`<col min="`)
sw.cols.WriteString(strconv.Itoa(minVal))
sw.cols.WriteString(`" max="`)
sw.cols.WriteString(strconv.Itoa(maxVal))
sw.cols.WriteString(`" width="`)
sw.cols.WriteString(strconv.FormatFloat(width, 'f', -1, 64))
sw.cols.WriteString(`" customWidth="1"/>`)
return nil return nil
} }
@ -557,15 +578,15 @@ func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
func setCellIntFunc(c *xlsxC, val interface{}) { func setCellIntFunc(c *xlsxC, val interface{}) {
switch val := val.(type) { switch val := val.(type) {
case int: case int:
c.T, c.V = setCellInt(val) c.T, c.V = setCellInt(int64(val))
case int8: case int8:
c.T, c.V = setCellInt(int(val)) c.T, c.V = setCellInt(int64(val))
case int16: case int16:
c.T, c.V = setCellInt(int(val)) c.T, c.V = setCellInt(int64(val))
case int32: case int32:
c.T, c.V = setCellInt(int(val)) c.T, c.V = setCellInt(int64(val))
case int64: case int64:
c.T, c.V = setCellInt(int(val)) c.T, c.V = setCellInt(val)
case uint: case uint:
c.T, c.V = setCellUint(uint64(val)) c.T, c.V = setCellUint(uint64(val))
case uint8: case uint8:
@ -642,9 +663,26 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
func (sw *StreamWriter) writeSheetData() { func (sw *StreamWriter) writeSheetData() {
if !sw.sheetWritten { if !sw.sheetWritten {
bulkAppendFields(&sw.rawData, sw.worksheet, 4, 5) bulkAppendFields(&sw.rawData, sw.worksheet, 4, 5)
if sw.cols.Len() > 0 { if sw.worksheet.Cols != nil {
_, _ = sw.rawData.WriteString("<cols>") _, _ = sw.rawData.WriteString("<cols>")
_, _ = sw.rawData.WriteString(sw.cols.String()) for _, col := range sw.worksheet.Cols.Col {
sw.rawData.WriteString(`<col min="`)
sw.rawData.WriteString(strconv.Itoa(col.Min))
sw.rawData.WriteString(`" max="`)
sw.rawData.WriteString(strconv.Itoa(col.Max))
sw.rawData.WriteString(`"`)
if col.Width != nil {
sw.rawData.WriteString(` width="`)
sw.rawData.WriteString(strconv.FormatFloat(*col.Width, 'f', -1, 64))
sw.rawData.WriteString(`" customWidth="1"`)
}
if col.Style != 0 {
sw.rawData.WriteString(` style="`)
sw.rawData.WriteString(strconv.Itoa(col.Style))
sw.rawData.WriteString(`"`)
}
sw.rawData.WriteString(`/>`)
}
_, _ = sw.rawData.WriteString("</cols>") _, _ = sw.rawData.WriteString("</cols>")
} }
_, _ = sw.rawData.WriteString(`<sheetData>`) _, _ = sw.rawData.WriteString(`<sheetData>`)

View File

@ -154,19 +154,53 @@ func TestStreamWriter(t *testing.T) {
assert.NoError(t, file.Close()) assert.NoError(t, file.Close())
} }
func TestStreamSetColWidth(t *testing.T) { func TestStreamSetColStyle(t *testing.T) {
file := NewFile() file := NewFile()
defer func() { defer func() {
assert.NoError(t, file.Close()) assert.NoError(t, file.Close())
}() }()
streamWriter, err := file.NewStreamWriter("Sheet1") streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, streamWriter.SetColStyle(3, 2, 0))
assert.Equal(t, ErrColumnNumber, streamWriter.SetColStyle(0, 3, 20))
assert.Equal(t, ErrColumnNumber, streamWriter.SetColStyle(MaxColumns+1, 3, 20))
assert.Equal(t, newInvalidStyleID(2), streamWriter.SetColStyle(1, 3, 2))
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
assert.Equal(t, ErrStreamSetColStyle, streamWriter.SetColStyle(2, 3, 0))
file = NewFile()
defer func() {
assert.NoError(t, file.Close())
}()
// Test set column style with unsupported charset style sheet
file.Styles = nil
file.Pkg.Store(defaultXMLPathStyles, MacintoshCyrillicCharset)
streamWriter, err = file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
assert.EqualError(t, streamWriter.SetColStyle(3, 2, 0), "XML syntax error on line 1: invalid UTF-8")
}
func TestStreamSetColWidth(t *testing.T) {
file := NewFile()
defer func() {
assert.NoError(t, file.Close())
}()
styleID, err := file.NewStyle(&Style{
Fill: Fill{Type: "pattern", Color: []string{"E0EBF5"}, Pattern: 1},
})
if err != nil {
fmt.Println(err)
}
streamWriter, err := file.NewStreamWriter("Sheet1")
assert.NoError(t, err)
assert.NoError(t, streamWriter.SetColWidth(3, 2, 20)) assert.NoError(t, streamWriter.SetColWidth(3, 2, 20))
assert.NoError(t, streamWriter.SetColStyle(3, 2, styleID))
assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(0, 3, 20)) assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(0, 3, 20))
assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(MaxColumns+1, 3, 20)) assert.Equal(t, ErrColumnNumber, streamWriter.SetColWidth(MaxColumns+1, 3, 20))
assert.Equal(t, ErrColumnWidth, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1)) assert.Equal(t, ErrColumnWidth, streamWriter.SetColWidth(1, 3, MaxColumnWidth+1))
assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"})) assert.NoError(t, streamWriter.SetRow("A1", []interface{}{"A", "B", "C"}))
assert.Equal(t, ErrStreamSetColWidth, streamWriter.SetColWidth(2, 3, 20)) assert.Equal(t, ErrStreamSetColWidth, streamWriter.SetColWidth(2, 3, 20))
assert.NoError(t, streamWriter.Flush())
} }
func TestStreamSetPanes(t *testing.T) { func TestStreamSetPanes(t *testing.T) {
@ -323,7 +357,6 @@ func TestStreamSetRowWithStyle(t *testing.T) {
defer func() { defer func() {
assert.NoError(t, file.Close()) assert.NoError(t, file.Close())
}() }()
zeroStyleID := 0
grayStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "777777"}}) grayStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "777777"}})
assert.NoError(t, err) assert.NoError(t, err)
blueStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "0000FF"}}) blueStyleID, err := file.NewStyle(&Style{Font: &Font{Color: "0000FF"}})
@ -342,7 +375,7 @@ func TestStreamSetRowWithStyle(t *testing.T) {
ws, err := file.workSheetReader("Sheet1") ws, err := file.workSheetReader("Sheet1")
assert.NoError(t, err) assert.NoError(t, err)
for colIdx, expected := range []int{grayStyleID, zeroStyleID, zeroStyleID, blueStyleID, blueStyleID} { for colIdx, expected := range []int{grayStyleID, grayStyleID, grayStyleID, blueStyleID, blueStyleID} {
assert.Equal(t, expected, ws.SheetData.Row[0].C[colIdx].S) assert.Equal(t, expected, ws.SheetData.Row[0].C[colIdx].S)
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -1441,8 +1441,8 @@ func (f *File) getThemeColor(clr *xlsxColor) string {
func (f *File) extractBorders(bdr *xlsxBorder, s *xlsxStyleSheet, style *Style) { func (f *File) extractBorders(bdr *xlsxBorder, s *xlsxStyleSheet, style *Style) {
if bdr != nil { if bdr != nil {
var borders []Border var borders []Border
extractBorder := func(lineType string, line xlsxLine) { extractBorder := func(lineType string, line *xlsxLine) {
if line.Style != "" { if line != nil && line.Style != "" {
borders = append(borders, Border{ borders = append(borders, Border{
Type: lineType, Type: lineType,
Color: f.getThemeColor(line.Color), Color: f.getThemeColor(line.Color),
@ -1450,7 +1450,7 @@ func (f *File) extractBorders(bdr *xlsxBorder, s *xlsxStyleSheet, style *Style)
}) })
} }
} }
for i, line := range []xlsxLine{ for i, line := range []*xlsxLine{
bdr.Left, bdr.Right, bdr.Top, bdr.Bottom, bdr.Diagonal, bdr.Diagonal, bdr.Left, bdr.Right, bdr.Top, bdr.Bottom, bdr.Diagonal, bdr.Diagonal,
} { } {
if i < 4 { if i < 4 {
@ -2051,11 +2051,12 @@ func newFills(style *Style, fg bool) *xlsxFill {
if style.Fill.Pattern > 18 || style.Fill.Pattern < 0 { if style.Fill.Pattern > 18 || style.Fill.Pattern < 0 {
break break
} }
if len(style.Fill.Color) < 1 {
break
}
var pattern xlsxPatternFill var pattern xlsxPatternFill
pattern.PatternType = styleFillPatterns[style.Fill.Pattern] pattern.PatternType = styleFillPatterns[style.Fill.Pattern]
if len(style.Fill.Color) < 1 {
fill.PatternFill = &pattern
break
}
if fg { if fg {
if pattern.FgColor == nil { if pattern.FgColor == nil {
pattern.FgColor = new(xlsxColor) pattern.FgColor = new(xlsxColor)
@ -2127,29 +2128,20 @@ func newBorders(style *Style) *xlsxBorder {
var border xlsxBorder var border xlsxBorder
for _, v := range style.Border { for _, v := range style.Border {
if 0 <= v.Style && v.Style < 14 { if 0 <= v.Style && v.Style < 14 {
var color xlsxColor line := &xlsxLine{Style: styleBorders[v.Style], Color: &xlsxColor{RGB: getPaletteColor(v.Color)}}
color.RGB = getPaletteColor(v.Color)
switch v.Type { switch v.Type {
case "left": case "left":
border.Left.Style = styleBorders[v.Style] border.Left = line
border.Left.Color = &color
case "right": case "right":
border.Right.Style = styleBorders[v.Style] border.Right = line
border.Right.Color = &color
case "top": case "top":
border.Top.Style = styleBorders[v.Style] border.Top = line
border.Top.Color = &color
case "bottom": case "bottom":
border.Bottom.Style = styleBorders[v.Style] border.Bottom = line
border.Bottom.Color = &color
case "diagonalUp": case "diagonalUp":
border.Diagonal.Style = styleBorders[v.Style] border.Diagonal, border.DiagonalUp = line, true
border.Diagonal.Color = &color
border.DiagonalUp = true
case "diagonalDown": case "diagonalDown":
border.Diagonal.Style = styleBorders[v.Style] border.Diagonal, border.DiagonalDown = line, true
border.Diagonal.Color = &color
border.DiagonalDown = true
} }
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
// //
// This file contains default templates for XML files we don't yet populated // This file contains default templates for XML files we don't yet populated
// based on content. // based on content.
@ -495,9 +495,19 @@ var supportedDrawingUnderlineTypes = []string{
"wavyDbl", "wavyDbl",
} }
// supportedDrawingTextVerticalType defined supported text vertical types in
// drawing markup language.
var supportedDrawingTextVerticalType = []string{"horz", "vert", "vert270", "wordArtVert", "eaVert", "mongolianVert", "wordArtVertRtl"}
// supportedPositioning defined supported positioning types. // supportedPositioning defined supported positioning types.
var supportedPositioning = []string{"absolute", "oneCell", "twoCell"} var supportedPositioning = []string{"absolute", "oneCell", "twoCell"}
// supportedPageOrientation defined supported page setup page orientation.
var supportedPageOrientation = []string{"portrait", "landscape"}
// supportedPageOrder defined supported page setup page order.
var supportedPageOrder = []string{"overThenDown", "downThenOver"}
// builtInDefinedNames defined built-in defined names are built with a _xlnm prefix. // builtInDefinedNames defined built-in defined names are built with a _xlnm prefix.
var builtInDefinedNames = []string{"_xlnm.Print_Area", "_xlnm.Print_Titles", "_xlnm.Criteria", "_xlnm._FilterDatabase", "_xlnm.Extract", "_xlnm.Consolidate_Area", "_xlnm.Database", "_xlnm.Sheet_Title"} var builtInDefinedNames = []string{"_xlnm.Print_Area", "_xlnm.Print_Titles", "_xlnm.Criteria", "_xlnm._FilterDatabase", "_xlnm.Extract", "_xlnm.Consolidate_Area", "_xlnm.Database", "_xlnm.Sheet_Title"}

158
vml.go
View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -36,6 +36,16 @@ const (
FormControlScrollBar FormControlScrollBar
) )
// HeaderFooterImagePositionType is the type of header and footer image position.
type HeaderFooterImagePositionType byte
// Worksheet header and footer image position types enumeration.
const (
HeaderFooterImagePositionLeft HeaderFooterImagePositionType = iota
HeaderFooterImagePositionCenter
HeaderFooterImagePositionRight
)
// GetComments retrieves all comments in a worksheet by given worksheet name. // GetComments retrieves all comments in a worksheet by given worksheet name.
func (f *File) GetComments(sheet string) ([]Comment, error) { func (f *File) GetComments(sheet string) ([]Comment, error) {
var comments []Comment var comments []Comment
@ -519,6 +529,7 @@ func (f *File) addVMLObject(opts vmlOptions) error {
} }
vmlID = f.countVMLDrawing() + 1 vmlID = f.countVMLDrawing() + 1
} }
sheetID := f.getSheetID(opts.sheet)
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml" drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml" sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
sheetXMLPath, _ := f.getSheetXMLPath(opts.sheet) sheetXMLPath, _ := f.getSheetXMLPath(opts.sheet)
@ -534,7 +545,7 @@ func (f *File) addVMLObject(opts vmlOptions) error {
f.addSheetNameSpace(opts.sheet, SourceRelationship) f.addSheetNameSpace(opts.sheet, SourceRelationship)
f.addSheetLegacyDrawing(opts.sheet, rID) f.addSheetLegacyDrawing(opts.sheet, rID)
} }
if err = f.addDrawingVML(vmlID, drawingVML, prepareFormCtrlOptions(&opts)); err != nil { if err = f.addDrawingVML(sheetID, drawingVML, prepareFormCtrlOptions(&opts)); err != nil {
return err return err
} }
if !opts.formCtrl { if !opts.formCtrl {
@ -823,7 +834,7 @@ func (f *File) addFormCtrlShape(preset formCtrlPreset, col, row int, anchor stri
// anchor value is a comma-separated list of data written out as: LeftColumn, // anchor value is a comma-separated list of data written out as: LeftColumn,
// LeftOffset, TopRow, TopOffset, RightColumn, RightOffset, BottomRow, // LeftOffset, TopRow, TopOffset, RightColumn, RightOffset, BottomRow,
// BottomOffset. // BottomOffset.
func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) error { func (f *File) addDrawingVML(sheetID int, drawingVML string, opts *vmlOptions) error {
col, row, err := CellNameToCoordinates(opts.FormControl.Cell) col, row, err := CellNameToCoordinates(opts.FormControl.Cell)
if err != nil { if err != nil {
return err return err
@ -843,7 +854,7 @@ func (f *File) addDrawingVML(dataID int, drawingVML string, opts *vmlOptions) er
XMLNSx: "urn:schemas-microsoft-com:office:excel", XMLNSx: "urn:schemas-microsoft-com:office:excel",
XMLNSmv: "http://macVmlSchemaUri", XMLNSmv: "http://macVmlSchemaUri",
ShapeLayout: &xlsxShapeLayout{ ShapeLayout: &xlsxShapeLayout{
Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: dataID}, Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: sheetID},
}, },
ShapeType: &xlsxShapeType{ ShapeType: &xlsxShapeType{
ID: fmt.Sprintf("_x0000_t%d", vmlID), ID: fmt.Sprintf("_x0000_t%d", vmlID),
@ -1070,3 +1081,140 @@ func extractVMLFont(font []decodeVMLFont) []RichTextRun {
} }
return runs return runs
} }
// AddHeaderFooterImage provides a mechanism to set the graphics that can be
// referenced in the header and footer definitions via &G, supported image
// types: EMF, EMZ, GIF, JPEG, JPG, PNG, SVG, TIF, TIFF, WMF, and WMZ.
//
// The extension should be provided with a "." in front, e.g. ".png".
// The width and height should have units in them, e.g. "100pt".
func (f *File) AddHeaderFooterImage(sheet string, opts *HeaderFooterImageOptions) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
ext, ok := supportedImageTypes[strings.ToLower(opts.Extension)]
if !ok {
return ErrImgExt
}
sheetID := f.getSheetID(sheet)
vmlID := f.countVMLDrawing() + 1
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(vmlID) + ".vml"
sheetXMLPath, _ := f.getSheetXMLPath(sheet)
sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetXMLPath, "xl/worksheets/") + ".rels"
if ws.LegacyDrawingHF != nil {
// The worksheet already has a VML relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.
sheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(sheet, ws.LegacyDrawingHF.RID)
vmlID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, "../drawings/vmlDrawing"), ".vml"))
drawingVML = strings.ReplaceAll(sheetRelationshipsDrawingVML, "..", "xl")
} else {
// Add first VML drawing for given sheet.
rID := f.addRels(sheetRels, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
f.addSheetNameSpace(sheet, SourceRelationship)
f.addSheetLegacyDrawingHF(sheet, rID)
}
shapeID := map[HeaderFooterImagePositionType]string{
HeaderFooterImagePositionLeft: "L",
HeaderFooterImagePositionCenter: "C",
HeaderFooterImagePositionRight: "R",
}[opts.Position] +
map[bool]string{false: "H", true: "F"}[opts.IsFooter] +
map[bool]string{false: "", true: "FIRST"}[opts.FirstPage]
vml := f.VMLDrawing[drawingVML]
if vml == nil {
vml = &vmlDrawing{
XMLNSv: "urn:schemas-microsoft-com:vml",
XMLNSo: "urn:schemas-microsoft-com:office:office",
XMLNSx: "urn:schemas-microsoft-com:office:excel",
ShapeLayout: &xlsxShapeLayout{
Ext: "edit", IDmap: &xlsxIDmap{Ext: "edit", Data: sheetID},
},
ShapeType: &xlsxShapeType{
ID: "_x0000_t75",
CoordSize: "21600,21600",
Spt: 75,
PreferRelative: "t",
Path: "m@4@5l@4@11@9@11@9@5xe",
Filled: "f",
Stroked: "f",
Stroke: &xlsxStroke{JoinStyle: "miter"},
VFormulas: &vFormulas{
Formulas: []vFormula{
{Equation: "if lineDrawn pixelLineWidth 0"},
{Equation: "sum @0 1 0"},
{Equation: "sum 0 0 @1"},
{Equation: "prod @2 1 2"},
{Equation: "prod @3 21600 pixelWidth"},
{Equation: "prod @3 21600 pixelHeight"},
{Equation: "sum @0 0 1"},
{Equation: "prod @6 1 2"},
{Equation: "prod @7 21600 pixelWidth"},
{Equation: "sum @8 21600 0"},
{Equation: "prod @7 21600 pixelHeight"},
{Equation: "sum @10 21600 0"},
},
},
VPath: &vPath{ExtrusionOK: "f", GradientShapeOK: "t", ConnectType: "rect"},
Lock: &oLock{Ext: "edit", AspectRatio: "t"},
},
}
// Load exist VML shapes from xl/drawings/vmlDrawing%d.vml
d, err := f.decodeVMLDrawingReader(drawingVML)
if err != nil {
return err
}
if d != nil {
vml.ShapeType.ID = d.ShapeType.ID
vml.ShapeType.CoordSize = d.ShapeType.CoordSize
vml.ShapeType.Spt = d.ShapeType.Spt
vml.ShapeType.PreferRelative = d.ShapeType.PreferRelative
vml.ShapeType.Path = d.ShapeType.Path
vml.ShapeType.Filled = d.ShapeType.Filled
vml.ShapeType.Stroked = d.ShapeType.Stroked
for _, v := range d.Shape {
s := xlsxShape{
ID: v.ID,
SpID: v.SpID,
Type: v.Type,
Style: v.Style,
Val: v.Val,
}
vml.Shape = append(vml.Shape, s)
}
}
}
for idx, shape := range vml.Shape {
if shape.ID == shapeID {
vml.Shape = append(vml.Shape[:idx], vml.Shape[idx+1:]...)
}
}
style := fmt.Sprintf("position:absolute;margin-left:0;margin-top:0;width:%s;height:%s;z-index:1", opts.Width, opts.Height)
drawingVMLRels := "xl/drawings/_rels/vmlDrawing" + strconv.Itoa(vmlID) + ".vml.rels"
mediaStr := ".." + strings.TrimPrefix(f.addMedia(opts.File, ext), "xl")
imageID := f.addRels(drawingVMLRels, SourceRelationshipImage, mediaStr, "")
shape := xlsxShape{
ID: shapeID,
SpID: "_x0000_s1025",
Type: "#_x0000_t75",
Style: style,
}
sp, _ := xml.Marshal(encodeShape{
ImageData: &vImageData{RelID: "rId" + strconv.Itoa(imageID)},
Lock: &oLock{Ext: "edit", Rotation: "t"},
})
shape.Val = string(sp[13 : len(sp)-14])
vml.Shape = append(vml.Shape, shape)
f.VMLDrawing[drawingVML] = vml
if err := f.setContentTypePartImageExtensions(); err != nil {
return err
}
return f.setContentTypePartVMLExtensions()
}

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -20,7 +20,7 @@ type vmlDrawing struct {
XMLNSv string `xml:"xmlns:v,attr"` XMLNSv string `xml:"xmlns:v,attr"`
XMLNSo string `xml:"xmlns:o,attr"` XMLNSo string `xml:"xmlns:o,attr"`
XMLNSx string `xml:"xmlns:x,attr"` XMLNSx string `xml:"xmlns:x,attr"`
XMLNSmv string `xml:"xmlns:mv,attr"` XMLNSmv string `xml:"xmlns:mv,attr,omitempty"`
ShapeLayout *xlsxShapeLayout `xml:"o:shapelayout"` ShapeLayout *xlsxShapeLayout `xml:"o:shapelayout"`
ShapeType *xlsxShapeType `xml:"v:shapetype"` ShapeType *xlsxShapeType `xml:"v:shapetype"`
Shape []xlsxShape `xml:"v:shape"` Shape []xlsxShape `xml:"v:shape"`
@ -44,6 +44,7 @@ type xlsxIDmap struct {
type xlsxShape struct { type xlsxShape struct {
XMLName xml.Name `xml:"v:shape"` XMLName xml.Name `xml:"v:shape"`
ID string `xml:"id,attr"` ID string `xml:"id,attr"`
SpID string `xml:"o:spid,attr,omitempty"`
Type string `xml:"type,attr"` Type string `xml:"type,attr"`
Style string `xml:"style,attr"` Style string `xml:"style,attr"`
Button string `xml:"o:button,attr,omitempty"` Button string `xml:"o:button,attr,omitempty"`
@ -60,9 +61,14 @@ type xlsxShapeType struct {
ID string `xml:"id,attr"` ID string `xml:"id,attr"`
CoordSize string `xml:"coordsize,attr"` CoordSize string `xml:"coordsize,attr"`
Spt int `xml:"o:spt,attr"` Spt int `xml:"o:spt,attr"`
PreferRelative string `xml:"o:preferrelative,attr,omitempty"`
Path string `xml:"path,attr"` Path string `xml:"path,attr"`
Filled string `xml:"filled,attr,omitempty"`
Stroked string `xml:"stroked,attr,omitempty"`
Stroke *xlsxStroke `xml:"v:stroke"` Stroke *xlsxStroke `xml:"v:stroke"`
VFormulas *vFormulas `xml:"v:formulas"`
VPath *vPath `xml:"v:path"` VPath *vPath `xml:"v:path"`
Lock *oLock `xml:"o:lock"`
} }
// xlsxStroke directly maps the stroke element. // xlsxStroke directly maps the stroke element.
@ -72,10 +78,28 @@ type xlsxStroke struct {
// vPath directly maps the v:path element. // vPath directly maps the v:path element.
type vPath struct { type vPath struct {
ExtrusionOK string `xml:"o:extrusionok,attr,omitempty"`
GradientShapeOK string `xml:"gradientshapeok,attr,omitempty"` GradientShapeOK string `xml:"gradientshapeok,attr,omitempty"`
ConnectType string `xml:"o:connecttype,attr"` ConnectType string `xml:"o:connecttype,attr"`
} }
// oLock directly maps the o:lock element.
type oLock struct {
Ext string `xml:"v:ext,attr"`
Rotation string `xml:"rotation,attr,omitempty"`
AspectRatio string `xml:"aspectratio,attr,omitempty"`
}
// vFormulas directly maps to the v:formulas element
type vFormulas struct {
Formulas []vFormula `xml:"v:f"`
}
// vFormula directly maps to the v:f element
type vFormula struct {
Equation string `xml:"eqn,attr"`
}
// vFill directly maps the v:fill element. This element must be defined within a // vFill directly maps the v:fill element. This element must be defined within a
// Shape element. // Shape element.
type vFill struct { type vFill struct {
@ -106,6 +130,13 @@ type vTextBox struct {
Div *xlsxDiv `xml:"div"` Div *xlsxDiv `xml:"div"`
} }
// vImageData directly maps the v:imagedata element. This element must be
// defined within a Shape element.
type vImageData struct {
RelID string `xml:"o:relid,attr"`
Title string `xml:"o:title,attr,omitempty"`
}
// xlsxDiv directly maps the div element. // xlsxDiv directly maps the div element.
type xlsxDiv struct { type xlsxDiv struct {
Style string `xml:"style,attr"` Style string `xml:"style,attr"`
@ -165,12 +196,16 @@ type decodeShapeType struct {
ID string `xml:"id,attr"` ID string `xml:"id,attr"`
CoordSize string `xml:"coordsize,attr"` CoordSize string `xml:"coordsize,attr"`
Spt int `xml:"spt,attr"` Spt int `xml:"spt,attr"`
PreferRelative string `xml:"preferrelative,attr,omitempty"`
Path string `xml:"path,attr"` Path string `xml:"path,attr"`
Filled string `xml:"filled,attr,omitempty"`
Stroked string `xml:"stroked,attr,omitempty"`
} }
// decodeShape defines the structure used to parse the particular shape element. // decodeShape defines the structure used to parse the particular shape element.
type decodeShape struct { type decodeShape struct {
ID string `xml:"id,attr"` ID string `xml:"id,attr"`
SpID string `xml:"spid,attr,omitempty"`
Type string `xml:"type,attr"` Type string `xml:"type,attr"`
Style string `xml:"style,attr"` Style string `xml:"style,attr"`
Button string `xml:"button,attr,omitempty"` Button string `xml:"button,attr,omitempty"`
@ -254,7 +289,9 @@ type encodeShape struct {
Shadow *vShadow `xml:"v:shadow"` Shadow *vShadow `xml:"v:shadow"`
Path *vPath `xml:"v:path"` Path *vPath `xml:"v:path"`
TextBox *vTextBox `xml:"v:textbox"` TextBox *vTextBox `xml:"v:textbox"`
ImageData *vImageData `xml:"v:imagedata"`
ClientData *xClientData `xml:"x:ClientData"` ClientData *xClientData `xml:"x:ClientData"`
Lock *oLock `xml:"o:lock"`
} }
// formCtrlPreset defines the structure used to form control presets. // formCtrlPreset defines the structure used to form control presets.
@ -301,3 +338,15 @@ type FormControl struct {
Type FormControlType Type FormControlType
Format GraphicOptions Format GraphicOptions
} }
// HeaderFooterImageOptions defines the settings for an image to be accessible
// from the worksheet header and footer options.
type HeaderFooterImageOptions struct {
Position HeaderFooterImagePositionType
File []byte
IsFooter bool
FirstPage bool
Extension string
Width string
Height string
}

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -412,3 +412,100 @@ func TestExtractFormControl(t *testing.T) {
_, err := extractFormControl(string(MacintoshCyrillicCharset)) _, err := extractFormControl(string(MacintoshCyrillicCharset))
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8") assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
} }
func TestAddHeaderFooterImage(t *testing.T) {
f, sheet, wb := NewFile(), "Sheet1", filepath.Join("test", "TestAddHeaderFooterImage.xlsx")
headerFooterOptions := HeaderFooterOptions{
DifferentFirst: true,
OddHeader: "&L&GExcelize&C&G&R&G",
OddFooter: "&L&GExcelize&C&G&R&G",
FirstHeader: "&L&GExcelize&C&G&R&G",
FirstFooter: "&L&GExcelize&C&G&R&G",
}
assert.NoError(t, f.SetHeaderFooter(sheet, &headerFooterOptions))
assert.NoError(t, f.SetSheetView(sheet, -1, &ViewOptions{View: stringPtr("pageLayout")}))
images := map[string][]byte{
".wmf": nil, ".tif": nil, ".png": nil,
".jpg": nil, ".gif": nil, ".emz": nil, ".emf": nil,
}
for ext := range images {
img, err := os.ReadFile(filepath.Join("test", "images", "excel"+ext))
assert.NoError(t, err)
images[ext] = img
}
for _, opt := range []struct {
position HeaderFooterImagePositionType
file []byte
isFooter bool
firstPage bool
ext string
}{
{position: HeaderFooterImagePositionLeft, file: images[".tif"], firstPage: true, ext: ".tif"},
{position: HeaderFooterImagePositionCenter, file: images[".gif"], firstPage: true, ext: ".gif"},
{position: HeaderFooterImagePositionRight, file: images[".png"], firstPage: true, ext: ".png"},
{position: HeaderFooterImagePositionLeft, file: images[".emf"], isFooter: true, firstPage: true, ext: ".emf"},
{position: HeaderFooterImagePositionCenter, file: images[".wmf"], isFooter: true, firstPage: true, ext: ".wmf"},
{position: HeaderFooterImagePositionRight, file: images[".emz"], isFooter: true, firstPage: true, ext: ".emz"},
{position: HeaderFooterImagePositionLeft, file: images[".png"], ext: ".png"},
{position: HeaderFooterImagePositionCenter, file: images[".png"], ext: ".png"},
{position: HeaderFooterImagePositionRight, file: images[".png"], ext: ".png"},
{position: HeaderFooterImagePositionLeft, file: images[".tif"], isFooter: true, ext: ".tif"},
{position: HeaderFooterImagePositionCenter, file: images[".tif"], isFooter: true, ext: ".tif"},
{position: HeaderFooterImagePositionRight, file: images[".tif"], isFooter: true, ext: ".tif"},
} {
assert.NoError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
Position: opt.position,
File: opt.file,
IsFooter: opt.isFooter,
FirstPage: opt.firstPage,
Extension: opt.ext,
Width: "50pt",
Height: "32pt",
}))
}
assert.NoError(t, f.SetCellValue(sheet, "A1", "Example"))
// Test add header footer image with not exist sheet
assert.EqualError(t, f.AddHeaderFooterImage("SheetN", nil), "sheet SheetN does not exist")
// Test add header footer image with unsupported file type
assert.Equal(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
Extension: "jpg",
}), ErrImgExt)
assert.NoError(t, f.SaveAs(wb))
assert.NoError(t, f.Close())
// Test change already exist header image with the different image
f, err := OpenFile(wb)
assert.NoError(t, err)
assert.NoError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
File: images[".jpg"],
FirstPage: true,
Extension: ".jpg",
Width: "50pt",
Height: "32pt",
}))
assert.NoError(t, f.Save())
assert.NoError(t, f.Close())
// Test add header image with unsupported charset VML drawing
f, err = OpenFile(wb)
assert.NoError(t, err)
f.Pkg.Store("xl/drawings/vmlDrawing1.vml", MacintoshCyrillicCharset)
assert.EqualError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
File: images[".jpg"],
Extension: ".jpg",
Width: "50pt",
Height: "32pt",
}), "XML syntax error on line 1: invalid UTF-8")
assert.NoError(t, f.Close())
// Test set legacy drawing header/footer with unsupported charset content types
f = NewFile()
f.ContentTypes = nil
f.Pkg.Store(defaultXMLPathContentTypes, MacintoshCyrillicCharset)
assert.EqualError(t, f.AddHeaderFooterImage(sheet, &HeaderFooterImageOptions{
Extension: ".png",
File: images[".png"],
Width: "50pt",
Height: "32pt",
}), "XML syntax error on line 1: invalid UTF-8")
assert.NoError(t, f.Close())
}

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -336,6 +336,7 @@ type cCharts struct {
SplitPos *attrValInt `xml:"splitPos"` SplitPos *attrValInt `xml:"splitPos"`
SerLines *attrValString `xml:"serLines"` SerLines *attrValString `xml:"serLines"`
DLbls *cDLbls `xml:"dLbls"` DLbls *cDLbls `xml:"dLbls"`
GapWidth *attrValInt `xml:"gapWidth"`
Shape *attrValString `xml:"shape"` Shape *attrValString `xml:"shape"`
HoleSize *attrValInt `xml:"holeSize"` HoleSize *attrValInt `xml:"holeSize"`
Smooth *attrValBool `xml:"smooth"` Smooth *attrValBool `xml:"smooth"`
@ -483,6 +484,8 @@ type cNumCache struct {
// the specific formatting and positioning settings. // the specific formatting and positioning settings.
type cDLbls struct { type cDLbls struct {
NumFmt *cNumFmt `xml:"numFmt"` NumFmt *cNumFmt `xml:"numFmt"`
SpPr *cSpPr `xml:"spPr"`
TxPr *cTxPr `xml:"txPr"`
DLblPos *attrValString `xml:"dLblPos"` DLblPos *attrValString `xml:"dLblPos"`
ShowLegendKey *attrValBool `xml:"showLegendKey"` ShowLegendKey *attrValBool `xml:"showLegendKey"`
ShowVal *attrValBool `xml:"showVal"` ShowVal *attrValBool `xml:"showVal"`
@ -540,6 +543,7 @@ type ChartAxis struct {
Secondary bool Secondary bool
Maximum *float64 Maximum *float64
Minimum *float64 Minimum *float64
Alignment Alignment
Font Font Font Font
LogBase float64 LogBase float64
NumFmt ChartNumFmt NumFmt ChartNumFmt
@ -583,6 +587,8 @@ type Chart struct {
ShowBlanksAs string ShowBlanksAs string
BubbleSize int BubbleSize int
HoleSize int HoleSize int
GapWidth *uint
Overlap *int
order int order int
} }
@ -606,6 +612,13 @@ type ChartLine struct {
Width float64 Width float64
} }
// ChartDataLabel directly maps the format settings of the chart labels.
type ChartDataLabel struct {
Alignment Alignment
Font Font
Fill Fill
}
// ChartSeries directly maps the format settings of the chart series. // ChartSeries directly maps the format settings of the chart series.
type ChartSeries struct { type ChartSeries struct {
Name string Name string
@ -615,5 +628,6 @@ type ChartSeries struct {
Fill Fill Fill Fill
Line ChartLine Line ChartLine
Marker ChartMarker Marker ChartMarker
DataLabel ChartDataLabel
DataLabelPosition ChartDataLabelPositionType DataLabelPosition ChartDataLabelPositionType
} }

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -9,7 +9,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -162,11 +162,13 @@ type xlsxBorder struct {
DiagonalDown bool `xml:"diagonalDown,attr,omitempty"` DiagonalDown bool `xml:"diagonalDown,attr,omitempty"`
DiagonalUp bool `xml:"diagonalUp,attr,omitempty"` DiagonalUp bool `xml:"diagonalUp,attr,omitempty"`
Outline bool `xml:"outline,attr,omitempty"` Outline bool `xml:"outline,attr,omitempty"`
Left xlsxLine `xml:"left,omitempty"` Left *xlsxLine `xml:"left"`
Right xlsxLine `xml:"right,omitempty"` Right *xlsxLine `xml:"right"`
Top xlsxLine `xml:"top,omitempty"` Top *xlsxLine `xml:"top"`
Bottom xlsxLine `xml:"bottom,omitempty"` Bottom *xlsxLine `xml:"bottom"`
Diagonal xlsxLine `xml:"diagonal,omitempty"` Diagonal *xlsxLine `xml:"diagonal"`
Vertical *xlsxLine `xml:"vertical"`
Horizontal *xlsxLine `xml:"horizontal"`
} }
// xlsxCellStyles directly maps the cellStyles element. This element contains // xlsxCellStyles directly maps the cellStyles element. This element contains

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize

View File

@ -1,4 +1,4 @@
// Copyright 2016 - 2024 The excelize Authors. All rights reserved. Use of // Copyright 2016 - 2025 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in // this source code is governed by a BSD-style license that can be found in
// the LICENSE file. // the LICENSE file.
// //
@ -7,7 +7,7 @@
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming // Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of // API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.18 or later. // data. This library needs Go version 1.20 or later.
package excelize package excelize
@ -48,7 +48,7 @@ type xlsxWorksheet struct {
ColBreaks *xlsxColBreaks `xml:"colBreaks"` ColBreaks *xlsxColBreaks `xml:"colBreaks"`
CustomProperties *xlsxInnerXML `xml:"customProperties"` CustomProperties *xlsxInnerXML `xml:"customProperties"`
CellWatches *xlsxInnerXML `xml:"cellWatches"` CellWatches *xlsxInnerXML `xml:"cellWatches"`
IgnoredErrors *xlsxInnerXML `xml:"ignoredErrors"` IgnoredErrors *xlsxIgnoredErrors `xml:"ignoredErrors"`
SmartTags *xlsxInnerXML `xml:"smartTags"` SmartTags *xlsxInnerXML `xml:"smartTags"`
Drawing *xlsxDrawing `xml:"drawing"` Drawing *xlsxDrawing `xml:"drawing"`
LegacyDrawing *xlsxLegacyDrawing `xml:"legacyDrawing"` LegacyDrawing *xlsxLegacyDrawing `xml:"legacyDrawing"`
@ -679,6 +679,28 @@ type xlsxPicture struct {
RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"` RID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr,omitempty"`
} }
// xlsxIgnoredError specifies a single ignored error for a range of cells.
type xlsxIgnoredError struct {
XMLName xml.Name `xml:"ignoredError"`
Sqref string `xml:"sqref,attr"`
EvalError bool `xml:"evalError,attr,omitempty"`
TwoDigitTextYear bool `xml:"twoDigitTextYear,attr,omitempty"`
NumberStoredAsText bool `xml:"numberStoredAsText,attr,omitempty"`
Formula bool `xml:"formula,attr,omitempty"`
FormulaRange bool `xml:"formulaRange,attr,omitempty"`
UnlockedFormula bool `xml:"unlockedFormula,attr,omitempty"`
EmptyCellReference bool `xml:"emptyCellReference,attr,omitempty"`
ListDataValidation bool `xml:"listDataValidation,attr,omitempty"`
CalculatedColumn bool `xml:"calculatedColumn,attr,omitempty"`
}
// xlsxIgnoredErrors specifies a collection of ignored errors, by cell range.
type xlsxIgnoredErrors struct {
XMLName xml.Name `xml:"ignoredErrors"`
IgnoredError []xlsxIgnoredError `xml:"ignoredError"`
ExtLst *xlsxExtLst `xml:"extLst"`
}
// xlsxLegacyDrawing directly maps the legacyDrawing element in the namespace // xlsxLegacyDrawing directly maps the legacyDrawing element in the namespace
// http://schemas.openxmlformats.org/spreadsheetml/2006/main - A comment is a // http://schemas.openxmlformats.org/spreadsheetml/2006/main - A comment is a
// rich text note that is attached to, and associated with, a cell, separate // rich text note that is attached to, and associated with, a cell, separate
@ -1006,6 +1028,9 @@ type PageLayoutOptions struct {
FitToWidth *int FitToWidth *int
// BlackAndWhite specified print black and white. // BlackAndWhite specified print black and white.
BlackAndWhite *bool BlackAndWhite *bool
// PageOrder specifies the ordering of multiple pages. Values
// accepted: overThenDown and downThenOver
PageOrder *string
} }
// ViewOptions directly maps the settings of sheet view. // ViewOptions directly maps the settings of sheet view.