drawing.go 36 KB

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