picture.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. // Copyright 2016 - 2021 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 Excel™ 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.15 or later.
  11. package excelize
  12. import (
  13. "bytes"
  14. "encoding/json"
  15. "encoding/xml"
  16. "fmt"
  17. "image"
  18. "io"
  19. "io/ioutil"
  20. "os"
  21. "path"
  22. "path/filepath"
  23. "strconv"
  24. "strings"
  25. )
  26. // parseFormatPictureSet provides a function to parse the format settings of
  27. // the picture with default value.
  28. func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
  29. format := formatPicture{
  30. FPrintsWithSheet: true,
  31. FLocksWithSheet: false,
  32. NoChangeAspect: false,
  33. Autofit: false,
  34. OffsetX: 0,
  35. OffsetY: 0,
  36. XScale: 1.0,
  37. YScale: 1.0,
  38. }
  39. err := json.Unmarshal(parseFormatSet(formatSet), &format)
  40. return &format, err
  41. }
  42. // AddPicture provides the method to add picture in a sheet by given picture
  43. // format set (such as offset, scale, aspect ratio setting and print settings)
  44. // and file path. For example:
  45. //
  46. // package main
  47. //
  48. // import (
  49. // _ "image/gif"
  50. // _ "image/jpeg"
  51. // _ "image/png"
  52. //
  53. // "github.com/xuri/excelize/v2"
  54. // )
  55. //
  56. // func main() {
  57. // f := excelize.NewFile()
  58. // // Insert a picture.
  59. // if err := f.AddPicture("Sheet1", "A2", "image.jpg", ""); err != nil {
  60. // fmt.Println(err)
  61. // }
  62. // // Insert a picture scaling in the cell with location hyperlink.
  63. // if err := f.AddPicture("Sheet1", "D2", "image.png", `{"x_scale": 0.5, "y_scale": 0.5, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`); err != nil {
  64. // fmt.Println(err)
  65. // }
  66. // // Insert a picture offset in the cell with external hyperlink, printing and positioning support.
  67. // if err := f.AddPicture("Sheet1", "H2", "image.gif", `{"x_offset": 15, "y_offset": 10, "hyperlink": "https://github.com/xuri/excelize", "hyperlink_type": "External", "print_obj": true, "lock_aspect_ratio": false, "locked": false, "positioning": "oneCell"}`); err != nil {
  68. // fmt.Println(err)
  69. // }
  70. // if err := f.SaveAs("Book1.xlsx"); err != nil {
  71. // fmt.Println(err)
  72. // }
  73. // }
  74. //
  75. // LinkType defines two types of hyperlink "External" for web site or
  76. // "Location" for moving to one of cell in this workbook. When the
  77. // "hyperlink_type" is "Location", coordinates need to start with "#".
  78. //
  79. // Positioning defines two types of the position of a picture in an Excel
  80. // spreadsheet, "oneCell" (Move but don't size with cells) or "absolute"
  81. // (Don't move or size with cells). If you don't set this parameter, default
  82. // positioning is move and size with cells.
  83. func (f *File) AddPicture(sheet, cell, picture, format string) error {
  84. var err error
  85. // Check picture exists first.
  86. if _, err = os.Stat(picture); os.IsNotExist(err) {
  87. return err
  88. }
  89. ext, ok := supportImageTypes[path.Ext(picture)]
  90. if !ok {
  91. return ErrImgExt
  92. }
  93. file, _ := ioutil.ReadFile(picture)
  94. _, name := filepath.Split(picture)
  95. return f.AddPictureFromBytes(sheet, cell, format, name, ext, file)
  96. }
  97. // AddPictureFromBytes provides the method to add picture in a sheet by given
  98. // picture format set (such as offset, scale, aspect ratio setting and print
  99. // settings), file base name, extension name and file bytes. For example:
  100. //
  101. // package main
  102. //
  103. // import (
  104. // "fmt"
  105. // _ "image/jpeg"
  106. // "io/ioutil"
  107. //
  108. // "github.com/xuri/excelize/v2"
  109. // )
  110. //
  111. // func main() {
  112. // f := excelize.NewFile()
  113. //
  114. // file, err := ioutil.ReadFile("image.jpg")
  115. // if err != nil {
  116. // fmt.Println(err)
  117. // }
  118. // if err := f.AddPictureFromBytes("Sheet1", "A2", "", "Excel Logo", ".jpg", file); err != nil {
  119. // fmt.Println(err)
  120. // }
  121. // if err := f.SaveAs("Book1.xlsx"); err != nil {
  122. // fmt.Println(err)
  123. // }
  124. // }
  125. //
  126. func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, file []byte) error {
  127. var drawingHyperlinkRID int
  128. var hyperlinkType string
  129. ext, ok := supportImageTypes[extension]
  130. if !ok {
  131. return ErrImgExt
  132. }
  133. formatSet, err := parseFormatPictureSet(format)
  134. if err != nil {
  135. return err
  136. }
  137. img, _, err := image.DecodeConfig(bytes.NewReader(file))
  138. if err != nil {
  139. return err
  140. }
  141. // Read sheet data.
  142. ws, err := f.workSheetReader(sheet)
  143. if err != nil {
  144. return err
  145. }
  146. // Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
  147. drawingID := f.countDrawings() + 1
  148. drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
  149. drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
  150. drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
  151. mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl")
  152. drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
  153. // Add picture with hyperlink.
  154. if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" {
  155. if formatSet.HyperlinkType == "External" {
  156. hyperlinkType = formatSet.HyperlinkType
  157. }
  158. drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
  159. }
  160. err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet)
  161. if err != nil {
  162. return err
  163. }
  164. f.addContentTypePart(drawingID, "drawings")
  165. f.addSheetNameSpace(sheet, SourceRelationship)
  166. return err
  167. }
  168. // deleteSheetRelationships provides a function to delete relationships in
  169. // xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
  170. // relationship index.
  171. func (f *File) deleteSheetRelationships(sheet, rID string) {
  172. name, ok := f.sheetMap[trimSheetName(sheet)]
  173. if !ok {
  174. name = strings.ToLower(sheet) + ".xml"
  175. }
  176. var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
  177. sheetRels := f.relsReader(rels)
  178. if sheetRels == nil {
  179. sheetRels = &xlsxRelationships{}
  180. }
  181. sheetRels.Lock()
  182. defer sheetRels.Unlock()
  183. for k, v := range sheetRels.Relationships {
  184. if v.ID == rID {
  185. sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...)
  186. }
  187. }
  188. f.Relationships.Store(rels, sheetRels)
  189. }
  190. // addSheetLegacyDrawing provides a function to add legacy drawing element to
  191. // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
  192. func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
  193. xlsx, _ := f.workSheetReader(sheet)
  194. xlsx.LegacyDrawing = &xlsxLegacyDrawing{
  195. RID: "rId" + strconv.Itoa(rID),
  196. }
  197. }
  198. // addSheetDrawing provides a function to add drawing element to
  199. // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
  200. func (f *File) addSheetDrawing(sheet string, rID int) {
  201. xlsx, _ := f.workSheetReader(sheet)
  202. xlsx.Drawing = &xlsxDrawing{
  203. RID: "rId" + strconv.Itoa(rID),
  204. }
  205. }
  206. // addSheetPicture provides a function to add picture element to
  207. // xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
  208. func (f *File) addSheetPicture(sheet string, rID int) {
  209. xlsx, _ := f.workSheetReader(sheet)
  210. xlsx.Picture = &xlsxPicture{
  211. RID: "rId" + strconv.Itoa(rID),
  212. }
  213. }
  214. // countDrawings provides a function to get drawing files count storage in the
  215. // folder xl/drawings.
  216. func (f *File) countDrawings() int {
  217. c1, c2 := 0, 0
  218. f.Pkg.Range(func(k, v interface{}) bool {
  219. if strings.Contains(k.(string), "xl/drawings/drawing") {
  220. c1++
  221. }
  222. return true
  223. })
  224. f.Drawings.Range(func(rel, value interface{}) bool {
  225. if strings.Contains(rel.(string), "xl/drawings/drawing") {
  226. c2++
  227. }
  228. return true
  229. })
  230. if c1 < c2 {
  231. return c2
  232. }
  233. return c1
  234. }
  235. // addDrawingPicture provides a function to add picture by given sheet,
  236. // drawingXML, cell, file name, width, height relationship index and format
  237. // sets.
  238. func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) error {
  239. col, row, err := CellNameToCoordinates(cell)
  240. if err != nil {
  241. return err
  242. }
  243. if formatSet.Autofit {
  244. width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), formatSet)
  245. if err != nil {
  246. return err
  247. }
  248. } else {
  249. width = int(float64(width) * formatSet.XScale)
  250. height = int(float64(height) * formatSet.YScale)
  251. }
  252. col--
  253. row--
  254. colStart, rowStart, colEnd, rowEnd, x2, y2 :=
  255. f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height)
  256. content, cNvPrID := f.drawingParser(drawingXML)
  257. twoCellAnchor := xdrCellAnchor{}
  258. twoCellAnchor.EditAs = formatSet.Positioning
  259. from := xlsxFrom{}
  260. from.Col = colStart
  261. from.ColOff = formatSet.OffsetX * EMU
  262. from.Row = rowStart
  263. from.RowOff = formatSet.OffsetY * EMU
  264. to := xlsxTo{}
  265. to.Col = colEnd
  266. to.ColOff = x2 * EMU
  267. to.Row = rowEnd
  268. to.RowOff = y2 * EMU
  269. twoCellAnchor.From = &from
  270. twoCellAnchor.To = &to
  271. pic := xlsxPic{}
  272. pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = formatSet.NoChangeAspect
  273. pic.NvPicPr.CNvPr.ID = cNvPrID
  274. pic.NvPicPr.CNvPr.Descr = file
  275. pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
  276. if hyperlinkRID != 0 {
  277. pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{
  278. R: SourceRelationship.Value,
  279. RID: "rId" + strconv.Itoa(hyperlinkRID),
  280. }
  281. }
  282. pic.BlipFill.Blip.R = SourceRelationship.Value
  283. pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID)
  284. pic.SpPr.PrstGeom.Prst = "rect"
  285. twoCellAnchor.Pic = &pic
  286. twoCellAnchor.ClientData = &xdrClientData{
  287. FLocksWithSheet: formatSet.FLocksWithSheet,
  288. FPrintsWithSheet: formatSet.FPrintsWithSheet,
  289. }
  290. content.Lock()
  291. defer content.Unlock()
  292. content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
  293. f.Drawings.Store(drawingXML, content)
  294. return err
  295. }
  296. // countMedia provides a function to get media files count storage in the
  297. // folder xl/media/image.
  298. func (f *File) countMedia() int {
  299. count := 0
  300. f.Pkg.Range(func(k, v interface{}) bool {
  301. if strings.Contains(k.(string), "xl/media/image") {
  302. count++
  303. }
  304. return true
  305. })
  306. return count
  307. }
  308. // addMedia provides a function to add a picture into folder xl/media/image by
  309. // given file and extension name. Duplicate images are only actually stored once
  310. // and drawings that use it will reference the same image.
  311. func (f *File) addMedia(file []byte, ext string) string {
  312. count := f.countMedia()
  313. var name string
  314. f.Pkg.Range(func(k, existing interface{}) bool {
  315. if !strings.HasPrefix(k.(string), "xl/media/image") {
  316. return true
  317. }
  318. if bytes.Equal(file, existing.([]byte)) {
  319. name = k.(string)
  320. return false
  321. }
  322. return true
  323. })
  324. if name != "" {
  325. return name
  326. }
  327. media := "xl/media/image" + strconv.Itoa(count+1) + ext
  328. f.Pkg.Store(media, file)
  329. return media
  330. }
  331. // setContentTypePartImageExtensions provides a function to set the content
  332. // type for relationship parts and the Main Document part.
  333. func (f *File) setContentTypePartImageExtensions() {
  334. var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false, "tiff": false}
  335. content := f.contentTypesReader()
  336. content.Lock()
  337. defer content.Unlock()
  338. for _, v := range content.Defaults {
  339. _, ok := imageTypes[v.Extension]
  340. if ok {
  341. imageTypes[v.Extension] = true
  342. }
  343. }
  344. for k, v := range imageTypes {
  345. if !v {
  346. content.Defaults = append(content.Defaults, xlsxDefault{
  347. Extension: k,
  348. ContentType: "image/" + k,
  349. })
  350. }
  351. }
  352. }
  353. // setContentTypePartVMLExtensions provides a function to set the content type
  354. // for relationship parts and the Main Document part.
  355. func (f *File) setContentTypePartVMLExtensions() {
  356. vml := false
  357. content := f.contentTypesReader()
  358. content.Lock()
  359. defer content.Unlock()
  360. for _, v := range content.Defaults {
  361. if v.Extension == "vml" {
  362. vml = true
  363. }
  364. }
  365. if !vml {
  366. content.Defaults = append(content.Defaults, xlsxDefault{
  367. Extension: "vml",
  368. ContentType: ContentTypeVML,
  369. })
  370. }
  371. }
  372. // addContentTypePart provides a function to add content type part
  373. // relationships in the file [Content_Types].xml by given index.
  374. func (f *File) addContentTypePart(index int, contentType string) {
  375. setContentType := map[string]func(){
  376. "comments": f.setContentTypePartVMLExtensions,
  377. "drawings": f.setContentTypePartImageExtensions,
  378. }
  379. partNames := map[string]string{
  380. "chart": "/xl/charts/chart" + strconv.Itoa(index) + ".xml",
  381. "chartsheet": "/xl/chartsheets/sheet" + strconv.Itoa(index) + ".xml",
  382. "comments": "/xl/comments" + strconv.Itoa(index) + ".xml",
  383. "drawings": "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml",
  384. "table": "/xl/tables/table" + strconv.Itoa(index) + ".xml",
  385. "pivotTable": "/xl/pivotTables/pivotTable" + strconv.Itoa(index) + ".xml",
  386. "pivotCache": "/xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(index) + ".xml",
  387. "sharedStrings": "/xl/sharedStrings.xml",
  388. }
  389. contentTypes := map[string]string{
  390. "chart": ContentTypeDrawingML,
  391. "chartsheet": ContentTypeSpreadSheetMLChartsheet,
  392. "comments": ContentTypeSpreadSheetMLComments,
  393. "drawings": ContentTypeDrawing,
  394. "table": ContentTypeSpreadSheetMLTable,
  395. "pivotTable": ContentTypeSpreadSheetMLPivotTable,
  396. "pivotCache": ContentTypeSpreadSheetMLPivotCacheDefinition,
  397. "sharedStrings": ContentTypeSpreadSheetMLSharedStrings,
  398. }
  399. s, ok := setContentType[contentType]
  400. if ok {
  401. s()
  402. }
  403. content := f.contentTypesReader()
  404. content.Lock()
  405. defer content.Unlock()
  406. for _, v := range content.Overrides {
  407. if v.PartName == partNames[contentType] {
  408. return
  409. }
  410. }
  411. content.Overrides = append(content.Overrides, xlsxOverride{
  412. PartName: partNames[contentType],
  413. ContentType: contentTypes[contentType],
  414. })
  415. }
  416. // getSheetRelationshipsTargetByID provides a function to get Target attribute
  417. // value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
  418. // relationship index.
  419. func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
  420. name, ok := f.sheetMap[trimSheetName(sheet)]
  421. if !ok {
  422. name = strings.ToLower(sheet) + ".xml"
  423. }
  424. var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
  425. sheetRels := f.relsReader(rels)
  426. if sheetRels == nil {
  427. sheetRels = &xlsxRelationships{}
  428. }
  429. sheetRels.Lock()
  430. defer sheetRels.Unlock()
  431. for _, v := range sheetRels.Relationships {
  432. if v.ID == rID {
  433. return v.Target
  434. }
  435. }
  436. return ""
  437. }
  438. // GetPicture provides a function to get picture base name and raw content
  439. // embed in XLSX by given worksheet and cell name. This function returns the
  440. // file name in XLSX and file contents as []byte data types. For example:
  441. //
  442. // f, err := excelize.OpenFile("Book1.xlsx")
  443. // if err != nil {
  444. // fmt.Println(err)
  445. // return
  446. // }
  447. // file, raw, err := f.GetPicture("Sheet1", "A2")
  448. // if err != nil {
  449. // fmt.Println(err)
  450. // return
  451. // }
  452. // if err := ioutil.WriteFile(file, raw, 0644); err != nil {
  453. // fmt.Println(err)
  454. // }
  455. //
  456. func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
  457. col, row, err := CellNameToCoordinates(cell)
  458. if err != nil {
  459. return "", nil, err
  460. }
  461. col--
  462. row--
  463. ws, err := f.workSheetReader(sheet)
  464. if err != nil {
  465. return "", nil, err
  466. }
  467. if ws.Drawing == nil {
  468. return "", nil, err
  469. }
  470. target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
  471. drawingXML := strings.Replace(target, "..", "xl", -1)
  472. if _, ok := f.Pkg.Load(drawingXML); !ok {
  473. return "", nil, err
  474. }
  475. drawingRelationships := strings.Replace(
  476. strings.Replace(target, "../drawings", "xl/drawings/_rels", -1), ".xml", ".xml.rels", -1)
  477. return f.getPicture(row, col, drawingXML, drawingRelationships)
  478. }
  479. // DeletePicture provides a function to delete charts in spreadsheet by given
  480. // worksheet and cell name. Note that the image file won't be deleted from the
  481. // document currently.
  482. func (f *File) DeletePicture(sheet, cell string) (err error) {
  483. col, row, err := CellNameToCoordinates(cell)
  484. if err != nil {
  485. return
  486. }
  487. col--
  488. row--
  489. ws, err := f.workSheetReader(sheet)
  490. if err != nil {
  491. return
  492. }
  493. if ws.Drawing == nil {
  494. return
  495. }
  496. drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1)
  497. return f.deleteDrawing(col, row, drawingXML, "Pic")
  498. }
  499. // getPicture provides a function to get picture base name and raw content
  500. // embed in spreadsheet by given coordinates and drawing relationships.
  501. func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) {
  502. var (
  503. wsDr *xlsxWsDr
  504. ok bool
  505. deWsDr *decodeWsDr
  506. drawRel *xlsxRelationship
  507. deTwoCellAnchor *decodeTwoCellAnchor
  508. )
  509. wsDr, _ = f.drawingParser(drawingXML)
  510. if ret, buf = f.getPictureFromWsDr(row, col, drawingRelationships, wsDr); len(buf) > 0 {
  511. return
  512. }
  513. deWsDr = new(decodeWsDr)
  514. if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))).
  515. Decode(deWsDr); err != nil && err != io.EOF {
  516. err = fmt.Errorf("xml decode error: %s", err)
  517. return
  518. }
  519. err = nil
  520. for _, anchor := range deWsDr.TwoCellAnchor {
  521. deTwoCellAnchor = new(decodeTwoCellAnchor)
  522. if err = f.xmlNewDecoder(strings.NewReader("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>")).
  523. Decode(deTwoCellAnchor); err != nil && err != io.EOF {
  524. err = fmt.Errorf("xml decode error: %s", err)
  525. return
  526. }
  527. if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic != nil {
  528. if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
  529. drawRel = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed)
  530. if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
  531. ret = filepath.Base(drawRel.Target)
  532. if buffer, _ := f.Pkg.Load(strings.Replace(drawRel.Target, "..", "xl", -1)); buffer != nil {
  533. buf = buffer.([]byte)
  534. }
  535. return
  536. }
  537. }
  538. }
  539. }
  540. return
  541. }
  542. // getPictureFromWsDr provides a function to get picture base name and raw
  543. // content in worksheet drawing by given coordinates and drawing
  544. // relationships.
  545. func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsDr *xlsxWsDr) (ret string, buf []byte) {
  546. var (
  547. ok bool
  548. anchor *xdrCellAnchor
  549. drawRel *xlsxRelationship
  550. )
  551. wsDr.Lock()
  552. defer wsDr.Unlock()
  553. for _, anchor = range wsDr.TwoCellAnchor {
  554. if anchor.From != nil && anchor.Pic != nil {
  555. if anchor.From.Col == col && anchor.From.Row == row {
  556. if drawRel = f.getDrawingRelationships(drawingRelationships,
  557. anchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
  558. if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
  559. ret = filepath.Base(drawRel.Target)
  560. if buffer, _ := f.Pkg.Load(strings.Replace(drawRel.Target, "..", "xl", -1)); buffer != nil {
  561. buf = buffer.([]byte)
  562. }
  563. return
  564. }
  565. }
  566. }
  567. }
  568. }
  569. return
  570. }
  571. // getDrawingRelationships provides a function to get drawing relationships
  572. // from xl/drawings/_rels/drawing%s.xml.rels by given file name and
  573. // relationship ID.
  574. func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {
  575. if drawingRels := f.relsReader(rels); drawingRels != nil {
  576. drawingRels.Lock()
  577. defer drawingRels.Unlock()
  578. for _, v := range drawingRels.Relationships {
  579. if v.ID == rID {
  580. return &v
  581. }
  582. }
  583. }
  584. return nil
  585. }
  586. // drawingsWriter provides a function to save xl/drawings/drawing%d.xml after
  587. // serialize structure.
  588. func (f *File) drawingsWriter() {
  589. f.Drawings.Range(func(path, d interface{}) bool {
  590. if d != nil {
  591. v, _ := xml.Marshal(d.(*xlsxWsDr))
  592. f.saveFileList(path.(string), v)
  593. }
  594. return true
  595. })
  596. }
  597. // drawingResize calculate the height and width after resizing.
  598. func (f *File) drawingResize(sheet string, cell string, width, height float64, formatSet *formatPicture) (w, h, c, r int, err error) {
  599. var mergeCells []MergeCell
  600. mergeCells, err = f.GetMergeCells(sheet)
  601. if err != nil {
  602. return
  603. }
  604. var rng []int
  605. var inMergeCell bool
  606. if c, r, err = CellNameToCoordinates(cell); err != nil {
  607. return
  608. }
  609. cellWidth, cellHeight := f.getColWidth(sheet, c), f.getRowHeight(sheet, r)
  610. for _, mergeCell := range mergeCells {
  611. if inMergeCell {
  612. continue
  613. }
  614. if inMergeCell, err = f.checkCellInArea(cell, mergeCell[0]); err != nil {
  615. return
  616. }
  617. if inMergeCell {
  618. rng, _ = areaRangeToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis())
  619. _ = sortCoordinates(rng)
  620. }
  621. }
  622. if inMergeCell {
  623. cellWidth, cellHeight = 0, 0
  624. c, r = rng[0], rng[1]
  625. for col := rng[0]; col <= rng[2]; col++ {
  626. cellWidth += f.getColWidth(sheet, col)
  627. }
  628. for row := rng[1]; row <= rng[3]; row++ {
  629. cellHeight += f.getRowHeight(sheet, row)
  630. }
  631. }
  632. if float64(cellWidth) < width {
  633. asp := float64(cellWidth) / width
  634. width, height = float64(cellWidth), height*asp
  635. }
  636. if float64(cellHeight) < height {
  637. asp := float64(cellHeight) / height
  638. height, width = float64(cellHeight), width*asp
  639. }
  640. width, height = width-float64(formatSet.OffsetX), height-float64(formatSet.OffsetY)
  641. w, h = int(width*formatSet.XScale), int(height*formatSet.YScale)
  642. return
  643. }