shape.go 15 KB

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