pivotTable.go 22 KB

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