table.go 5.2 KB
Newer Older
xurime's avatar
xurime 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
package excelize

import (
	"encoding/json"
	"encoding/xml"
	"strconv"
	"strings"
)

// parseFormatTableSet provides function to parse the format settings of the
// table with default value.
func parseFormatTableSet(formatSet string) *formatTable {
	format := formatTable{
		TableStyle:     "",
		ShowRowStripes: true,
	}
	json.Unmarshal([]byte(formatSet), &format)
	return &format
}

// AddTable provides the method to add table in a worksheet by given sheet
// index, coordinate area and format set. For example, create a table of A1:D5
// on Sheet1:
//
//    xlsx.AddTable("Sheet1", "A1", "D5", ``)
//
// Create a table of F2:H6 on Sheet2 with format set:
//
//    xlsx.AddTable("Sheet2", "F2", "H6", `{"table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
//
// Note that the table at least two lines include string type header. The two
// chart coordinate areas can not have an intersection.
//
// table_style: The built-in table style names
//
//    TableStyleLight1 - TableStyleLight21
//    TableStyleMedium1 - TableStyleMedium28
//    TableStyleDark1 - TableStyleDark11
//
func (f *File) AddTable(sheet, hcell, vcell, format string) {
	formatSet := parseFormatTableSet(format)
	hcell = strings.ToUpper(hcell)
	vcell = strings.ToUpper(vcell)
	// Coordinate conversion, convert C1:B3 to 2,0,1,2.
	hcol := string(strings.Map(letterOnlyMapF, hcell))
	hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
	hyAxis := hrow - 1
	hxAxis := titleToNumber(hcol)

	vcol := string(strings.Map(letterOnlyMapF, vcell))
	vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
	vyAxis := vrow - 1
	vxAxis := titleToNumber(vcol)
	if vxAxis < hxAxis {
		vxAxis, hxAxis = hxAxis, vxAxis
	}
	if vyAxis < hyAxis {
		vyAxis, hyAxis = hyAxis, vyAxis
	}
	tableID := f.countTables() + 1
	sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
	tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1)
	// Add first table for given sheet.
	rID := f.addSheetRelationships(sheet, SourceRelationshipTable, sheetRelationshipsTableXML, "")
	f.addSheetTable(sheet, rID)
	f.addTable(sheet, tableXML, hxAxis, hyAxis, vxAxis, vyAxis, tableID, formatSet)
	f.addTableContentTypePart(tableID)
}

// countTables provides function to get table files count storage in the folder
// xl/tables.
func (f *File) countTables() int {
	count := 0
	for k := range f.XLSX {
		if strings.Contains(k, "xl/tables/table") {
			count++
		}
	}
	return count
}

// addSheetTable provides function to add tablePart element to
// xl/worksheets/sheet%d.xml by given sheet name and relationship index.
func (f *File) addSheetTable(sheet string, rID int) {
	xlsx := f.workSheetReader(sheet)
	table := &xlsxTablePart{
		RID: "rId" + strconv.Itoa(rID),
	}
	if xlsx.TableParts != nil {
		xlsx.TableParts.Count++
		xlsx.TableParts.TableParts = append(xlsx.TableParts.TableParts, table)
	} else {
		xlsx.TableParts = &xlsxTableParts{
			Count:      1,
			TableParts: []*xlsxTablePart{table},
		}
	}

}

// addTable provides function to add table by given sheet index, coordinate area
// and format set.
func (f *File) addTable(sheet, tableXML string, hxAxis, hyAxis, vxAxis, vyAxis, i int, formatSet *formatTable) {
	// Correct the minimum number of rows, the table at least two lines.
	if hyAxis == vyAxis {
		vyAxis++
	}
	// Correct table reference coordinate area, such correct C1:B3 to B1:C3.
	ref := toAlphaString(hxAxis+1) + strconv.Itoa(hyAxis+1) + ":" + toAlphaString(vxAxis+1) + strconv.Itoa(vyAxis+1)
	tableColumn := []*xlsxTableColumn{}
	idx := 0
	for i := hxAxis; i <= vxAxis; i++ {
		idx++
		cell := toAlphaString(i+1) + strconv.Itoa(hyAxis+1)
		name := f.GetCellValue(sheet, cell)
		if _, err := strconv.Atoi(name); err == nil {
			f.SetCellStr(sheet, cell, name)
		}
		if name == "" {
			name = "Column" + strconv.Itoa(idx)
			f.SetCellStr(sheet, cell, name)
		}
		tableColumn = append(tableColumn, &xlsxTableColumn{
			ID:   idx,
			Name: name,
		})
	}
	name := "Table" + strconv.Itoa(i)
	t := xlsxTable{
		XMLNS:       NameSpaceSpreadSheet,
		ID:          i,
		Name:        name,
		DisplayName: name,
		Ref:         ref,
		AutoFilter: &xlsxAutoFilter{
			Ref: ref,
		},
		TableColumns: &xlsxTableColumns{
			Count:       idx,
			TableColumn: tableColumn,
		},
		TableStyleInfo: &xlsxTableStyleInfo{
			Name:              formatSet.TableStyle,
			ShowFirstColumn:   formatSet.ShowFirstColumn,
			ShowLastColumn:    formatSet.ShowLastColumn,
			ShowRowStripes:    formatSet.ShowRowStripes,
			ShowColumnStripes: formatSet.ShowColumnStripes,
		},
	}
	table, _ := xml.Marshal(t)
	f.saveFileList(tableXML, string(table))
}

// addTableContentTypePart provides function to add image part relationships
// in the file [Content_Types].xml by given drawing index.
func (f *File) addTableContentTypePart(index int) {
	content := f.contentTypesReader()
	for _, v := range content.Overrides {
		if v.PartName == "/xl/tables/table"+strconv.Itoa(index)+".xml" {
			return
		}
	}
	content.Overrides = append(content.Overrides, xlsxOverride{
		PartName:    "/xl/tables/table" + strconv.Itoa(index) + ".xml",
		ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml",
	})
}