drawing.go 38 KB

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