drawing.go 34 KB

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