drawing.go 38 KB

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