drawing.go 41 KB

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