소스 검색

Resolve #394, init set header and footer support

xuri 6 년 전
부모
커밋
69b38ddcd6
4개의 변경된 파일213개의 추가작업 그리고 37개의 파일을 삭제
  1. 15 15
      datavalidation_test.go
  2. 4 4
      excelize_test.go
  3. 153 2
      sheet.go
  4. 41 16
      sheet_test.go

+ 15 - 15
datavalidation_test.go

@@ -19,7 +19,7 @@ import (
 func TestDataValidation(t *testing.T) {
 func TestDataValidation(t *testing.T) {
 	resultFile := filepath.Join("test", "TestDataValidation.xlsx")
 	resultFile := filepath.Join("test", "TestDataValidation.xlsx")
 
 
-	xlsx := NewFile()
+	f := NewFile()
 
 
 	dvRange := NewDataValidation(true)
 	dvRange := NewDataValidation(true)
 	dvRange.Sqref = "A1:B2"
 	dvRange.Sqref = "A1:B2"
@@ -27,8 +27,8 @@ func TestDataValidation(t *testing.T) {
 	dvRange.SetError(DataValidationErrorStyleStop, "error title", "error body")
 	dvRange.SetError(DataValidationErrorStyleStop, "error title", "error body")
 	dvRange.SetError(DataValidationErrorStyleWarning, "error title", "error body")
 	dvRange.SetError(DataValidationErrorStyleWarning, "error title", "error body")
 	dvRange.SetError(DataValidationErrorStyleInformation, "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()
 		t.FailNow()
 	}
 	}
 
 
@@ -36,16 +36,16 @@ func TestDataValidation(t *testing.T) {
 	dvRange.Sqref = "A3:B4"
 	dvRange.Sqref = "A3:B4"
 	dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
 	dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
 	dvRange.SetInput("input title", "input body")
 	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()
 		t.FailNow()
 	}
 	}
 
 
 	dvRange = NewDataValidation(true)
 	dvRange = NewDataValidation(true)
 	dvRange.Sqref = "A5:B6"
 	dvRange.Sqref = "A5:B6"
 	dvRange.SetDropList([]string{"1", "2", "3"})
 	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()
 		t.FailNow()
 	}
 	}
 }
 }
@@ -53,10 +53,10 @@ func TestDataValidation(t *testing.T) {
 func TestDataValidationError(t *testing.T) {
 func TestDataValidationError(t *testing.T) {
 	resultFile := filepath.Join("test", "TestDataValidationError.xlsx")
 	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 := NewDataValidation(true)
 	dvRange.SetSqref("A7:B8")
 	dvRange.SetSqref("A7:B8")
@@ -66,8 +66,8 @@ func TestDataValidationError(t *testing.T) {
 	err := dvRange.SetSqrefDropList("$E$1:$E$3", false)
 	err := dvRange.SetSqrefDropList("$E$1:$E$3", false)
 	assert.EqualError(t, err, "cross-sheet sqref cell are not supported")
 	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()
 		t.FailNow()
 	}
 	}
 
 
@@ -81,8 +81,8 @@ func TestDataValidationError(t *testing.T) {
 	dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
 	dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan)
 	dvRange.SetSqref("A9:B10")
 	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()
 		t.FailNow()
 	}
 	}
 }
 }

+ 4 - 4
excelize_test.go

@@ -789,9 +789,9 @@ func TestCopySheet(t *testing.T) {
 	}
 	}
 
 
 	idx := f.NewSheet("CopySheet")
 	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")
 	val, err := f.GetCellValue("Sheet1", "F1")
 	assert.NoError(t, err)
 	assert.NoError(t, err)
 	assert.NotEqual(t, "Hello", val)
 	assert.NotEqual(t, "Hello", val)
@@ -805,8 +805,8 @@ func TestCopySheetError(t *testing.T) {
 		t.FailNow()
 		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()
 		t.FailNow()
 	}
 	}
 
 

+ 153 - 2
sheet.go

@@ -14,9 +14,11 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"encoding/xml"
 	"encoding/xml"
 	"errors"
 	"errors"
+	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
 	"path"
 	"path"
+	"reflect"
 	"regexp"
 	"regexp"
 	"strconv"
 	"strconv"
 	"strings"
 	"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
 // copySheet provides a function to duplicate a worksheet by gave source and
 // target worksheet name.
 // target worksheet name.
 func (f *File) copySheet(from, to int) error {
 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 {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -761,6 +763,155 @@ func (f *File) SearchSheet(sheet, value string, reg ...bool) ([]string, error) {
 	return result, nil
 	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
 // ProtectSheet provides a function to prevent other users from accidentally
 // or deliberately changing, moving, or deleting data in a worksheet. For
 // or deliberately changing, moving, or deleting data in a worksheet. For
 // example, protect Sheet1 with protection settings:
 // example, protect Sheet1 with protection settings:
@@ -898,7 +1049,7 @@ func (p *PageLayoutPaperSize) getPageLayout(ps *xlsxPageSetUp) {
 //
 //
 // Available options:
 // Available options:
 //   PageLayoutOrientation(string)
 //   PageLayoutOrientation(string)
-// 	 PageLayoutPaperSize(int)
+//   PageLayoutPaperSize(int)
 //
 //
 // The following shows the paper size sorted by excelize index number:
 // The following shows the paper size sorted by excelize index number:
 //
 //

+ 41 - 16
sheet_test.go

@@ -2,6 +2,8 @@ package excelize_test
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"path/filepath"
+	"strings"
 	"testing"
 	"testing"
 
 
 	"github.com/360EntSecGroup-Skylar/excelize/v2"
 	"github.com/360EntSecGroup-Skylar/excelize/v2"
@@ -10,15 +12,15 @@ import (
 )
 )
 
 
 func ExampleFile_SetPageLayout() {
 func ExampleFile_SetPageLayout() {
-	xl := excelize.NewFile()
+	f := excelize.NewFile()
 
 
-	if err := xl.SetPageLayout(
+	if err := f.SetPageLayout(
 		"Sheet1",
 		"Sheet1",
 		excelize.PageLayoutOrientation(excelize.OrientationLandscape),
 		excelize.PageLayoutOrientation(excelize.OrientationLandscape),
 	); err != nil {
 	); err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
-	if err := xl.SetPageLayout(
+	if err := f.SetPageLayout(
 		"Sheet1",
 		"Sheet1",
 		excelize.PageLayoutPaperSize(10),
 		excelize.PageLayoutPaperSize(10),
 	); err != nil {
 	); err != nil {
@@ -28,15 +30,15 @@ func ExampleFile_SetPageLayout() {
 }
 }
 
 
 func ExampleFile_GetPageLayout() {
 func ExampleFile_GetPageLayout() {
-	xl := excelize.NewFile()
+	f := excelize.NewFile()
 	var (
 	var (
 		orientation excelize.PageLayoutOrientation
 		orientation excelize.PageLayoutOrientation
 		paperSize   excelize.PageLayoutPaperSize
 		paperSize   excelize.PageLayoutPaperSize
 	)
 	)
-	if err := xl.GetPageLayout("Sheet1", &orientation); err != nil {
+	if err := f.GetPageLayout("Sheet1", &orientation); err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
-	if err := xl.GetPageLayout("Sheet1", &paperSize); err != nil {
+	if err := f.GetPageLayout("Sheet1", &paperSize); err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
 	fmt.Println("Defaults:")
 	fmt.Println("Defaults:")
@@ -69,26 +71,26 @@ func TestPageLayoutOption(t *testing.T) {
 			val1 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr)
 			val1 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr)
 			val2 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr)
 			val2 := deepcopy.Copy(def).(excelize.PageLayoutOptionPtr)
 
 
-			xl := excelize.NewFile()
+			f := excelize.NewFile()
 			// Get the default value
 			// Get the default value
-			assert.NoError(t, xl.GetPageLayout(sheet, def), opt)
+			assert.NoError(t, f.GetPageLayout(sheet, def), opt)
 			// Get again and check
 			// 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) {
 			if !assert.Equal(t, val1, def, opt) {
 				t.FailNow()
 				t.FailNow()
 			}
 			}
 			// Set the same value
 			// Set the same value
-			assert.NoError(t, xl.SetPageLayout(sheet, val1), opt)
+			assert.NoError(t, f.SetPageLayout(sheet, val1), opt)
 			// Get again and check
 			// 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) {
 			if !assert.Equal(t, val1, def, "%T: value should not have changed", opt) {
 				t.FailNow()
 				t.FailNow()
 			}
 			}
 			// Set a different value
 			// 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
 			// 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) {
 			if !assert.Equal(t, val1, val2, "%T: value should not have changed", opt) {
 				t.FailNow()
 				t.FailNow()
 			}
 			}
@@ -97,8 +99,8 @@ func TestPageLayoutOption(t *testing.T) {
 				t.FailNow()
 				t.FailNow()
 			}
 			}
 			// Restore the default value
 			// 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) {
 			if !assert.Equal(t, def, val1) {
 				t.FailNow()
 				t.FailNow()
 			}
 			}
@@ -117,3 +119,26 @@ func TestGetPageLayout(t *testing.T) {
 	// Test get page layout on not exists worksheet.
 	// Test get page layout on not exists worksheet.
 	assert.EqualError(t, f.GetPageLayout("SheetN"), "sheet SheetN is not exist")
 	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")))
+}