summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2019-05-05 16:25:57 +0800
committerxuri <xuri.me@gmail.com>2019-05-05 16:25:57 +0800
commit69b38ddcd60f7cf4c158c706ddbbeb89a8ff2108 (patch)
treeae7fdf36fda004ab48b3f2e82120bcfc51824515
parent72701e89c7145f9d08a79c93040e232b2875c855 (diff)
Resolve #394, init set header and footer support
-rw-r--r--datavalidation_test.go30
-rw-r--r--excelize_test.go8
-rw-r--r--sheet.go155
-rw-r--r--sheet_test.go57
4 files changed, 213 insertions, 37 deletions
diff --git a/datavalidation_test.go b/datavalidation_test.go
index afb659c..0fee092 100644
--- a/datavalidation_test.go
+++ b/datavalidation_test.go
@@ -19,7 +19,7 @@ import (
func TestDataValidation(t *testing.T) {
resultFile := filepath.Join("test", "TestDataValidation.xlsx")
- xlsx := NewFile()
+ f := NewFile()
dvRange := NewDataValidation(true)
dvRange.Sqref = "A1:B2"
@@ -27,8 +27,8 @@ func TestDataValidation(t *testing.T) {
dvRange.SetError(DataValidationErrorStyleStop, "error title", "error body")
dvRange.SetError(DataValidationErrorStyleWarning, "error title", "error body")
dvRange.SetError(DataValidationErrorStyleInformation, "error title", "error body")
- xlsx.AddDataValidation("Sheet1", dvRange)
- if !assert.NoError(t, xlsx.SaveAs(resultFile)) {
+ f.AddDataValidation("Sheet1", dvRange)
+ if !assert.NoError(t, f.SaveAs(resultFile)) {
t.FailNow()
}
@@ -36,16 +36,16 @@ func TestDataValidation(t *testing.T) {
dvRange.Sqref = "A3:B4"
dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
dvRange.SetInput("input title", "input body")
- xlsx.AddDataValidation("Sheet1", dvRange)
- if !assert.NoError(t, xlsx.SaveAs(resultFile)) {
+ f.AddDataValidation("Sheet1", dvRange)
+ if !assert.NoError(t, f.SaveAs(resultFile)) {
t.FailNow()
}
dvRange = NewDataValidation(true)
dvRange.Sqref = "A5:B6"
dvRange.SetDropList([]string{"1", "2", "3"})
- xlsx.AddDataValidation("Sheet1", dvRange)
- if !assert.NoError(t, xlsx.SaveAs(resultFile)) {
+ f.AddDataValidation("Sheet1", dvRange)
+ if !assert.NoError(t, f.SaveAs(resultFile)) {
t.FailNow()
}
}
@@ -53,10 +53,10 @@ func TestDataValidation(t *testing.T) {
func TestDataValidationError(t *testing.T) {
resultFile := filepath.Join("test", "TestDataValidationError.xlsx")
- xlsx := NewFile()
- xlsx.SetCellStr("Sheet1", "E1", "E1")
- xlsx.SetCellStr("Sheet1", "E2", "E2")
- xlsx.SetCellStr("Sheet1", "E3", "E3")
+ f := NewFile()
+ f.SetCellStr("Sheet1", "E1", "E1")
+ f.SetCellStr("Sheet1", "E2", "E2")
+ f.SetCellStr("Sheet1", "E3", "E3")
dvRange := NewDataValidation(true)
dvRange.SetSqref("A7:B8")
@@ -66,8 +66,8 @@ func TestDataValidationError(t *testing.T) {
err := dvRange.SetSqrefDropList("$E$1:$E$3", false)
assert.EqualError(t, err, "cross-sheet sqref cell are not supported")
- xlsx.AddDataValidation("Sheet1", dvRange)
- if !assert.NoError(t, xlsx.SaveAs(resultFile)) {
+ f.AddDataValidation("Sheet1", dvRange)
+ if !assert.NoError(t, f.SaveAs(resultFile)) {
t.FailNow()
}
@@ -81,8 +81,8 @@ func TestDataValidationError(t *testing.T) {
dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
dvRange.SetSqref("A9:B10")
- xlsx.AddDataValidation("Sheet1", dvRange)
- if !assert.NoError(t, xlsx.SaveAs(resultFile)) {
+ f.AddDataValidation("Sheet1", dvRange)
+ if !assert.NoError(t, f.SaveAs(resultFile)) {
t.FailNow()
}
}
diff --git a/excelize_test.go b/excelize_test.go
index c76aa92..3509cb8 100644
--- a/excelize_test.go
+++ b/excelize_test.go
@@ -789,9 +789,9 @@ func TestCopySheet(t *testing.T) {
}
idx := f.NewSheet("CopySheet")
- assert.EqualError(t, f.CopySheet(1, idx), "sheet sheet1 is not exist")
+ assert.NoError(t, f.CopySheet(1, idx))
- f.SetCellValue("Sheet4", "F1", "Hello")
+ f.SetCellValue("CopySheet", "F1", "Hello")
val, err := f.GetCellValue("Sheet1", "F1")
assert.NoError(t, err)
assert.NotEqual(t, "Hello", val)
@@ -805,8 +805,8 @@ func TestCopySheetError(t *testing.T) {
t.FailNow()
}
- err = f.CopySheet(0, -1)
- if !assert.EqualError(t, err, "invalid worksheet index") {
+ assert.EqualError(t, f.copySheet(0, -1), "sheet is not exist")
+ if !assert.EqualError(t, f.CopySheet(0, -1), "invalid worksheet index") {
t.FailNow()
}
diff --git a/sheet.go b/sheet.go
index 5e9fcae..b22592d 100644
--- a/sheet.go
+++ b/sheet.go
@@ -14,9 +14,11 @@ import (
"encoding/json"
"encoding/xml"
"errors"
+ "fmt"
"io/ioutil"
"os"
"path"
+ "reflect"
"regexp"
"strconv"
"strings"
@@ -466,7 +468,7 @@ func (f *File) CopySheet(from, to int) error {
// copySheet provides a function to duplicate a worksheet by gave source and
// target worksheet name.
func (f *File) copySheet(from, to int) error {
- sheet, err := f.workSheetReader("sheet" + strconv.Itoa(from))
+ sheet, err := f.workSheetReader(f.GetSheetName(from))
if err != nil {
return err
}
@@ -761,6 +763,155 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {
return result, nil
}
+// SetHeaderFooter provides a function to set headers and footers by given
+// worksheet name and the control characters.
+//
+// Headers and footers are specified using the following settings fields:
+//
+// Fields | Description
+// ------------------+-----------------------------------------------------------
+// AlignWithMargins | Align header footer margins with page margins
+// DifferentFirst | Different first-page header and footer indicator
+// DifferentOddEven | Different odd and even page headers and footers indicator
+// ScaleWithDoc | Scale header and footer with document scaling
+// OddFooter | Odd Page Footer
+// OddHeader | Odd Header
+// EvenFooter | Even Page Footer
+// EvenHeader | Even Page Header
+// FirstFooter | First Page Footer
+// FirstHeader | First Page Header
+//
+// The following formatting codes can be used in 6 string type fields:
+// OddHeader, OddFooter, EvenHeader, EvenFooter, FirstFooter, FirstHeader
+//
+// Formatting Code | Description
+// ------------------------+-------------------------------------------------------------------------
+// && | The character "&"
+// |
+// &font-size | Size of the text font, where font-size is a decimal font size in points
+// |
+// &"font name,font type" | A text font-name string, font name, and a text font-type string,
+// | font type
+// |
+// &"-,Regular" | Regular text format. Toggles bold and italic modes to off
+// |
+// &A | Current worksheet's tab name
+// |
+// &B or &"-,Bold" | Bold text format, from off to on, or vice versa. The default mode is off
+// |
+// &D | Current date
+// |
+// &C | Center section
+// |
+// &E | Double-underline text format
+// |
+// &F | Current workbook's file name
+// |
+// &G | Drawing object as background
+// |
+// &H | Shadow text format
+// |
+// &I or &"-,Italic" | Italic text format
+// |
+// &K | Text font color
+// |
+// | An RGB Color is specified as RRGGBB
+// |
+// | A Theme Color is specified as TTSNNN where TT is the theme color Id,
+// | S is either "+" or "-" of the tint/shade value, and NNN is the
+// | tint/shade value
+// |
+// &L | Left section
+// |
+// &N | Total number of pages
+// |
+// &O | Outline text format
+// |
+// &P[[+|-]n] | Without the optional suffix, the current page number in decimal
+// |
+// &R | Right section
+// |
+// &S | Strikethrough text format
+// |
+// &T | Current time
+// |
+// &U | Single-underline text format. If double-underline mode is on, the next
+// | occurrence in a section specifier toggles double-underline mode to off;
+// | otherwise, it toggles single-underline mode, from off to on, or vice
+// | versa. The default mode is off
+// |
+// &X | Superscript text format
+// |
+// &Y | Subscript text format
+// |
+// &Z | Current workbook's file path
+//
+// For example:
+//
+// err := f.SetHeaderFooter("Sheet1", &excelize.FormatHeaderFooter{
+// DifferentFirst: true,
+// DifferentOddEven: true,
+// OddHeader: "&R&P",
+// OddFooter: "&C&F",
+// EvenHeader: "&L&P",
+// EvenFooter: "&L&D&R&T",
+// FirstHeader: `&CCenter &"-,Bold"Bold&"-,Regular"HeaderU+000A&D`,
+// })
+//
+// This example shows:
+//
+// - The first page has its own header and footer
+//
+// - Odd and even-numbered pages have different headers and footers
+//
+// - Current page number in the right section of odd-page headers
+//
+// - Current workbook's file name in the center section of odd-page footers
+//
+// - Current page number in the left section of even-page headers
+//
+// - Current date in the left section and the current time in the right section
+// of even-page footers
+//
+// - The text "Center Bold Header" on the first line of the center section of
+// the first page, and the date on the second line of the center section of
+// that same page
+//
+// - No footer on the first page
+//
+func (f *File) SetHeaderFooter(sheet string, settings *FormatHeaderFooter) error {
+ xlsx, err := f.workSheetReader(sheet)
+ if err != nil {
+ return err
+ }
+ if settings == nil {
+ xlsx.HeaderFooter = nil
+ return err
+ }
+
+ v := reflect.ValueOf(*settings)
+ // Check 6 string type fields: OddHeader, OddFooter, EvenHeader, EvenFooter,
+ // FirstFooter, FirstHeader
+ for i := 4; i < v.NumField()-1; i++ {
+ if v.Field(i).Len() >= 255 {
+ return fmt.Errorf("field %s must be less than 255 characters", v.Type().Field(i).Name)
+ }
+ }
+ xlsx.HeaderFooter = &xlsxHeaderFooter{
+ AlignWithMargins: settings.AlignWithMargins,
+ DifferentFirst: settings.DifferentFirst,
+ DifferentOddEven: settings.DifferentOddEven,
+ ScaleWithDoc: settings.ScaleWithDoc,
+ OddHeader: settings.OddHeader,
+ OddFooter: settings.OddFooter,
+ EvenHeader: settings.EvenHeader,
+ EvenFooter: settings.EvenFooter,
+ FirstFooter: settings.FirstFooter,
+ FirstHeader: settings.FirstHeader,
+ }
+ return err
+}
+
// ProtectSheet provides a function to prevent other users from accidentally
// or deliberately changing, moving, or deleting data in a worksheet. For
// example, protect Sheet1 with protection settings:
@@ -898,7 +1049,7 @@ func (p *PageLayoutPaperSize) getPageLayout(ps *xlsxPageSetUp) {
//
// Available options:
// PageLayoutOrientation(string)
-// PageLayoutPaperSize(int)
+// PageLayoutPaperSize(int)
//
// The following shows the paper size sorted by excelize index number:
//
diff --git a/sheet_test.go b/sheet_test.go
index f0a1963..beee10b 100644
--- a/sheet_test.go
+++ b/sheet_test.go
@@ -2,6 +2,8 @@ package excelize_test
import (
"fmt"
+ "path/filepath"
+ "strings"
"testing"
"github.com/360EntSecGroup-Skylar/excelize/v2"
@@ -10,15 +12,15 @@ import (
)
func ExampleFile_SetPageLayout() {
- xl := excelize.NewFile()
+ f := excelize.NewFile()
- if err := xl.SetPageLayout(
+ if err := f.SetPageLayout(
"Sheet1",
excelize.PageLayoutOrientation(excelize.OrientationLandscape),
); err != nil {
panic(err)
}
- if err := xl.SetPageLayout(
+ if err := f.SetPageLayout(
"Sheet1",
excelize.PageLayoutPaperSize(10),
); err != nil {
@@ -28,15 +30,15 @@ func ExampleFile_SetPageLayout() {
}
func ExampleFile_GetPageLayout() {
- xl := excelize.NewFile()
+ f := excelize.NewFile()
var (
orientation excelize.PageLayoutOrientation
paperSize excelize.PageLayoutPaperSize
)
- if err := xl.GetPageLayout("Sheet1", &orientation); err != nil {
+ if err := f.GetPageLayout("Sheet1", &orientation); err != nil {
panic(err)
}
- if err := xl.GetPageLayout("Sheet1", &paperSize); err != nil {
+ if err := f.GetPageLayout("Sheet1", &paperSize); err != nil {
panic(err)
}
fmt.Println("Defaults:")
@@ -69,26 +71,26 @@ func TestPageLayoutOption(t *testing.T) {
val1 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr)
val2 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr)
- xl := excelize.NewFile()
+ f := excelize.NewFile()
// Get the default value
- assert.NoError(t, xl.GetPageLayout(sheet, def), opt)
+ assert.NoError(t, f.GetPageLayout(sheet, def), opt)
// Get again and check
- assert.NoError(t, xl.GetPageLayout(sheet, val1), opt)
+ assert.NoError(t, f.GetPageLayout(sheet, val1), opt)
if !assert.Equal(t, val1, def, opt) {
t.FailNow()
}
// Set the same value
- assert.NoError(t, xl.SetPageLayout(sheet, val1), opt)
+ assert.NoError(t, f.SetPageLayout(sheet, val1), opt)
// Get again and check
- assert.NoError(t, xl.GetPageLayout(sheet, val1), opt)
+ assert.NoError(t, f.GetPageLayout(sheet, val1), opt)
if !assert.Equal(t, val1, def, "%T: value should not have changed", opt) {
t.FailNow()
}
// Set a different value
- assert.NoError(t, xl.SetPageLayout(sheet, test.nonDefault), opt)
- assert.NoError(t, xl.GetPageLayout(sheet, val1), opt)
+ assert.NoError(t, f.SetPageLayout(sheet, test.nonDefault), opt)
+ assert.NoError(t, f.GetPageLayout(sheet, val1), opt)
// Get again and compare
- assert.NoError(t, xl.GetPageLayout(sheet, val2), opt)
+ assert.NoError(t, f.GetPageLayout(sheet, val2), opt)
if !assert.Equal(t, val1, val2, "%T: value should not have changed", opt) {
t.FailNow()
}
@@ -97,8 +99,8 @@ func TestPageLayoutOption(t *testing.T) {
t.FailNow()
}
// Restore the default value
- assert.NoError(t, xl.SetPageLayout(sheet, def), opt)
- assert.NoError(t, xl.GetPageLayout(sheet, val1), opt)
+ assert.NoError(t, f.SetPageLayout(sheet, def), opt)
+ assert.NoError(t, f.GetPageLayout(sheet, val1), opt)
if !assert.Equal(t, def, val1) {
t.FailNow()
}
@@ -117,3 +119,26 @@ func TestGetPageLayout(t *testing.T) {
// Test get page layout on not exists worksheet.
assert.EqualError(t, f.GetPageLayout("SheetN"), "sheet SheetN is not exist")
}
+
+func TestSetHeaderFooter(t *testing.T) {
+ f := excelize.NewFile()
+ f.SetCellStr("Sheet1", "A1", "Test SetHeaderFooter")
+ // Test set header and footer on not exists worksheet.
+ assert.EqualError(t, f.SetHeaderFooter("SheetN", nil), "sheet SheetN is not exist")
+ // Test set header and footer with illegal setting.
+ assert.EqualError(t, f.SetHeaderFooter("Sheet1", &excelize.FormatHeaderFooter{
+ OddHeader: strings.Repeat("c", 256),
+ }), "field OddHeader must be less than 255 characters")
+
+ assert.NoError(t, f.SetHeaderFooter("Sheet1", nil))
+ assert.NoError(t, f.SetHeaderFooter("Sheet1", &excelize.FormatHeaderFooter{
+ DifferentFirst: true,
+ DifferentOddEven: true,
+ OddHeader: "&R&P",
+ OddFooter: "&C&F",
+ EvenHeader: "&L&P",
+ EvenFooter: "&L&D&R&T",
+ FirstHeader: `&CCenter &"-,Bold"Bold&"-,Regular"HeaderU+000A&D`,
+ }))
+ assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetHeaderFooter.xlsx")))
+}