pivotTable.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. // Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
  2. // this source code is governed by a BSD-style license that can be found in
  3. // the LICENSE file.
  4. //
  5. // Package excelize providing a set of functions that allow you to write to
  6. // and read from XLSX / XLSM / XLTM files. Supports reading and writing
  7. // spreadsheet documents generated by Microsoft Exce™ 2007 and later. Supports
  8. // complex components by high compatibility, and provided streaming API for
  9. // generating or reading data from a worksheet with huge amounts of data. This
  10. // library needs Go version 1.10 or later.
  11. package excelize
  12. import (
  13. "encoding/xml"
  14. "errors"
  15. "fmt"
  16. "strconv"
  17. "strings"
  18. )
  19. // PivotTableOption directly maps the format settings of the pivot table.
  20. type PivotTableOption struct {
  21. DataRange string
  22. PivotTableRange string
  23. Rows []PivotTableField
  24. Columns []PivotTableField
  25. Data []PivotTableField
  26. Filter []PivotTableField
  27. RowGrandTotals bool
  28. ColGrandTotals bool
  29. ShowDrill bool
  30. UseAutoFormatting bool
  31. PageOverThenDown bool
  32. MergeItem bool
  33. CompactData bool
  34. ShowRowHeaders bool
  35. ShowColHeaders bool
  36. ShowRowStripes bool
  37. ShowColStripes bool
  38. ShowLastColumn bool
  39. PivotTableStyleName string
  40. }
  41. // PivotTableField directly maps the field settings of the pivot table.
  42. // Subtotal specifies the aggregation function that applies to this data
  43. // field. The default value is sum. The possible values for this attribute
  44. // are:
  45. //
  46. // Average
  47. // Count
  48. // CountNums
  49. // Max
  50. // Min
  51. // Product
  52. // StdDev
  53. // StdDevp
  54. // Sum
  55. // Var
  56. // Varp
  57. //
  58. // Name specifies the name of the data field. Maximum 255 characters
  59. // are allowed in data field name, excess characters will be truncated.
  60. type PivotTableField struct {
  61. Data string
  62. Name string
  63. Subtotal string
  64. DefaultSubtotal bool
  65. }
  66. // AddPivotTable provides the method to add pivot table by given pivot table
  67. // options.
  68. //
  69. // For example, create a pivot table on the Sheet1!$G$2:$M$34 area with the
  70. // region Sheet1!$A$1:$E$31 as the data source, summarize by sum for sales:
  71. //
  72. // package main
  73. //
  74. // import (
  75. // "fmt"
  76. // "math/rand"
  77. //
  78. // "github.com/360EntSecGroup-Skylar/excelize"
  79. // )
  80. //
  81. // func main() {
  82. // f := excelize.NewFile()
  83. // // Create some data in a sheet
  84. // month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
  85. // year := []int{2017, 2018, 2019}
  86. // types := []string{"Meat", "Dairy", "Beverages", "Produce"}
  87. // region := []string{"East", "West", "North", "South"}
  88. // f.SetSheetRow("Sheet1", "A1", &[]string{"Month", "Year", "Type", "Sales", "Region"})
  89. // for i := 0; i < 30; i++ {
  90. // f.SetCellValue("Sheet1", fmt.Sprintf("A%d", i+2), month[rand.Intn(12)])
  91. // f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+2), year[rand.Intn(3)])
  92. // f.SetCellValue("Sheet1", fmt.Sprintf("C%d", i+2), types[rand.Intn(4)])
  93. // f.SetCellValue("Sheet1", fmt.Sprintf("D%d", i+2), rand.Intn(5000))
  94. // f.SetCellValue("Sheet1", fmt.Sprintf("E%d", i+2), region[rand.Intn(4)])
  95. // }
  96. // if err := f.AddPivotTable(&excelize.PivotTableOption{
  97. // DataRange: "Sheet1!$A$1:$E$31",
  98. // PivotTableRange: "Sheet1!$G$2:$M$34",
  99. // Rows: []excelize.PivotTableField{{Data: "Month"}, {Data: "Year"}},
  100. // Filter: []excelize.PivotTableField{{Data: "Region"}},
  101. // Columns: []excelize.PivotTableField{{Data: "Type"}},
  102. // Data: []excelize.PivotTableField{{Data: "Sales", Name: "Summarize", Subtotal: "Sum"}},
  103. // }); err != nil {
  104. // fmt.Println(err)
  105. // }
  106. // if err := f.SaveAs("Book1.xlsx"); err != nil {
  107. // fmt.Println(err)
  108. // }
  109. // }
  110. //
  111. func (f *File) AddPivotTable(opt *PivotTableOption) error {
  112. // parameter validation
  113. dataSheet, pivotTableSheetPath, err := f.parseFormatPivotTableSet(opt)
  114. if err != nil {
  115. return err
  116. }
  117. pivotTableID := f.countPivotTables() + 1
  118. pivotCacheID := f.countPivotCache() + 1
  119. sheetRelationshipsPivotTableXML := "../pivotTables/pivotTable" + strconv.Itoa(pivotTableID) + ".xml"
  120. pivotTableXML := strings.Replace(sheetRelationshipsPivotTableXML, "..", "xl", -1)
  121. pivotCacheXML := "xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(pivotCacheID) + ".xml"
  122. err = f.addPivotCache(pivotCacheID, pivotCacheXML, opt, dataSheet)
  123. if err != nil {
  124. return err
  125. }
  126. // workbook pivot cache
  127. workBookPivotCacheRID := f.addRels("xl/_rels/workbook.xml.rels", SourceRelationshipPivotCache, fmt.Sprintf("pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
  128. cacheID := f.addWorkbookPivotCache(workBookPivotCacheRID)
  129. pivotCacheRels := "xl/pivotTables/_rels/pivotTable" + strconv.Itoa(pivotTableID) + ".xml.rels"
  130. // rId not used
  131. _ = f.addRels(pivotCacheRels, SourceRelationshipPivotCache, fmt.Sprintf("../pivotCache/pivotCacheDefinition%d.xml", pivotCacheID), "")
  132. err = f.addPivotTable(cacheID, pivotTableID, pivotTableXML, opt)
  133. if err != nil {
  134. return err
  135. }
  136. pivotTableSheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(pivotTableSheetPath, "xl/worksheets/") + ".rels"
  137. f.addRels(pivotTableSheetRels, SourceRelationshipPivotTable, sheetRelationshipsPivotTableXML, "")
  138. f.addContentTypePart(pivotTableID, "pivotTable")
  139. f.addContentTypePart(pivotCacheID, "pivotCache")
  140. return nil
  141. }
  142. // parseFormatPivotTableSet provides a function to validate pivot table
  143. // properties.
  144. func (f *File) parseFormatPivotTableSet(opt *PivotTableOption) (*xlsxWorksheet, string, error) {
  145. if opt == nil {
  146. return nil, "", errors.New("parameter is required")
  147. }
  148. dataSheetName, _, err := f.adjustRange(opt.DataRange)
  149. if err != nil {
  150. return nil, "", fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
  151. }
  152. pivotTableSheetName, _, err := f.adjustRange(opt.PivotTableRange)
  153. if err != nil {
  154. return nil, "", fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", err.Error())
  155. }
  156. dataSheet, err := f.workSheetReader(dataSheetName)
  157. if err != nil {
  158. return dataSheet, "", err
  159. }
  160. pivotTableSheetPath, ok := f.sheetMap[trimSheetName(pivotTableSheetName)]
  161. if !ok {
  162. return dataSheet, pivotTableSheetPath, fmt.Errorf("sheet %s is not exist", pivotTableSheetName)
  163. }
  164. return dataSheet, pivotTableSheetPath, err
  165. }
  166. // adjustRange adjust range, for example: adjust Sheet1!$E$31:$A$1 to Sheet1!$A$1:$E$31
  167. func (f *File) adjustRange(rangeStr string) (string, []int, error) {
  168. if len(rangeStr) < 1 {
  169. return "", []int{}, errors.New("parameter is required")
  170. }
  171. rng := strings.Split(rangeStr, "!")
  172. if len(rng) != 2 {
  173. return "", []int{}, errors.New("parameter is invalid")
  174. }
  175. trimRng := strings.Replace(rng[1], "$", "", -1)
  176. coordinates, err := f.areaRefToCoordinates(trimRng)
  177. if err != nil {
  178. return rng[0], []int{}, err
  179. }
  180. x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
  181. if x1 == x2 && y1 == y2 {
  182. return rng[0], []int{}, errors.New("parameter is invalid")
  183. }
  184. // Correct the coordinate area, such correct C1:B3 to B1:C3.
  185. if x2 < x1 {
  186. x1, x2 = x2, x1
  187. }
  188. if y2 < y1 {
  189. y1, y2 = y2, y1
  190. }
  191. return rng[0], []int{x1, y1, x2, y2}, nil
  192. }
  193. // getPivotFieldsOrder provides a function to get order list of pivot table
  194. // fields.
  195. func (f *File) getPivotFieldsOrder(dataRange string) ([]string, error) {
  196. order := []string{}
  197. dataSheet, coordinates, err := f.adjustRange(dataRange)
  198. if err != nil {
  199. return order, fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
  200. }
  201. for col := coordinates[0]; col <= coordinates[2]; col++ {
  202. coordinate, _ := CoordinatesToCellName(col, coordinates[1])
  203. name, err := f.GetCellValue(dataSheet, coordinate)
  204. if err != nil {
  205. return order, err
  206. }
  207. order = append(order, name)
  208. }
  209. return order, nil
  210. }
  211. // addPivotCache provides a function to create a pivot cache by given properties.
  212. func (f *File) addPivotCache(pivotCacheID int, pivotCacheXML string, opt *PivotTableOption, ws *xlsxWorksheet) error {
  213. // validate data range
  214. dataSheet, coordinates, err := f.adjustRange(opt.DataRange)
  215. if err != nil {
  216. return fmt.Errorf("parameter 'DataRange' parsing error: %s", err.Error())
  217. }
  218. // data range has been checked
  219. order, _ := f.getPivotFieldsOrder(opt.DataRange)
  220. hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
  221. vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
  222. pc := xlsxPivotCacheDefinition{
  223. SaveData: false,
  224. RefreshOnLoad: true,
  225. CacheSource: &xlsxCacheSource{
  226. Type: "worksheet",
  227. WorksheetSource: &xlsxWorksheetSource{
  228. Ref: hcell + ":" + vcell,
  229. Sheet: dataSheet,
  230. },
  231. },
  232. CacheFields: &xlsxCacheFields{},
  233. }
  234. for _, name := range order {
  235. defaultRowsSubtotal, rowOk := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Rows)
  236. defaultColumnsSubtotal, colOk := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Columns)
  237. sharedItems := xlsxSharedItems{
  238. Count: 0,
  239. }
  240. s := xlsxString{}
  241. if (rowOk && !defaultRowsSubtotal) || (colOk && !defaultColumnsSubtotal) {
  242. s = xlsxString{
  243. V: "",
  244. }
  245. sharedItems.Count++
  246. sharedItems.S = &s
  247. }
  248. pc.CacheFields.CacheField = append(pc.CacheFields.CacheField, &xlsxCacheField{
  249. Name: name,
  250. SharedItems: &sharedItems,
  251. })
  252. }
  253. pc.CacheFields.Count = len(pc.CacheFields.CacheField)
  254. pivotCache, err := xml.Marshal(pc)
  255. f.saveFileList(pivotCacheXML, pivotCache)
  256. return err
  257. }
  258. // addPivotTable provides a function to create a pivot table by given pivot
  259. // table ID and properties.
  260. func (f *File) addPivotTable(cacheID, pivotTableID int, pivotTableXML string, opt *PivotTableOption) error {
  261. // validate pivot table range
  262. _, coordinates, err := f.adjustRange(opt.PivotTableRange)
  263. if err != nil {
  264. return fmt.Errorf("parameter 'PivotTableRange' parsing error: %s", err.Error())
  265. }
  266. hcell, _ := CoordinatesToCellName(coordinates[0], coordinates[1])
  267. vcell, _ := CoordinatesToCellName(coordinates[2], coordinates[3])
  268. pivotTableStyle := func() string {
  269. if opt.PivotTableStyleName == "" {
  270. return "PivotStyleLight16"
  271. } else {
  272. return opt.PivotTableStyleName
  273. }
  274. }
  275. pt := xlsxPivotTableDefinition{
  276. Name: fmt.Sprintf("Pivot Table%d", pivotTableID),
  277. CacheID: cacheID,
  278. RowGrandTotals: &opt.RowGrandTotals,
  279. ColGrandTotals: &opt.ColGrandTotals,
  280. ShowDrill: &opt.ShowDrill,
  281. UseAutoFormatting: &opt.UseAutoFormatting,
  282. PageOverThenDown: &opt.PageOverThenDown,
  283. MergeItem: &opt.MergeItem,
  284. CompactData: &opt.CompactData,
  285. DataCaption: "Values",
  286. Location: &xlsxLocation{
  287. Ref: hcell + ":" + vcell,
  288. FirstDataCol: 1,
  289. FirstDataRow: 1,
  290. FirstHeaderRow: 1,
  291. },
  292. PivotFields: &xlsxPivotFields{},
  293. RowItems: &xlsxRowItems{
  294. Count: 1,
  295. I: []*xlsxI{
  296. {
  297. []*xlsxX{{}, {}},
  298. },
  299. },
  300. },
  301. ColItems: &xlsxColItems{
  302. Count: 1,
  303. I: []*xlsxI{{}},
  304. },
  305. PivotTableStyleInfo: &xlsxPivotTableStyleInfo{
  306. Name: pivotTableStyle(),
  307. ShowRowHeaders: opt.ShowRowHeaders,
  308. ShowColHeaders: opt.ShowColHeaders,
  309. ShowRowStripes: opt.ShowRowStripes,
  310. ShowColStripes: opt.ShowColStripes,
  311. ShowLastColumn: opt.ShowLastColumn,
  312. },
  313. }
  314. // pivot fields
  315. _ = f.addPivotFields(&pt, opt)
  316. // count pivot fields
  317. pt.PivotFields.Count = len(pt.PivotFields.PivotField)
  318. // data range has been checked
  319. _ = f.addPivotRowFields(&pt, opt)
  320. _ = f.addPivotColFields(&pt, opt)
  321. _ = f.addPivotPageFields(&pt, opt)
  322. _ = f.addPivotDataFields(&pt, opt)
  323. pivotTable, err := xml.Marshal(pt)
  324. f.saveFileList(pivotTableXML, pivotTable)
  325. return err
  326. }
  327. // addPivotRowFields provides a method to add row fields for pivot table by
  328. // given pivot table options.
  329. func (f *File) addPivotRowFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
  330. // row fields
  331. rowFieldsIndex, err := f.getPivotFieldsIndex(opt.Rows, opt)
  332. if err != nil {
  333. return err
  334. }
  335. for _, fieldIdx := range rowFieldsIndex {
  336. if pt.RowFields == nil {
  337. pt.RowFields = &xlsxRowFields{}
  338. }
  339. pt.RowFields.Field = append(pt.RowFields.Field, &xlsxField{
  340. X: fieldIdx,
  341. })
  342. }
  343. // count row fields
  344. if pt.RowFields != nil {
  345. pt.RowFields.Count = len(pt.RowFields.Field)
  346. }
  347. return err
  348. }
  349. // addPivotPageFields provides a method to add page fields for pivot table by
  350. // given pivot table options.
  351. func (f *File) addPivotPageFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
  352. // page fields
  353. pageFieldsIndex, err := f.getPivotFieldsIndex(opt.Filter, opt)
  354. if err != nil {
  355. return err
  356. }
  357. pageFieldsName := f.getPivotTableFieldsName(opt.Filter)
  358. for idx, pageField := range pageFieldsIndex {
  359. if pt.PageFields == nil {
  360. pt.PageFields = &xlsxPageFields{}
  361. }
  362. pt.PageFields.PageField = append(pt.PageFields.PageField, &xlsxPageField{
  363. Name: pageFieldsName[idx],
  364. Fld: pageField,
  365. })
  366. }
  367. // count page fields
  368. if pt.PageFields != nil {
  369. pt.PageFields.Count = len(pt.PageFields.PageField)
  370. }
  371. return err
  372. }
  373. // addPivotDataFields provides a method to add data fields for pivot table by
  374. // given pivot table options.
  375. func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
  376. // data fields
  377. dataFieldsIndex, err := f.getPivotFieldsIndex(opt.Data, opt)
  378. if err != nil {
  379. return err
  380. }
  381. dataFieldsSubtotals := f.getPivotTableFieldsSubtotal(opt.Data)
  382. dataFieldsName := f.getPivotTableFieldsName(opt.Data)
  383. for idx, dataField := range dataFieldsIndex {
  384. if pt.DataFields == nil {
  385. pt.DataFields = &xlsxDataFields{}
  386. }
  387. pt.DataFields.DataField = append(pt.DataFields.DataField, &xlsxDataField{
  388. Name: dataFieldsName[idx],
  389. Fld: dataField,
  390. Subtotal: dataFieldsSubtotals[idx],
  391. })
  392. }
  393. // count data fields
  394. if pt.DataFields != nil {
  395. pt.DataFields.Count = len(pt.DataFields.DataField)
  396. }
  397. return err
  398. }
  399. // inStrSlice provides a method to check if an element is present in an array,
  400. // and return the index of its location, otherwise return -1.
  401. func inStrSlice(a []string, x string) int {
  402. for idx, n := range a {
  403. if x == n {
  404. return idx
  405. }
  406. }
  407. return -1
  408. }
  409. // inPivotTableField provides a method to check if an element is present in
  410. // pivot table fields list, and return the index of its location, otherwise
  411. // return -1.
  412. func inPivotTableField(a []PivotTableField, x string) int {
  413. for idx, n := range a {
  414. if x == n.Data {
  415. return idx
  416. }
  417. }
  418. return -1
  419. }
  420. // addPivotColFields create pivot column fields by given pivot table
  421. // definition and option.
  422. func (f *File) addPivotColFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
  423. if len(opt.Columns) == 0 {
  424. return nil
  425. }
  426. pt.ColFields = &xlsxColFields{}
  427. // col fields
  428. colFieldsIndex, err := f.getPivotFieldsIndex(opt.Columns, opt)
  429. if err != nil {
  430. return err
  431. }
  432. for _, fieldIdx := range colFieldsIndex {
  433. pt.ColFields.Field = append(pt.ColFields.Field, &xlsxField{
  434. X: fieldIdx,
  435. })
  436. }
  437. // count col fields
  438. pt.ColFields.Count = len(pt.ColFields.Field)
  439. return err
  440. }
  441. // addPivotFields create pivot fields based on the column order of the first
  442. // row in the data region by given pivot table definition and option.
  443. func (f *File) addPivotFields(pt *xlsxPivotTableDefinition, opt *PivotTableOption) error {
  444. order, err := f.getPivotFieldsOrder(opt.DataRange)
  445. if err != nil {
  446. return err
  447. }
  448. x := 0
  449. for _, name := range order {
  450. if inPivotTableField(opt.Rows, name) != -1 {
  451. defaultSubtotal, ok := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Rows)
  452. var items []*xlsxItem
  453. if !ok || !defaultSubtotal {
  454. items = append(items, &xlsxItem{X: &x})
  455. } else {
  456. items = append(items, &xlsxItem{T: "default"})
  457. }
  458. pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
  459. Axis: "axisRow",
  460. Name: f.getPivotTableFieldName(name, opt.Rows),
  461. Items: &xlsxItems{
  462. Count: len(items),
  463. Item: items,
  464. },
  465. DefaultSubtotal: &defaultSubtotal,
  466. })
  467. continue
  468. }
  469. if inPivotTableField(opt.Filter, name) != -1 {
  470. pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
  471. Axis: "axisPage",
  472. Name: f.getPivotTableFieldName(name, opt.Columns),
  473. Items: &xlsxItems{
  474. Count: 1,
  475. Item: []*xlsxItem{
  476. {T: "default"},
  477. },
  478. },
  479. })
  480. continue
  481. }
  482. if inPivotTableField(opt.Columns, name) != -1 {
  483. defaultSubtotal, ok := f.getPivotTableFieldNameDefaultSubtotal(name, opt.Columns)
  484. var items []*xlsxItem
  485. if !ok || !defaultSubtotal {
  486. items = append(items, &xlsxItem{X: &x})
  487. } else {
  488. items = append(items, &xlsxItem{T: "default"})
  489. }
  490. pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
  491. Axis: "axisCol",
  492. Name: f.getPivotTableFieldName(name, opt.Columns),
  493. Items: &xlsxItems{
  494. Count: len(items),
  495. Item: items,
  496. },
  497. DefaultSubtotal: &defaultSubtotal,
  498. })
  499. continue
  500. }
  501. if inPivotTableField(opt.Data, name) != -1 {
  502. pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{
  503. DataField: true,
  504. })
  505. continue
  506. }
  507. pt.PivotFields.PivotField = append(pt.PivotFields.PivotField, &xlsxPivotField{})
  508. }
  509. return err
  510. }
  511. // countPivotTables provides a function to get drawing files count storage in
  512. // the folder xl/pivotTables.
  513. func (f *File) countPivotTables() int {
  514. count := 0
  515. for k := range f.XLSX {
  516. if strings.Contains(k, "xl/pivotTables/pivotTable") {
  517. count++
  518. }
  519. }
  520. return count
  521. }
  522. // countPivotCache provides a function to get drawing files count storage in
  523. // the folder xl/pivotCache.
  524. func (f *File) countPivotCache() int {
  525. count := 0
  526. for k := range f.XLSX {
  527. if strings.Contains(k, "xl/pivotCache/pivotCacheDefinition") {
  528. count++
  529. }
  530. }
  531. return count
  532. }
  533. // getPivotFieldsIndex convert the column of the first row in the data region
  534. // to a sequential index by given fields and pivot option.
  535. func (f *File) getPivotFieldsIndex(fields []PivotTableField, opt *PivotTableOption) ([]int, error) {
  536. pivotFieldsIndex := []int{}
  537. orders, err := f.getPivotFieldsOrder(opt.DataRange)
  538. if err != nil {
  539. return pivotFieldsIndex, err
  540. }
  541. for _, field := range fields {
  542. if pos := inStrSlice(orders, field.Data); pos != -1 {
  543. pivotFieldsIndex = append(pivotFieldsIndex, pos)
  544. }
  545. }
  546. return pivotFieldsIndex, nil
  547. }
  548. // getPivotTableFieldsSubtotal prepare fields subtotal by given pivot table fields.
  549. func (f *File) getPivotTableFieldsSubtotal(fields []PivotTableField) []string {
  550. field := make([]string, len(fields))
  551. enums := []string{"average", "count", "countNums", "max", "min", "product", "stdDev", "stdDevp", "sum", "var", "varp"}
  552. inEnums := func(enums []string, val string) string {
  553. for _, enum := range enums {
  554. if strings.ToLower(enum) == strings.ToLower(val) {
  555. return enum
  556. }
  557. }
  558. return "sum"
  559. }
  560. for idx, fld := range fields {
  561. field[idx] = inEnums(enums, fld.Subtotal)
  562. }
  563. return field
  564. }
  565. // getPivotTableFieldsName prepare fields name list by given pivot table
  566. // fields.
  567. func (f *File) getPivotTableFieldsName(fields []PivotTableField) []string {
  568. field := make([]string, len(fields))
  569. for idx, fld := range fields {
  570. if len(fld.Name) > 255 {
  571. field[idx] = fld.Name[0:255]
  572. continue
  573. }
  574. field[idx] = fld.Name
  575. }
  576. return field
  577. }
  578. // getPivotTableFieldName prepare field name by given pivot table fields.
  579. func (f *File) getPivotTableFieldName(name string, fields []PivotTableField) string {
  580. fieldsName := f.getPivotTableFieldsName(fields)
  581. for idx, field := range fields {
  582. if field.Data == name {
  583. return fieldsName[idx]
  584. }
  585. }
  586. return ""
  587. }
  588. func (f *File) getPivotTableFieldNameDefaultSubtotal(name string, fields []PivotTableField) (bool, bool) {
  589. for _, field := range fields {
  590. if field.Data == name {
  591. return field.DefaultSubtotal, true
  592. }
  593. }
  594. return false, false
  595. }
  596. // addWorkbookPivotCache add the association ID of the pivot cache in xl/workbook.xml.
  597. func (f *File) addWorkbookPivotCache(RID int) int {
  598. wb := f.workbookReader()
  599. if wb.PivotCaches == nil {
  600. wb.PivotCaches = &xlsxPivotCaches{}
  601. }
  602. cacheID := 1
  603. for _, pivotCache := range wb.PivotCaches.PivotCache {
  604. if pivotCache.CacheID > cacheID {
  605. cacheID = pivotCache.CacheID
  606. }
  607. }
  608. cacheID++
  609. wb.PivotCaches.PivotCache = append(wb.PivotCaches.PivotCache, xlsxPivotCache{
  610. CacheID: cacheID,
  611. RID: fmt.Sprintf("rId%d", RID),
  612. })
  613. return cacheID
  614. }