shape.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. "encoding/json"
  14. "strconv"
  15. "strings"
  16. )
  17. // parseFormatShapeSet provides a function to parse the format settings of the
  18. // shape with default value.
  19. func parseFormatShapeSet(formatSet string) (*formatShape, error) {
  20. format := formatShape{
  21. Width: 160,
  22. Height: 160,
  23. Format: formatPicture{
  24. FPrintsWithSheet: true,
  25. FLocksWithSheet: false,
  26. NoChangeAspect: false,
  27. OffsetX: 0,
  28. OffsetY: 0,
  29. XScale: 1.0,
  30. YScale: 1.0,
  31. },
  32. }
  33. err := json.Unmarshal([]byte(formatSet), &format)
  34. return &format, err
  35. }
  36. // AddShape provides the method to add shape in a sheet by given worksheet
  37. // index, shape format set (such as offset, scale, aspect ratio setting and
  38. // print settings) and properties set. For example, add text box (rect shape)
  39. // in Sheet1:
  40. //
  41. // err := f.AddShape("Sheet1", "G6", `{"type":"rect","color":{"line":"#4286F4","fill":"#8eb9ff"},"paragraph":[{"text":"Rectangle Shape","font":{"bold":true,"italic":true,"family":"Times New Roman","size":36,"color":"#777777","underline":"sng"}}],"width":180,"height": 90}`)
  42. //
  43. // The following shows the type of shape supported by excelize:
  44. //
  45. // accentBorderCallout1 (Callout 1 with Border and Accent Shape)
  46. // accentBorderCallout2 (Callout 2 with Border and Accent Shape)
  47. // accentBorderCallout3 (Callout 3 with Border and Accent Shape)
  48. // accentCallout1 (Callout 1 Shape)
  49. // accentCallout2 (Callout 2 Shape)
  50. // accentCallout3 (Callout 3 Shape)
  51. // actionButtonBackPrevious (Back or Previous Button Shape)
  52. // actionButtonBeginning (Beginning Button Shape)
  53. // actionButtonBlank (Blank Button Shape)
  54. // actionButtonDocument (Document Button Shape)
  55. // actionButtonEnd (End Button Shape)
  56. // actionButtonForwardNext (Forward or Next Button Shape)
  57. // actionButtonHelp (Help Button Shape)
  58. // actionButtonHome (Home Button Shape)
  59. // actionButtonInformation (Information Button Shape)
  60. // actionButtonMovie (Movie Button Shape)
  61. // actionButtonReturn (Return Button Shape)
  62. // actionButtonSound (Sound Button Shape)
  63. // arc (Curved Arc Shape)
  64. // bentArrow (Bent Arrow Shape)
  65. // bentConnector2 (Bent Connector 2 Shape)
  66. // bentConnector3 (Bent Connector 3 Shape)
  67. // bentConnector4 (Bent Connector 4 Shape)
  68. // bentConnector5 (Bent Connector 5 Shape)
  69. // bentUpArrow (Bent Up Arrow Shape)
  70. // bevel (Bevel Shape)
  71. // blockArc (Block Arc Shape)
  72. // borderCallout1 (Callout 1 with Border Shape)
  73. // borderCallout2 (Callout 2 with Border Shape)
  74. // borderCallout3 (Callout 3 with Border Shape)
  75. // bracePair (Brace Pair Shape)
  76. // bracketPair (Bracket Pair Shape)
  77. // callout1 (Callout 1 Shape)
  78. // callout2 (Callout 2 Shape)
  79. // callout3 (Callout 3 Shape)
  80. // can (Can Shape)
  81. // chartPlus (Chart Plus Shape)
  82. // chartStar (Chart Star Shape)
  83. // chartX (Chart X Shape)
  84. // chevron (Chevron Shape)
  85. // chord (Chord Shape)
  86. // circularArrow (Circular Arrow Shape)
  87. // cloud (Cloud Shape)
  88. // cloudCallout (Callout Cloud Shape)
  89. // corner (Corner Shape)
  90. // cornerTabs (Corner Tabs Shape)
  91. // cube (Cube Shape)
  92. // curvedConnector2 (Curved Connector 2 Shape)
  93. // curvedConnector3 (Curved Connector 3 Shape)
  94. // curvedConnector4 (Curved Connector 4 Shape)
  95. // curvedConnector5 (Curved Connector 5 Shape)
  96. // curvedDownArrow (Curved Down Arrow Shape)
  97. // curvedLeftArrow (Curved Left Arrow Shape)
  98. // curvedRightArrow (Curved Right Arrow Shape)
  99. // curvedUpArrow (Curved Up Arrow Shape)
  100. // decagon (Decagon Shape)
  101. // diagStripe (Diagonal Stripe Shape)
  102. // diamond (Diamond Shape)
  103. // dodecagon (Dodecagon Shape)
  104. // donut (Donut Shape)
  105. // doubleWave (Double Wave Shape)
  106. // downArrow (Down Arrow Shape)
  107. // downArrowCallout (Callout Down Arrow Shape)
  108. // ellipse (Ellipse Shape)
  109. // ellipseRibbon (Ellipse Ribbon Shape)
  110. // ellipseRibbon2 (Ellipse Ribbon 2 Shape)
  111. // flowChartAlternateProcess (Alternate Process Flow Shape)
  112. // flowChartCollate (Collate Flow Shape)
  113. // flowChartConnector (Connector Flow Shape)
  114. // flowChartDecision (Decision Flow Shape)
  115. // flowChartDelay (Delay Flow Shape)
  116. // flowChartDisplay (Display Flow Shape)
  117. // flowChartDocument (Document Flow Shape)
  118. // flowChartExtract (Extract Flow Shape)
  119. // flowChartInputOutput (Input Output Flow Shape)
  120. // flowChartInternalStorage (Internal Storage Flow Shape)
  121. // flowChartMagneticDisk (Magnetic Disk Flow Shape)
  122. // flowChartMagneticDrum (Magnetic Drum Flow Shape)
  123. // flowChartMagneticTape (Magnetic Tape Flow Shape)
  124. // flowChartManualInput (Manual Input Flow Shape)
  125. // flowChartManualOperation (Manual Operation Flow Shape)
  126. // flowChartMerge (Merge Flow Shape)
  127. // flowChartMultidocument (Multi-Document Flow Shape)
  128. // flowChartOfflineStorage (Offline Storage Flow Shape)
  129. // flowChartOffpageConnector (Off-Page Connector Flow Shape)
  130. // flowChartOnlineStorage (Online Storage Flow Shape)
  131. // flowChartOr (Or Flow Shape)
  132. // flowChartPredefinedProcess (Predefined Process Flow Shape)
  133. // flowChartPreparation (Preparation Flow Shape)
  134. // flowChartProcess (Process Flow Shape)
  135. // flowChartPunchedCard (Punched Card Flow Shape)
  136. // flowChartPunchedTape (Punched Tape Flow Shape)
  137. // flowChartSort (Sort Flow Shape)
  138. // flowChartSummingJunction (Summing Junction Flow Shape)
  139. // flowChartTerminator (Terminator Flow Shape)
  140. // foldedCorner (Folded Corner Shape)
  141. // frame (Frame Shape)
  142. // funnel (Funnel Shape)
  143. // gear6 (Gear 6 Shape)
  144. // gear9 (Gear 9 Shape)
  145. // halfFrame (Half Frame Shape)
  146. // heart (Heart Shape)
  147. // heptagon (Heptagon Shape)
  148. // hexagon (Hexagon Shape)
  149. // homePlate (Home Plate Shape)
  150. // horizontalScroll (Horizontal Scroll Shape)
  151. // irregularSeal1 (Irregular Seal 1 Shape)
  152. // irregularSeal2 (Irregular Seal 2 Shape)
  153. // leftArrow (Left Arrow Shape)
  154. // leftArrowCallout (Callout Left Arrow Shape)
  155. // leftBrace (Left Brace Shape)
  156. // leftBracket (Left Bracket Shape)
  157. // leftCircularArrow (Left Circular Arrow Shape)
  158. // leftRightArrow (Left Right Arrow Shape)
  159. // leftRightArrowCallout (Callout Left Right Arrow Shape)
  160. // leftRightCircularArrow (Left Right Circular Arrow Shape)
  161. // leftRightRibbon (Left Right Ribbon Shape)
  162. // leftRightUpArrow (Left Right Up Arrow Shape)
  163. // leftUpArrow (Left Up Arrow Shape)
  164. // lightningBolt (Lightning Bolt Shape)
  165. // line (Line Shape)
  166. // lineInv (Line Inverse Shape)
  167. // mathDivide (Divide Math Shape)
  168. // mathEqual (Equal Math Shape)
  169. // mathMinus (Minus Math Shape)
  170. // mathMultiply (Multiply Math Shape)
  171. // mathNotEqual (Not Equal Math Shape)
  172. // mathPlus (Plus Math Shape)
  173. // moon (Moon Shape)
  174. // nonIsoscelesTrapezoid (Non-Isosceles Trapezoid Shape)
  175. // noSmoking (No Smoking Shape)
  176. // notchedRightArrow (Notched Right Arrow Shape)
  177. // octagon (Octagon Shape)
  178. // parallelogram (Parallelogram Shape)
  179. // pentagon (Pentagon Shape)
  180. // pie (Pie Shape)
  181. // pieWedge (Pie Wedge Shape)
  182. // plaque (Plaque Shape)
  183. // plaqueTabs (Plaque Tabs Shape)
  184. // plus (Plus Shape)
  185. // quadArrow (Quad-Arrow Shape)
  186. // quadArrowCallout (Callout Quad-Arrow Shape)
  187. // rect (Rectangle Shape)
  188. // ribbon (Ribbon Shape)
  189. // ribbon2 (Ribbon 2 Shape)
  190. // rightArrow (Right Arrow Shape)
  191. // rightArrowCallout (Callout Right Arrow Shape)
  192. // rightBrace (Right Brace Shape)
  193. // rightBracket (Right Bracket Shape)
  194. // round1Rect (One Round Corner Rectangle Shape)
  195. // round2DiagRect (Two Diagonal Round Corner Rectangle Shape)
  196. // round2SameRect (Two Same-side Round Corner Rectangle Shape)
  197. // roundRect (Round Corner Rectangle Shape)
  198. // rtTriangle (Right Triangle Shape)
  199. // smileyFace (Smiley Face Shape)
  200. // snip1Rect (One Snip Corner Rectangle Shape)
  201. // snip2DiagRect (Two Diagonal Snip Corner Rectangle Shape)
  202. // snip2SameRect (Two Same-side Snip Corner Rectangle Shape)
  203. // snipRoundRect (One Snip One Round Corner Rectangle Shape)
  204. // squareTabs (Square Tabs Shape)
  205. // star10 (Ten Pointed Star Shape)
  206. // star12 (Twelve Pointed Star Shape)
  207. // star16 (Sixteen Pointed Star Shape)
  208. // star24 (Twenty Four Pointed Star Shape)
  209. // star32 (Thirty Two Pointed Star Shape)
  210. // star4 (Four Pointed Star Shape)
  211. // star5 (Five Pointed Star Shape)
  212. // star6 (Six Pointed Star Shape)
  213. // star7 (Seven Pointed Star Shape)
  214. // star8 (Eight Pointed Star Shape)
  215. // straightConnector1 (Straight Connector 1 Shape)
  216. // stripedRightArrow (Striped Right Arrow Shape)
  217. // sun (Sun Shape)
  218. // swooshArrow (Swoosh Arrow Shape)
  219. // teardrop (Teardrop Shape)
  220. // trapezoid (Trapezoid Shape)
  221. // triangle (Triangle Shape)
  222. // upArrow (Up Arrow Shape)
  223. // upArrowCallout (Callout Up Arrow Shape)
  224. // upDownArrow (Up Down Arrow Shape)
  225. // upDownArrowCallout (Callout Up Down Arrow Shape)
  226. // uturnArrow (U-Turn Arrow Shape)
  227. // verticalScroll (Vertical Scroll Shape)
  228. // wave (Wave Shape)
  229. // wedgeEllipseCallout (Callout Wedge Ellipse Shape)
  230. // wedgeRectCallout (Callout Wedge Rectangle Shape)
  231. // wedgeRoundRectCallout (Callout Wedge Round Rectangle Shape)
  232. //
  233. // The following shows the type of text underline supported by excelize:
  234. //
  235. // none
  236. // words
  237. // sng
  238. // dbl
  239. // heavy
  240. // dotted
  241. // dottedHeavy
  242. // dash
  243. // dashHeavy
  244. // dashLong
  245. // dashLongHeavy
  246. // dotDash
  247. // dotDashHeavy
  248. // dotDotDash
  249. // dotDotDashHeavy
  250. // wavy
  251. // wavyHeavy
  252. // wavyDbl
  253. //
  254. func (f *File) AddShape(sheet, cell, format string) error {
  255. formatSet, err := parseFormatShapeSet(format)
  256. if err != nil {
  257. return err
  258. }
  259. // Read sheet data.
  260. xlsx, err := f.workSheetReader(sheet)
  261. if err != nil {
  262. return err
  263. }
  264. // Add first shape for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
  265. drawingID := f.countDrawings() + 1
  266. drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
  267. sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
  268. if xlsx.Drawing != nil {
  269. // The worksheet already has a shape or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
  270. sheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
  271. drawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, "../drawings/drawing"), ".xml"))
  272. drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1)
  273. } else {
  274. // Add first shape for given sheet.
  275. sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(f.sheetMap[trimSheetName(sheet)], "xl/worksheets/") + ".rels"
  276. rID := f.addRels(sheetRels, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
  277. f.addSheetDrawing(sheet, rID)
  278. }
  279. err = f.addDrawingShape(sheet, drawingXML, cell, formatSet)
  280. if err != nil {
  281. return err
  282. }
  283. f.addContentTypePart(drawingID, "drawings")
  284. return err
  285. }
  286. // addDrawingShape provides a function to add preset geometry by given sheet,
  287. // drawingXMLand format sets.
  288. func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) error {
  289. fromCol, fromRow, err := CellNameToCoordinates(cell)
  290. if err != nil {
  291. return err
  292. }
  293. colIdx := fromCol - 1
  294. rowIdx := fromRow - 1
  295. textUnderlineType := map[string]bool{
  296. "none": true,
  297. "words": true,
  298. "sng": true,
  299. "dbl": true,
  300. "heavy": true,
  301. "dotted": true,
  302. "dottedHeavy": true,
  303. "dash": true,
  304. "dashHeavy": true,
  305. "dashLong": true,
  306. "dashLongHeavy": true,
  307. "dotDash": true,
  308. "dotDashHeavy": true,
  309. "dotDotDash": true,
  310. "dotDotDashHeavy": true,
  311. "wavy": true,
  312. "wavyHeavy": true,
  313. "wavyDbl": true,
  314. }
  315. width := int(float64(formatSet.Width) * formatSet.Format.XScale)
  316. height := int(float64(formatSet.Height) * formatSet.Format.YScale)
  317. colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 :=
  318. f.positionObjectPixels(sheet, colIdx, rowIdx, formatSet.Format.OffsetX, formatSet.Format.OffsetY,
  319. width, height)
  320. content, cNvPrID := f.drawingParser(drawingXML)
  321. twoCellAnchor := xdrCellAnchor{}
  322. twoCellAnchor.EditAs = formatSet.Format.Positioning
  323. from := xlsxFrom{}
  324. from.Col = colStart
  325. from.ColOff = formatSet.Format.OffsetX * EMU
  326. from.Row = rowStart
  327. from.RowOff = formatSet.Format.OffsetY * EMU
  328. to := xlsxTo{}
  329. to.Col = colEnd
  330. to.ColOff = x2 * EMU
  331. to.Row = rowEnd
  332. to.RowOff = y2 * EMU
  333. twoCellAnchor.From = &from
  334. twoCellAnchor.To = &to
  335. shape := xdrSp{
  336. NvSpPr: &xdrNvSpPr{
  337. CNvPr: &xlsxCNvPr{
  338. ID: cNvPrID,
  339. Name: "Shape " + strconv.Itoa(cNvPrID),
  340. },
  341. CNvSpPr: &xdrCNvSpPr{
  342. TxBox: true,
  343. },
  344. },
  345. SpPr: &xlsxSpPr{
  346. PrstGeom: xlsxPrstGeom{
  347. Prst: formatSet.Type,
  348. },
  349. },
  350. Style: &xdrStyle{
  351. LnRef: setShapeRef(formatSet.Color.Line, 2),
  352. FillRef: setShapeRef(formatSet.Color.Fill, 1),
  353. EffectRef: setShapeRef(formatSet.Color.Effect, 0),
  354. FontRef: &aFontRef{
  355. Idx: "minor",
  356. SchemeClr: &attrValString{
  357. Val: stringPtr("tx1"),
  358. },
  359. },
  360. },
  361. TxBody: &xdrTxBody{
  362. BodyPr: &aBodyPr{
  363. VertOverflow: "clip",
  364. HorzOverflow: "clip",
  365. Wrap: "none",
  366. RtlCol: false,
  367. Anchor: "t",
  368. },
  369. },
  370. }
  371. if len(formatSet.Paragraph) < 1 {
  372. formatSet.Paragraph = []formatShapeParagraph{
  373. {
  374. Font: Font{
  375. Bold: false,
  376. Italic: false,
  377. Underline: "none",
  378. Family: f.GetDefaultFont(),
  379. Size: 11,
  380. Color: "#000000",
  381. },
  382. Text: " ",
  383. },
  384. }
  385. }
  386. for _, p := range formatSet.Paragraph {
  387. u := p.Font.Underline
  388. _, ok := textUnderlineType[u]
  389. if !ok {
  390. u = "none"
  391. }
  392. text := p.Text
  393. if text == "" {
  394. text = " "
  395. }
  396. paragraph := &aP{
  397. R: &aR{
  398. RPr: aRPr{
  399. I: p.Font.Italic,
  400. B: p.Font.Bold,
  401. Lang: "en-US",
  402. AltLang: "en-US",
  403. U: u,
  404. Sz: p.Font.Size * 100,
  405. Latin: &aLatin{Typeface: p.Font.Family},
  406. },
  407. T: text,
  408. },
  409. EndParaRPr: &aEndParaRPr{
  410. Lang: "en-US",
  411. },
  412. }
  413. srgbClr := strings.Replace(strings.ToUpper(p.Font.Color), "#", "", -1)
  414. if len(srgbClr) == 6 {
  415. paragraph.R.RPr.SolidFill = &aSolidFill{
  416. SrgbClr: &attrValString{
  417. Val: stringPtr(srgbClr),
  418. },
  419. }
  420. }
  421. shape.TxBody.P = append(shape.TxBody.P, paragraph)
  422. }
  423. twoCellAnchor.Sp = &shape
  424. twoCellAnchor.ClientData = &xdrClientData{
  425. FLocksWithSheet: formatSet.Format.FLocksWithSheet,
  426. FPrintsWithSheet: formatSet.Format.FPrintsWithSheet,
  427. }
  428. content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
  429. f.Drawings[drawingXML] = content
  430. return err
  431. }
  432. // setShapeRef provides a function to set color with hex model by given actual
  433. // color value.
  434. func setShapeRef(color string, i int) *aRef {
  435. if color == "" {
  436. return &aRef{
  437. Idx: 0,
  438. ScrgbClr: &aScrgbClr{
  439. R: 0,
  440. G: 0,
  441. B: 0,
  442. },
  443. }
  444. }
  445. return &aRef{
  446. Idx: i,
  447. SrgbClr: &attrValString{
  448. Val: stringPtr(strings.Replace(strings.ToUpper(color), "#", "", -1)),
  449. },
  450. }
  451. }