drawing.go 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312
  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 files. Support reads and writes XLSX file generated by
  7. // Microsoft Excel™ 2007 and later. Support save file without losing original
  8. // charts of XLSX. This library needs Go version 1.10 or later.
  9. package excelize
  10. import (
  11. "bytes"
  12. "encoding/xml"
  13. "fmt"
  14. "io"
  15. "log"
  16. "reflect"
  17. "strconv"
  18. "strings"
  19. )
  20. // prepareDrawing provides a function to prepare drawing ID and XML by given
  21. // drawingID, worksheet name and default drawingXML.
  22. func (f *File) prepareDrawing(xlsx *xlsxWorksheet, drawingID int, sheet, drawingXML string) (int, string) {
  23. sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
  24. if xlsx.Drawing != nil {
  25. // The worksheet already has a picture or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
  26. sheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
  27. drawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, "../drawings/drawing"), ".xml"))
  28. drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1)
  29. } else {
  30. // Add first picture for given sheet.
  31. sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
  32. rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
  33. f.addSheetDrawing(sheet, rID)
  34. }
  35. return drawingID, drawingXML
  36. }
  37. // prepareChartSheetDrawing provides a function to prepare drawing ID and XML
  38. // by given drawingID, worksheet name and default drawingXML.
  39. func (f *File) prepareChartSheetDrawing(xlsx *xlsxChartsheet, drawingID int, sheet, drawingXML string) (int, string) {
  40. sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
  41. if xlsx.Drawing != nil {
  42. // The worksheet already has a picture or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
  43. sheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
  44. drawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, "../drawings/drawing"), ".xml"))
  45. drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1)
  46. } else {
  47. // Add first picture for given sheet.
  48. sheetRels := "xl/chartsheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/chartsheets/") + ".rels"
  49. rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
  50. xlsx.Drawing = &xlsxDrawing{
  51. RID: "rId" + strconv.Itoa(rID),
  52. }
  53. }
  54. return drawingID, drawingXML
  55. }
  56. // addChart provides a function to create chart as xl/charts/chart%d.xml by
  57. // given format sets.
  58. func (f *File) addChart(formatSet *formatChart, comboCharts []*formatChart) {
  59. count := f.countCharts()
  60. xlsxChartSpace := xlsxChartSpace{
  61. XMLNSc: NameSpaceDrawingMLChart,
  62. XMLNSa: NameSpaceDrawingML,
  63. XMLNSr: SourceRelationship,
  64. XMLNSc16r2: SourceRelationshipChart201506,
  65. Date1904: &attrValBool{Val: boolPtr(false)},
  66. Lang: &attrValString{Val: stringPtr("en-US")},
  67. RoundedCorners: &attrValBool{Val: boolPtr(false)},
  68. Chart: cChart{
  69. Title: &cTitle{
  70. Tx: cTx{
  71. Rich: &cRich{
  72. P: aP{
  73. PPr: &aPPr{
  74. DefRPr: aRPr{
  75. Kern: 1200,
  76. Strike: "noStrike",
  77. U: "none",
  78. Sz: 1400,
  79. SolidFill: &aSolidFill{
  80. SchemeClr: &aSchemeClr{
  81. Val: "tx1",
  82. LumMod: &attrValInt{
  83. Val: intPtr(65000),
  84. },
  85. LumOff: &attrValInt{
  86. Val: intPtr(35000),
  87. },
  88. },
  89. },
  90. Ea: &aEa{
  91. Typeface: "+mn-ea",
  92. },
  93. Cs: &aCs{
  94. Typeface: "+mn-cs",
  95. },
  96. Latin: &aLatin{
  97. Typeface: "+mn-lt",
  98. },
  99. },
  100. },
  101. R: &aR{
  102. RPr: aRPr{
  103. Lang: "en-US",
  104. AltLang: "en-US",
  105. },
  106. T: formatSet.Title.Name,
  107. },
  108. },
  109. },
  110. },
  111. TxPr: cTxPr{
  112. P: aP{
  113. PPr: &aPPr{
  114. DefRPr: aRPr{
  115. Kern: 1200,
  116. U: "none",
  117. Sz: 14000,
  118. Strike: "noStrike",
  119. },
  120. },
  121. EndParaRPr: &aEndParaRPr{
  122. Lang: "en-US",
  123. },
  124. },
  125. },
  126. Overlay: &attrValBool{Val: boolPtr(false)},
  127. },
  128. View3D: &cView3D{
  129. RotX: &attrValInt{Val: intPtr(chartView3DRotX[formatSet.Type])},
  130. RotY: &attrValInt{Val: intPtr(chartView3DRotY[formatSet.Type])},
  131. Perspective: &attrValInt{Val: intPtr(chartView3DPerspective[formatSet.Type])},
  132. RAngAx: &attrValInt{Val: intPtr(chartView3DRAngAx[formatSet.Type])},
  133. },
  134. Floor: &cThicknessSpPr{
  135. Thickness: &attrValInt{Val: intPtr(0)},
  136. },
  137. SideWall: &cThicknessSpPr{
  138. Thickness: &attrValInt{Val: intPtr(0)},
  139. },
  140. BackWall: &cThicknessSpPr{
  141. Thickness: &attrValInt{Val: intPtr(0)},
  142. },
  143. PlotArea: &cPlotArea{},
  144. Legend: &cLegend{
  145. LegendPos: &attrValString{Val: stringPtr(chartLegendPosition[formatSet.Legend.Position])},
  146. Overlay: &attrValBool{Val: boolPtr(false)},
  147. },
  148. PlotVisOnly: &attrValBool{Val: boolPtr(false)},
  149. DispBlanksAs: &attrValString{Val: stringPtr(formatSet.ShowBlanksAs)},
  150. ShowDLblsOverMax: &attrValBool{Val: boolPtr(false)},
  151. },
  152. SpPr: &cSpPr{
  153. SolidFill: &aSolidFill{
  154. SchemeClr: &aSchemeClr{Val: "bg1"},
  155. },
  156. Ln: &aLn{
  157. W: 9525,
  158. Cap: "flat",
  159. Cmpd: "sng",
  160. Algn: "ctr",
  161. SolidFill: &aSolidFill{
  162. SchemeClr: &aSchemeClr{Val: "tx1",
  163. LumMod: &attrValInt{
  164. Val: intPtr(15000),
  165. },
  166. LumOff: &attrValInt{
  167. Val: intPtr(85000),
  168. },
  169. },
  170. },
  171. },
  172. },
  173. PrintSettings: &cPrintSettings{
  174. PageMargins: &cPageMargins{
  175. B: 0.75,
  176. L: 0.7,
  177. R: 0.7,
  178. T: 0.7,
  179. Header: 0.3,
  180. Footer: 0.3,
  181. },
  182. },
  183. }
  184. plotAreaFunc := map[string]func(*formatChart) *cPlotArea{
  185. Area: f.drawBaseChart,
  186. AreaStacked: f.drawBaseChart,
  187. AreaPercentStacked: f.drawBaseChart,
  188. Area3D: f.drawBaseChart,
  189. Area3DStacked: f.drawBaseChart,
  190. Area3DPercentStacked: f.drawBaseChart,
  191. Bar: f.drawBaseChart,
  192. BarStacked: f.drawBaseChart,
  193. BarPercentStacked: f.drawBaseChart,
  194. Bar3DClustered: f.drawBaseChart,
  195. Bar3DStacked: f.drawBaseChart,
  196. Bar3DPercentStacked: f.drawBaseChart,
  197. Bar3DConeClustered: f.drawBaseChart,
  198. Bar3DConeStacked: f.drawBaseChart,
  199. Bar3DConePercentStacked: f.drawBaseChart,
  200. Bar3DPyramidClustered: f.drawBaseChart,
  201. Bar3DPyramidStacked: f.drawBaseChart,
  202. Bar3DPyramidPercentStacked: f.drawBaseChart,
  203. Bar3DCylinderClustered: f.drawBaseChart,
  204. Bar3DCylinderStacked: f.drawBaseChart,
  205. Bar3DCylinderPercentStacked: f.drawBaseChart,
  206. Col: f.drawBaseChart,
  207. ColStacked: f.drawBaseChart,
  208. ColPercentStacked: f.drawBaseChart,
  209. Col3D: f.drawBaseChart,
  210. Col3DClustered: f.drawBaseChart,
  211. Col3DStacked: f.drawBaseChart,
  212. Col3DPercentStacked: f.drawBaseChart,
  213. Col3DCone: f.drawBaseChart,
  214. Col3DConeClustered: f.drawBaseChart,
  215. Col3DConeStacked: f.drawBaseChart,
  216. Col3DConePercentStacked: f.drawBaseChart,
  217. Col3DPyramid: f.drawBaseChart,
  218. Col3DPyramidClustered: f.drawBaseChart,
  219. Col3DPyramidStacked: f.drawBaseChart,
  220. Col3DPyramidPercentStacked: f.drawBaseChart,
  221. Col3DCylinder: f.drawBaseChart,
  222. Col3DCylinderClustered: f.drawBaseChart,
  223. Col3DCylinderStacked: f.drawBaseChart,
  224. Col3DCylinderPercentStacked: f.drawBaseChart,
  225. Doughnut: f.drawDoughnutChart,
  226. Line: f.drawLineChart,
  227. Pie3D: f.drawPie3DChart,
  228. Pie: f.drawPieChart,
  229. PieOfPieChart: f.drawPieOfPieChart,
  230. BarOfPieChart: f.drawBarOfPieChart,
  231. Radar: f.drawRadarChart,
  232. Scatter: f.drawScatterChart,
  233. Surface3D: f.drawSurface3DChart,
  234. WireframeSurface3D: f.drawSurface3DChart,
  235. Contour: f.drawSurfaceChart,
  236. WireframeContour: f.drawSurfaceChart,
  237. Bubble: f.drawBaseChart,
  238. Bubble3D: f.drawBaseChart,
  239. }
  240. addChart := func(c, p *cPlotArea) {
  241. immutable, mutable := reflect.ValueOf(c).Elem(), reflect.ValueOf(p).Elem()
  242. for i := 0; i < mutable.NumField(); i++ {
  243. field := mutable.Field(i)
  244. if field.IsNil() {
  245. continue
  246. }
  247. immutable.FieldByName(mutable.Type().Field(i).Name).Set(field)
  248. }
  249. }
  250. addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[formatSet.Type](formatSet))
  251. order := len(formatSet.Series)
  252. for idx := range comboCharts {
  253. comboCharts[idx].order = order
  254. addChart(xlsxChartSpace.Chart.PlotArea, plotAreaFunc[comboCharts[idx].Type](comboCharts[idx]))
  255. order += len(comboCharts[idx].Series)
  256. }
  257. chart, _ := xml.Marshal(xlsxChartSpace)
  258. media := "xl/charts/chart" + strconv.Itoa(count+1) + ".xml"
  259. f.saveFileList(media, chart)
  260. }
  261. // drawBaseChart provides a function to draw the c:plotArea element for bar,
  262. // and column series charts by given format sets.
  263. func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
  264. c := cCharts{
  265. BarDir: &attrValString{
  266. Val: stringPtr("col"),
  267. },
  268. Grouping: &attrValString{
  269. Val: stringPtr("clustered"),
  270. },
  271. VaryColors: &attrValBool{
  272. Val: boolPtr(true),
  273. },
  274. Ser: f.drawChartSeries(formatSet),
  275. Shape: f.drawChartShape(formatSet),
  276. DLbls: f.drawChartDLbls(formatSet),
  277. AxID: []*attrValInt{
  278. {Val: intPtr(754001152)},
  279. {Val: intPtr(753999904)},
  280. },
  281. Overlap: &attrValInt{Val: intPtr(100)},
  282. }
  283. var ok bool
  284. if *c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok {
  285. c.BarDir = nil
  286. }
  287. if *c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok {
  288. c.Grouping = nil
  289. }
  290. if *c.Overlap.Val, ok = plotAreaChartOverlap[formatSet.Type]; !ok {
  291. c.Overlap = nil
  292. }
  293. catAx := f.drawPlotAreaCatAx(formatSet)
  294. valAx := f.drawPlotAreaValAx(formatSet)
  295. charts := map[string]*cPlotArea{
  296. "area": {
  297. AreaChart: &c,
  298. CatAx: catAx,
  299. ValAx: valAx,
  300. },
  301. "areaStacked": {
  302. AreaChart: &c,
  303. CatAx: catAx,
  304. ValAx: valAx,
  305. },
  306. "areaPercentStacked": {
  307. AreaChart: &c,
  308. CatAx: catAx,
  309. ValAx: valAx,
  310. },
  311. "area3D": {
  312. Area3DChart: &c,
  313. CatAx: catAx,
  314. ValAx: valAx,
  315. },
  316. "area3DStacked": {
  317. Area3DChart: &c,
  318. CatAx: catAx,
  319. ValAx: valAx,
  320. },
  321. "area3DPercentStacked": {
  322. Area3DChart: &c,
  323. CatAx: catAx,
  324. ValAx: valAx,
  325. },
  326. "bar": {
  327. BarChart: &c,
  328. CatAx: catAx,
  329. ValAx: valAx,
  330. },
  331. "barStacked": {
  332. BarChart: &c,
  333. CatAx: catAx,
  334. ValAx: valAx,
  335. },
  336. "barPercentStacked": {
  337. BarChart: &c,
  338. CatAx: catAx,
  339. ValAx: valAx,
  340. },
  341. "bar3DClustered": {
  342. Bar3DChart: &c,
  343. CatAx: catAx,
  344. ValAx: valAx,
  345. },
  346. "bar3DStacked": {
  347. Bar3DChart: &c,
  348. CatAx: catAx,
  349. ValAx: valAx,
  350. },
  351. "bar3DPercentStacked": {
  352. Bar3DChart: &c,
  353. CatAx: catAx,
  354. ValAx: valAx,
  355. },
  356. "bar3DConeClustered": {
  357. Bar3DChart: &c,
  358. CatAx: catAx,
  359. ValAx: valAx,
  360. },
  361. "bar3DConeStacked": {
  362. Bar3DChart: &c,
  363. CatAx: catAx,
  364. ValAx: valAx,
  365. },
  366. "bar3DConePercentStacked": {
  367. Bar3DChart: &c,
  368. CatAx: catAx,
  369. ValAx: valAx,
  370. },
  371. "bar3DPyramidClustered": {
  372. Bar3DChart: &c,
  373. CatAx: catAx,
  374. ValAx: valAx,
  375. },
  376. "bar3DPyramidStacked": {
  377. Bar3DChart: &c,
  378. CatAx: catAx,
  379. ValAx: valAx,
  380. },
  381. "bar3DPyramidPercentStacked": {
  382. Bar3DChart: &c,
  383. CatAx: catAx,
  384. ValAx: valAx,
  385. },
  386. "bar3DCylinderClustered": {
  387. Bar3DChart: &c,
  388. CatAx: catAx,
  389. ValAx: valAx,
  390. },
  391. "bar3DCylinderStacked": {
  392. Bar3DChart: &c,
  393. CatAx: catAx,
  394. ValAx: valAx,
  395. },
  396. "bar3DCylinderPercentStacked": {
  397. Bar3DChart: &c,
  398. CatAx: catAx,
  399. ValAx: valAx,
  400. },
  401. "col": {
  402. BarChart: &c,
  403. CatAx: catAx,
  404. ValAx: valAx,
  405. },
  406. "colStacked": {
  407. BarChart: &c,
  408. CatAx: catAx,
  409. ValAx: valAx,
  410. },
  411. "colPercentStacked": {
  412. BarChart: &c,
  413. CatAx: catAx,
  414. ValAx: valAx,
  415. },
  416. "col3D": {
  417. Bar3DChart: &c,
  418. CatAx: catAx,
  419. ValAx: valAx,
  420. },
  421. "col3DClustered": {
  422. Bar3DChart: &c,
  423. CatAx: catAx,
  424. ValAx: valAx,
  425. },
  426. "col3DStacked": {
  427. Bar3DChart: &c,
  428. CatAx: catAx,
  429. ValAx: valAx,
  430. },
  431. "col3DPercentStacked": {
  432. Bar3DChart: &c,
  433. CatAx: catAx,
  434. ValAx: valAx,
  435. },
  436. "col3DCone": {
  437. Bar3DChart: &c,
  438. CatAx: catAx,
  439. ValAx: valAx,
  440. },
  441. "col3DConeClustered": {
  442. Bar3DChart: &c,
  443. CatAx: catAx,
  444. ValAx: valAx,
  445. },
  446. "col3DConeStacked": {
  447. Bar3DChart: &c,
  448. CatAx: catAx,
  449. ValAx: valAx,
  450. },
  451. "col3DConePercentStacked": {
  452. Bar3DChart: &c,
  453. CatAx: catAx,
  454. ValAx: valAx,
  455. },
  456. "col3DPyramid": {
  457. Bar3DChart: &c,
  458. CatAx: catAx,
  459. ValAx: valAx,
  460. },
  461. "col3DPyramidClustered": {
  462. Bar3DChart: &c,
  463. CatAx: catAx,
  464. ValAx: valAx,
  465. },
  466. "col3DPyramidStacked": {
  467. Bar3DChart: &c,
  468. CatAx: catAx,
  469. ValAx: valAx,
  470. },
  471. "col3DPyramidPercentStacked": {
  472. Bar3DChart: &c,
  473. CatAx: catAx,
  474. ValAx: valAx,
  475. },
  476. "col3DCylinder": {
  477. Bar3DChart: &c,
  478. CatAx: catAx,
  479. ValAx: valAx,
  480. },
  481. "col3DCylinderClustered": {
  482. Bar3DChart: &c,
  483. CatAx: catAx,
  484. ValAx: valAx,
  485. },
  486. "col3DCylinderStacked": {
  487. Bar3DChart: &c,
  488. CatAx: catAx,
  489. ValAx: valAx,
  490. },
  491. "col3DCylinderPercentStacked": {
  492. Bar3DChart: &c,
  493. CatAx: catAx,
  494. ValAx: valAx,
  495. },
  496. "bubble": {
  497. BubbleChart: &c,
  498. CatAx: catAx,
  499. ValAx: valAx,
  500. },
  501. "bubble3D": {
  502. BubbleChart: &c,
  503. CatAx: catAx,
  504. ValAx: valAx,
  505. },
  506. }
  507. return charts[formatSet.Type]
  508. }
  509. // drawDoughnutChart provides a function to draw the c:plotArea element for
  510. // doughnut chart by given format sets.
  511. func (f *File) drawDoughnutChart(formatSet *formatChart) *cPlotArea {
  512. return &cPlotArea{
  513. DoughnutChart: &cCharts{
  514. VaryColors: &attrValBool{
  515. Val: boolPtr(true),
  516. },
  517. Ser: f.drawChartSeries(formatSet),
  518. HoleSize: &attrValInt{Val: intPtr(75)},
  519. },
  520. }
  521. }
  522. // drawLineChart provides a function to draw the c:plotArea element for line
  523. // chart by given format sets.
  524. func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
  525. return &cPlotArea{
  526. LineChart: &cCharts{
  527. Grouping: &attrValString{
  528. Val: stringPtr(plotAreaChartGrouping[formatSet.Type]),
  529. },
  530. VaryColors: &attrValBool{
  531. Val: boolPtr(false),
  532. },
  533. Ser: f.drawChartSeries(formatSet),
  534. DLbls: f.drawChartDLbls(formatSet),
  535. Smooth: &attrValBool{
  536. Val: boolPtr(false),
  537. },
  538. AxID: []*attrValInt{
  539. {Val: intPtr(754001152)},
  540. {Val: intPtr(753999904)},
  541. },
  542. },
  543. CatAx: f.drawPlotAreaCatAx(formatSet),
  544. ValAx: f.drawPlotAreaValAx(formatSet),
  545. }
  546. }
  547. // drawPieChart provides a function to draw the c:plotArea element for pie
  548. // chart by given format sets.
  549. func (f *File) drawPieChart(formatSet *formatChart) *cPlotArea {
  550. return &cPlotArea{
  551. PieChart: &cCharts{
  552. VaryColors: &attrValBool{
  553. Val: boolPtr(true),
  554. },
  555. Ser: f.drawChartSeries(formatSet),
  556. },
  557. }
  558. }
  559. // drawPie3DChart provides a function to draw the c:plotArea element for 3D
  560. // pie chart by given format sets.
  561. func (f *File) drawPie3DChart(formatSet *formatChart) *cPlotArea {
  562. return &cPlotArea{
  563. Pie3DChart: &cCharts{
  564. VaryColors: &attrValBool{
  565. Val: boolPtr(true),
  566. },
  567. Ser: f.drawChartSeries(formatSet),
  568. },
  569. }
  570. }
  571. // drawPieOfPieChart provides a function to draw the c:plotArea element for
  572. // pie chart by given format sets.
  573. func (f *File) drawPieOfPieChart(formatSet *formatChart) *cPlotArea {
  574. return &cPlotArea{
  575. OfPieChart: &cCharts{
  576. OfPieType: &attrValString{
  577. Val: stringPtr("pie"),
  578. },
  579. VaryColors: &attrValBool{
  580. Val: boolPtr(true),
  581. },
  582. Ser: f.drawChartSeries(formatSet),
  583. SerLines: &attrValString{},
  584. },
  585. }
  586. }
  587. // drawBarOfPieChart provides a function to draw the c:plotArea element for
  588. // pie chart by given format sets.
  589. func (f *File) drawBarOfPieChart(formatSet *formatChart) *cPlotArea {
  590. return &cPlotArea{
  591. OfPieChart: &cCharts{
  592. OfPieType: &attrValString{
  593. Val: stringPtr("bar"),
  594. },
  595. VaryColors: &attrValBool{
  596. Val: boolPtr(true),
  597. },
  598. Ser: f.drawChartSeries(formatSet),
  599. SerLines: &attrValString{},
  600. },
  601. }
  602. }
  603. // drawRadarChart provides a function to draw the c:plotArea element for radar
  604. // chart by given format sets.
  605. func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
  606. return &cPlotArea{
  607. RadarChart: &cCharts{
  608. RadarStyle: &attrValString{
  609. Val: stringPtr("marker"),
  610. },
  611. VaryColors: &attrValBool{
  612. Val: boolPtr(false),
  613. },
  614. Ser: f.drawChartSeries(formatSet),
  615. DLbls: f.drawChartDLbls(formatSet),
  616. AxID: []*attrValInt{
  617. {Val: intPtr(754001152)},
  618. {Val: intPtr(753999904)},
  619. },
  620. },
  621. CatAx: f.drawPlotAreaCatAx(formatSet),
  622. ValAx: f.drawPlotAreaValAx(formatSet),
  623. }
  624. }
  625. // drawScatterChart provides a function to draw the c:plotArea element for
  626. // scatter chart by given format sets.
  627. func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
  628. return &cPlotArea{
  629. ScatterChart: &cCharts{
  630. ScatterStyle: &attrValString{
  631. Val: stringPtr("smoothMarker"), // line,lineMarker,marker,none,smooth,smoothMarker
  632. },
  633. VaryColors: &attrValBool{
  634. Val: boolPtr(false),
  635. },
  636. Ser: f.drawChartSeries(formatSet),
  637. DLbls: f.drawChartDLbls(formatSet),
  638. AxID: []*attrValInt{
  639. {Val: intPtr(754001152)},
  640. {Val: intPtr(753999904)},
  641. },
  642. },
  643. CatAx: f.drawPlotAreaCatAx(formatSet),
  644. ValAx: f.drawPlotAreaValAx(formatSet),
  645. }
  646. }
  647. // drawSurface3DChart provides a function to draw the c:surface3DChart element by
  648. // given format sets.
  649. func (f *File) drawSurface3DChart(formatSet *formatChart) *cPlotArea {
  650. plotArea := &cPlotArea{
  651. Surface3DChart: &cCharts{
  652. Ser: f.drawChartSeries(formatSet),
  653. AxID: []*attrValInt{
  654. {Val: intPtr(754001152)},
  655. {Val: intPtr(753999904)},
  656. {Val: intPtr(832256642)},
  657. },
  658. },
  659. CatAx: f.drawPlotAreaCatAx(formatSet),
  660. ValAx: f.drawPlotAreaValAx(formatSet),
  661. SerAx: f.drawPlotAreaSerAx(formatSet),
  662. }
  663. if formatSet.Type == WireframeSurface3D {
  664. plotArea.Surface3DChart.Wireframe = &attrValBool{Val: boolPtr(true)}
  665. }
  666. return plotArea
  667. }
  668. // drawSurfaceChart provides a function to draw the c:surfaceChart element by
  669. // given format sets.
  670. func (f *File) drawSurfaceChart(formatSet *formatChart) *cPlotArea {
  671. plotArea := &cPlotArea{
  672. SurfaceChart: &cCharts{
  673. Ser: f.drawChartSeries(formatSet),
  674. AxID: []*attrValInt{
  675. {Val: intPtr(754001152)},
  676. {Val: intPtr(753999904)},
  677. {Val: intPtr(832256642)},
  678. },
  679. },
  680. CatAx: f.drawPlotAreaCatAx(formatSet),
  681. ValAx: f.drawPlotAreaValAx(formatSet),
  682. SerAx: f.drawPlotAreaSerAx(formatSet),
  683. }
  684. if formatSet.Type == WireframeContour {
  685. plotArea.SurfaceChart.Wireframe = &attrValBool{Val: boolPtr(true)}
  686. }
  687. return plotArea
  688. }
  689. // drawChartShape provides a function to draw the c:shape element by given
  690. // format sets.
  691. func (f *File) drawChartShape(formatSet *formatChart) *attrValString {
  692. shapes := map[string]string{
  693. Bar3DConeClustered: "cone",
  694. Bar3DConeStacked: "cone",
  695. Bar3DConePercentStacked: "cone",
  696. Bar3DPyramidClustered: "pyramid",
  697. Bar3DPyramidStacked: "pyramid",
  698. Bar3DPyramidPercentStacked: "pyramid",
  699. Bar3DCylinderClustered: "cylinder",
  700. Bar3DCylinderStacked: "cylinder",
  701. Bar3DCylinderPercentStacked: "cylinder",
  702. Col3DCone: "cone",
  703. Col3DConeClustered: "cone",
  704. Col3DConeStacked: "cone",
  705. Col3DConePercentStacked: "cone",
  706. Col3DPyramid: "pyramid",
  707. Col3DPyramidClustered: "pyramid",
  708. Col3DPyramidStacked: "pyramid",
  709. Col3DPyramidPercentStacked: "pyramid",
  710. Col3DCylinder: "cylinder",
  711. Col3DCylinderClustered: "cylinder",
  712. Col3DCylinderStacked: "cylinder",
  713. Col3DCylinderPercentStacked: "cylinder",
  714. }
  715. if shape, ok := shapes[formatSet.Type]; ok {
  716. return &attrValString{Val: stringPtr(shape)}
  717. }
  718. return nil
  719. }
  720. // drawChartSeries provides a function to draw the c:ser element by given
  721. // format sets.
  722. func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
  723. ser := []cSer{}
  724. for k := range formatSet.Series {
  725. ser = append(ser, cSer{
  726. IDx: &attrValInt{Val: intPtr(k + formatSet.order)},
  727. Order: &attrValInt{Val: intPtr(k + formatSet.order)},
  728. Tx: &cTx{
  729. StrRef: &cStrRef{
  730. F: formatSet.Series[k].Name,
  731. },
  732. },
  733. SpPr: f.drawChartSeriesSpPr(k, formatSet),
  734. Marker: f.drawChartSeriesMarker(k, formatSet),
  735. DPt: f.drawChartSeriesDPt(k, formatSet),
  736. DLbls: f.drawChartSeriesDLbls(formatSet),
  737. Cat: f.drawChartSeriesCat(formatSet.Series[k], formatSet),
  738. Val: f.drawChartSeriesVal(formatSet.Series[k], formatSet),
  739. XVal: f.drawChartSeriesXVal(formatSet.Series[k], formatSet),
  740. YVal: f.drawChartSeriesYVal(formatSet.Series[k], formatSet),
  741. BubbleSize: f.drawCharSeriesBubbleSize(formatSet.Series[k], formatSet),
  742. Bubble3D: f.drawCharSeriesBubble3D(formatSet),
  743. })
  744. }
  745. return &ser
  746. }
  747. // drawChartSeriesSpPr provides a function to draw the c:spPr element by given
  748. // format sets.
  749. func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
  750. spPrScatter := &cSpPr{
  751. Ln: &aLn{
  752. W: 25400,
  753. NoFill: " ",
  754. },
  755. }
  756. spPrLine := &cSpPr{
  757. Ln: &aLn{
  758. W: f.ptToEMUs(formatSet.Series[i].Line.Width),
  759. Cap: "rnd", // rnd, sq, flat
  760. },
  761. }
  762. if i+formatSet.order < 6 {
  763. spPrLine.Ln.SolidFill = &aSolidFill{
  764. SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa(i+formatSet.order+1)},
  765. }
  766. }
  767. chartSeriesSpPr := map[string]*cSpPr{Line: spPrLine, Scatter: spPrScatter}
  768. return chartSeriesSpPr[formatSet.Type]
  769. }
  770. // drawChartSeriesDPt provides a function to draw the c:dPt element by given
  771. // data index and format sets.
  772. func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
  773. dpt := []*cDPt{{
  774. IDx: &attrValInt{Val: intPtr(i)},
  775. Bubble3D: &attrValBool{Val: boolPtr(false)},
  776. SpPr: &cSpPr{
  777. SolidFill: &aSolidFill{
  778. SchemeClr: &aSchemeClr{Val: "accent" + strconv.Itoa(i+1)},
  779. },
  780. Ln: &aLn{
  781. W: 25400,
  782. Cap: "rnd",
  783. SolidFill: &aSolidFill{
  784. SchemeClr: &aSchemeClr{Val: "lt" + strconv.Itoa(i+1)},
  785. },
  786. },
  787. Sp3D: &aSp3D{
  788. ContourW: 25400,
  789. ContourClr: &aContourClr{
  790. SchemeClr: &aSchemeClr{Val: "lt" + strconv.Itoa(i+1)},
  791. },
  792. },
  793. },
  794. }}
  795. chartSeriesDPt := map[string][]*cDPt{Pie: dpt, Pie3D: dpt}
  796. return chartSeriesDPt[formatSet.Type]
  797. }
  798. // drawChartSeriesCat provides a function to draw the c:cat element by given
  799. // chart series and format sets.
  800. func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *cCat {
  801. cat := &cCat{
  802. StrRef: &cStrRef{
  803. F: v.Categories,
  804. },
  805. }
  806. chartSeriesCat := map[string]*cCat{Scatter: nil, Bubble: nil, Bubble3D: nil}
  807. if _, ok := chartSeriesCat[formatSet.Type]; ok || v.Categories == "" {
  808. return nil
  809. }
  810. return cat
  811. }
  812. // drawChartSeriesVal provides a function to draw the c:val element by given
  813. // chart series and format sets.
  814. func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *cVal {
  815. val := &cVal{
  816. NumRef: &cNumRef{
  817. F: v.Values,
  818. },
  819. }
  820. chartSeriesVal := map[string]*cVal{Scatter: nil, Bubble: nil, Bubble3D: nil}
  821. if _, ok := chartSeriesVal[formatSet.Type]; ok {
  822. return nil
  823. }
  824. return val
  825. }
  826. // drawChartSeriesMarker provides a function to draw the c:marker element by
  827. // given data index and format sets.
  828. func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
  829. marker := &cMarker{
  830. Symbol: &attrValString{Val: stringPtr("circle")},
  831. Size: &attrValInt{Val: intPtr(5)},
  832. }
  833. if i < 6 {
  834. marker.SpPr = &cSpPr{
  835. SolidFill: &aSolidFill{
  836. SchemeClr: &aSchemeClr{
  837. Val: "accent" + strconv.Itoa(i+1),
  838. },
  839. },
  840. Ln: &aLn{
  841. W: 9252,
  842. SolidFill: &aSolidFill{
  843. SchemeClr: &aSchemeClr{
  844. Val: "accent" + strconv.Itoa(i+1),
  845. },
  846. },
  847. },
  848. }
  849. }
  850. chartSeriesMarker := map[string]*cMarker{Scatter: marker}
  851. return chartSeriesMarker[formatSet.Type]
  852. }
  853. // drawChartSeriesXVal provides a function to draw the c:xVal element by given
  854. // chart series and format sets.
  855. func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart) *cCat {
  856. cat := &cCat{
  857. StrRef: &cStrRef{
  858. F: v.Categories,
  859. },
  860. }
  861. chartSeriesXVal := map[string]*cCat{Scatter: cat}
  862. return chartSeriesXVal[formatSet.Type]
  863. }
  864. // drawChartSeriesYVal provides a function to draw the c:yVal element by given
  865. // chart series and format sets.
  866. func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart) *cVal {
  867. val := &cVal{
  868. NumRef: &cNumRef{
  869. F: v.Values,
  870. },
  871. }
  872. chartSeriesYVal := map[string]*cVal{Scatter: val, Bubble: val, Bubble3D: val}
  873. return chartSeriesYVal[formatSet.Type]
  874. }
  875. // drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
  876. // element by given chart series and format sets.
  877. func (f *File) drawCharSeriesBubbleSize(v formatChartSeries, formatSet *formatChart) *cVal {
  878. if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[formatSet.Type]; !ok {
  879. return nil
  880. }
  881. return &cVal{
  882. NumRef: &cNumRef{
  883. F: v.Values,
  884. },
  885. }
  886. }
  887. // drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
  888. // by given format sets.
  889. func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool {
  890. if _, ok := map[string]bool{Bubble3D: true}[formatSet.Type]; !ok {
  891. return nil
  892. }
  893. return &attrValBool{Val: boolPtr(true)}
  894. }
  895. // drawChartDLbls provides a function to draw the c:dLbls element by given
  896. // format sets.
  897. func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
  898. return &cDLbls{
  899. ShowLegendKey: &attrValBool{Val: boolPtr(formatSet.Legend.ShowLegendKey)},
  900. ShowVal: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowVal)},
  901. ShowCatName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowCatName)},
  902. ShowSerName: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowSerName)},
  903. ShowBubbleSize: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowBubbleSize)},
  904. ShowPercent: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowPercent)},
  905. ShowLeaderLines: &attrValBool{Val: boolPtr(formatSet.Plotarea.ShowLeaderLines)},
  906. }
  907. }
  908. // drawChartSeriesDLbls provides a function to draw the c:dLbls element by
  909. // given format sets.
  910. func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
  911. dLbls := f.drawChartDLbls(formatSet)
  912. chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil}
  913. if _, ok := chartSeriesDLbls[formatSet.Type]; ok {
  914. return nil
  915. }
  916. return dLbls
  917. }
  918. // drawPlotAreaCatAx provides a function to draw the c:catAx element.
  919. func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
  920. min := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Minimum)}
  921. max := &attrValFloat{Val: float64Ptr(formatSet.XAxis.Maximum)}
  922. if formatSet.XAxis.Minimum == 0 {
  923. min = nil
  924. }
  925. if formatSet.XAxis.Maximum == 0 {
  926. max = nil
  927. }
  928. axs := []*cAxs{
  929. {
  930. AxID: &attrValInt{Val: intPtr(754001152)},
  931. Scaling: &cScaling{
  932. Orientation: &attrValString{Val: stringPtr(orientation[formatSet.XAxis.ReverseOrder])},
  933. Max: max,
  934. Min: min,
  935. },
  936. Delete: &attrValBool{Val: boolPtr(false)},
  937. AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])},
  938. NumFmt: &cNumFmt{
  939. FormatCode: "General",
  940. SourceLinked: true,
  941. },
  942. MajorTickMark: &attrValString{Val: stringPtr("none")},
  943. MinorTickMark: &attrValString{Val: stringPtr("none")},
  944. TickLblPos: &attrValString{Val: stringPtr("nextTo")},
  945. SpPr: f.drawPlotAreaSpPr(),
  946. TxPr: f.drawPlotAreaTxPr(),
  947. CrossAx: &attrValInt{Val: intPtr(753999904)},
  948. Crosses: &attrValString{Val: stringPtr("autoZero")},
  949. Auto: &attrValBool{Val: boolPtr(true)},
  950. LblAlgn: &attrValString{Val: stringPtr("ctr")},
  951. LblOffset: &attrValInt{Val: intPtr(100)},
  952. NoMultiLvlLbl: &attrValBool{Val: boolPtr(false)},
  953. },
  954. }
  955. if formatSet.XAxis.MajorGridlines {
  956. axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
  957. }
  958. if formatSet.XAxis.MinorGridlines {
  959. axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
  960. }
  961. if formatSet.XAxis.TickLabelSkip != 0 {
  962. axs[0].TickLblSkip = &attrValInt{Val: intPtr(formatSet.XAxis.TickLabelSkip)}
  963. }
  964. return axs
  965. }
  966. // drawPlotAreaValAx provides a function to draw the c:valAx element.
  967. func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
  968. min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)}
  969. max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)}
  970. if formatSet.YAxis.Minimum == 0 {
  971. min = nil
  972. }
  973. if formatSet.YAxis.Maximum == 0 {
  974. max = nil
  975. }
  976. axs := []*cAxs{
  977. {
  978. AxID: &attrValInt{Val: intPtr(753999904)},
  979. Scaling: &cScaling{
  980. Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])},
  981. Max: max,
  982. Min: min,
  983. },
  984. Delete: &attrValBool{Val: boolPtr(false)},
  985. AxPos: &attrValString{Val: stringPtr(valAxPos[formatSet.YAxis.ReverseOrder])},
  986. NumFmt: &cNumFmt{
  987. FormatCode: chartValAxNumFmtFormatCode[formatSet.Type],
  988. SourceLinked: true,
  989. },
  990. MajorTickMark: &attrValString{Val: stringPtr("none")},
  991. MinorTickMark: &attrValString{Val: stringPtr("none")},
  992. TickLblPos: &attrValString{Val: stringPtr("nextTo")},
  993. SpPr: f.drawPlotAreaSpPr(),
  994. TxPr: f.drawPlotAreaTxPr(),
  995. CrossAx: &attrValInt{Val: intPtr(754001152)},
  996. Crosses: &attrValString{Val: stringPtr("autoZero")},
  997. CrossBetween: &attrValString{Val: stringPtr(chartValAxCrossBetween[formatSet.Type])},
  998. },
  999. }
  1000. if formatSet.YAxis.MajorGridlines {
  1001. axs[0].MajorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
  1002. }
  1003. if formatSet.YAxis.MinorGridlines {
  1004. axs[0].MinorGridlines = &cChartLines{SpPr: f.drawPlotAreaSpPr()}
  1005. }
  1006. if pos, ok := valTickLblPos[formatSet.Type]; ok {
  1007. axs[0].TickLblPos.Val = stringPtr(pos)
  1008. }
  1009. if formatSet.YAxis.MajorUnit != 0 {
  1010. axs[0].MajorUnit = &attrValFloat{Val: float64Ptr(formatSet.YAxis.MajorUnit)}
  1011. }
  1012. return axs
  1013. }
  1014. // drawPlotAreaSerAx provides a function to draw the c:serAx element.
  1015. func (f *File) drawPlotAreaSerAx(formatSet *formatChart) []*cAxs {
  1016. min := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Minimum)}
  1017. max := &attrValFloat{Val: float64Ptr(formatSet.YAxis.Maximum)}
  1018. if formatSet.YAxis.Minimum == 0 {
  1019. min = nil
  1020. }
  1021. if formatSet.YAxis.Maximum == 0 {
  1022. max = nil
  1023. }
  1024. return []*cAxs{
  1025. {
  1026. AxID: &attrValInt{Val: intPtr(832256642)},
  1027. Scaling: &cScaling{
  1028. Orientation: &attrValString{Val: stringPtr(orientation[formatSet.YAxis.ReverseOrder])},
  1029. Max: max,
  1030. Min: min,
  1031. },
  1032. Delete: &attrValBool{Val: boolPtr(false)},
  1033. AxPos: &attrValString{Val: stringPtr(catAxPos[formatSet.XAxis.ReverseOrder])},
  1034. TickLblPos: &attrValString{Val: stringPtr("nextTo")},
  1035. SpPr: f.drawPlotAreaSpPr(),
  1036. TxPr: f.drawPlotAreaTxPr(),
  1037. CrossAx: &attrValInt{Val: intPtr(753999904)},
  1038. },
  1039. }
  1040. }
  1041. // drawPlotAreaSpPr provides a function to draw the c:spPr element.
  1042. func (f *File) drawPlotAreaSpPr() *cSpPr {
  1043. return &cSpPr{
  1044. Ln: &aLn{
  1045. W: 9525,
  1046. Cap: "flat",
  1047. Cmpd: "sng",
  1048. Algn: "ctr",
  1049. SolidFill: &aSolidFill{
  1050. SchemeClr: &aSchemeClr{
  1051. Val: "tx1",
  1052. LumMod: &attrValInt{Val: intPtr(15000)},
  1053. LumOff: &attrValInt{Val: intPtr(85000)},
  1054. },
  1055. },
  1056. },
  1057. }
  1058. }
  1059. // drawPlotAreaTxPr provides a function to draw the c:txPr element.
  1060. func (f *File) drawPlotAreaTxPr() *cTxPr {
  1061. return &cTxPr{
  1062. BodyPr: aBodyPr{
  1063. Rot: -60000000,
  1064. SpcFirstLastPara: true,
  1065. VertOverflow: "ellipsis",
  1066. Vert: "horz",
  1067. Wrap: "square",
  1068. Anchor: "ctr",
  1069. AnchorCtr: true,
  1070. },
  1071. P: aP{
  1072. PPr: &aPPr{
  1073. DefRPr: aRPr{
  1074. Sz: 900,
  1075. B: false,
  1076. I: false,
  1077. U: "none",
  1078. Strike: "noStrike",
  1079. Kern: 1200,
  1080. Baseline: 0,
  1081. SolidFill: &aSolidFill{
  1082. SchemeClr: &aSchemeClr{
  1083. Val: "tx1",
  1084. LumMod: &attrValInt{Val: intPtr(15000)},
  1085. LumOff: &attrValInt{Val: intPtr(85000)},
  1086. },
  1087. },
  1088. Latin: &aLatin{Typeface: "+mn-lt"},
  1089. Ea: &aEa{Typeface: "+mn-ea"},
  1090. Cs: &aCs{Typeface: "+mn-cs"},
  1091. },
  1092. },
  1093. EndParaRPr: &aEndParaRPr{Lang: "en-US"},
  1094. },
  1095. }
  1096. }
  1097. // drawingParser provides a function to parse drawingXML. In order to solve
  1098. // the problem that the label structure is changed after serialization and
  1099. // deserialization, two different structures: decodeWsDr and encodeWsDr are
  1100. // defined.
  1101. func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
  1102. var (
  1103. err error
  1104. ok bool
  1105. )
  1106. if f.Drawings[path] == nil {
  1107. content := xlsxWsDr{}
  1108. content.A = NameSpaceDrawingML
  1109. content.Xdr = NameSpaceDrawingMLSpreadSheet
  1110. if _, ok = f.XLSX[path]; ok { // Append Model
  1111. decodeWsDr := decodeWsDr{}
  1112. if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(path)))).
  1113. Decode(&decodeWsDr); err != nil && err != io.EOF {
  1114. log.Printf("xml decode error: %s", err)
  1115. }
  1116. content.R = decodeWsDr.R
  1117. for _, v := range decodeWsDr.OneCellAnchor {
  1118. content.OneCellAnchor = append(content.OneCellAnchor, &xdrCellAnchor{
  1119. EditAs: v.EditAs,
  1120. GraphicFrame: v.Content,
  1121. })
  1122. }
  1123. for _, v := range decodeWsDr.TwoCellAnchor {
  1124. content.TwoCellAnchor = append(content.TwoCellAnchor, &xdrCellAnchor{
  1125. EditAs: v.EditAs,
  1126. GraphicFrame: v.Content,
  1127. })
  1128. }
  1129. }
  1130. f.Drawings[path] = &content
  1131. }
  1132. wsDr := f.Drawings[path]
  1133. return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2
  1134. }
  1135. // addDrawingChart provides a function to add chart graphic frame by given
  1136. // sheet, drawingXML, cell, width, height, relationship index and format sets.
  1137. func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rID int, formatSet *formatPicture) error {
  1138. col, row, err := CellNameToCoordinates(cell)
  1139. if err != nil {
  1140. return err
  1141. }
  1142. colIdx := col - 1
  1143. rowIdx := row - 1
  1144. width = int(float64(width) * formatSet.XScale)
  1145. height = int(float64(height) * formatSet.YScale)
  1146. colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 :=
  1147. f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.OffsetX, formatSet.OffsetY, width, height)
  1148. content, cNvPrID := f.drawingParser(drawingXML)
  1149. twoCellAnchor := xdrCellAnchor{}
  1150. twoCellAnchor.EditAs = formatSet.Positioning
  1151. from := xlsxFrom{}
  1152. from.Col = colStart
  1153. from.ColOff = formatSet.OffsetX * EMU
  1154. from.Row = rowStart
  1155. from.RowOff = formatSet.OffsetY * EMU
  1156. to := xlsxTo{}
  1157. to.Col = colEnd
  1158. to.ColOff = x2 * EMU
  1159. to.Row = rowEnd
  1160. to.RowOff = y2 * EMU
  1161. twoCellAnchor.From = &from
  1162. twoCellAnchor.To = &to
  1163. graphicFrame := xlsxGraphicFrame{
  1164. NvGraphicFramePr: xlsxNvGraphicFramePr{
  1165. CNvPr: &xlsxCNvPr{
  1166. ID: cNvPrID,
  1167. Name: "Chart " + strconv.Itoa(cNvPrID),
  1168. },
  1169. },
  1170. Graphic: &xlsxGraphic{
  1171. GraphicData: &xlsxGraphicData{
  1172. URI: NameSpaceDrawingMLChart,
  1173. Chart: &xlsxChart{
  1174. C: NameSpaceDrawingMLChart,
  1175. R: SourceRelationship,
  1176. RID: "rId" + strconv.Itoa(rID),
  1177. },
  1178. },
  1179. },
  1180. }
  1181. graphic, _ := xml.Marshal(graphicFrame)
  1182. twoCellAnchor.GraphicFrame = string(graphic)
  1183. twoCellAnchor.ClientData = &xdrClientData{
  1184. FLocksWithSheet: formatSet.FLocksWithSheet,
  1185. FPrintsWithSheet: formatSet.FPrintsWithSheet,
  1186. }
  1187. content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
  1188. f.Drawings[drawingXML] = content
  1189. return err
  1190. }
  1191. // addSheetDrawingChart provides a function to add chart graphic frame for
  1192. // chartsheet by given sheet, drawingXML, width, height, relationship index
  1193. // and format sets.
  1194. func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *formatPicture) {
  1195. content, cNvPrID := f.drawingParser(drawingXML)
  1196. absoluteAnchor := xdrCellAnchor{
  1197. EditAs: formatSet.Positioning,
  1198. Pos: &xlsxPoint2D{},
  1199. Ext: &xlsxExt{},
  1200. }
  1201. graphicFrame := xlsxGraphicFrame{
  1202. NvGraphicFramePr: xlsxNvGraphicFramePr{
  1203. CNvPr: &xlsxCNvPr{
  1204. ID: cNvPrID,
  1205. Name: "Chart " + strconv.Itoa(cNvPrID),
  1206. },
  1207. },
  1208. Graphic: &xlsxGraphic{
  1209. GraphicData: &xlsxGraphicData{
  1210. URI: NameSpaceDrawingMLChart,
  1211. Chart: &xlsxChart{
  1212. C: NameSpaceDrawingMLChart,
  1213. R: SourceRelationship,
  1214. RID: "rId" + strconv.Itoa(rID),
  1215. },
  1216. },
  1217. },
  1218. }
  1219. graphic, _ := xml.Marshal(graphicFrame)
  1220. absoluteAnchor.GraphicFrame = string(graphic)
  1221. absoluteAnchor.ClientData = &xdrClientData{
  1222. FLocksWithSheet: formatSet.FLocksWithSheet,
  1223. FPrintsWithSheet: formatSet.FPrintsWithSheet,
  1224. }
  1225. content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor)
  1226. f.Drawings[drawingXML] = content
  1227. return
  1228. }
  1229. // deleteDrawing provides a function to delete chart graphic frame by given by
  1230. // given coordinates and graphic type.
  1231. func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err error) {
  1232. var (
  1233. wsDr *xlsxWsDr
  1234. deTwoCellAnchor *decodeTwoCellAnchor
  1235. )
  1236. xdrCellAnchorFuncs := map[string]func(anchor *xdrCellAnchor) bool{
  1237. "Chart": func(anchor *xdrCellAnchor) bool { return anchor.Pic == nil },
  1238. "Pic": func(anchor *xdrCellAnchor) bool { return anchor.Pic != nil },
  1239. }
  1240. decodeTwoCellAnchorFuncs := map[string]func(anchor *decodeTwoCellAnchor) bool{
  1241. "Chart": func(anchor *decodeTwoCellAnchor) bool { return anchor.Pic == nil },
  1242. "Pic": func(anchor *decodeTwoCellAnchor) bool { return anchor.Pic != nil },
  1243. }
  1244. wsDr, _ = f.drawingParser(drawingXML)
  1245. for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
  1246. if err = nil; wsDr.TwoCellAnchor[idx].From != nil && xdrCellAnchorFuncs[drawingType](wsDr.TwoCellAnchor[idx]) {
  1247. if wsDr.TwoCellAnchor[idx].From.Col == col && wsDr.TwoCellAnchor[idx].From.Row == row {
  1248. wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
  1249. idx--
  1250. }
  1251. }
  1252. }
  1253. for idx := 0; idx < len(wsDr.TwoCellAnchor); idx++ {
  1254. deTwoCellAnchor = new(decodeTwoCellAnchor)
  1255. if err = f.xmlNewDecoder(bytes.NewReader([]byte("<decodeTwoCellAnchor>" + wsDr.TwoCellAnchor[idx].GraphicFrame + "</decodeTwoCellAnchor>"))).
  1256. Decode(deTwoCellAnchor); err != nil && err != io.EOF {
  1257. err = fmt.Errorf("xml decode error: %s", err)
  1258. return
  1259. }
  1260. if err = nil; deTwoCellAnchor.From != nil && decodeTwoCellAnchorFuncs[drawingType](deTwoCellAnchor) {
  1261. if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
  1262. wsDr.TwoCellAnchor = append(wsDr.TwoCellAnchor[:idx], wsDr.TwoCellAnchor[idx+1:]...)
  1263. idx--
  1264. }
  1265. }
  1266. }
  1267. f.Drawings[drawingXML] = wsDr
  1268. return err
  1269. }