2021-02-15 00:09:35 +08:00
|
|
|
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
|
2018-09-14 00:44:23 +08:00
|
|
|
// this source code is governed by a BSD-style license that can be found in
|
|
|
|
// the LICENSE file.
|
|
|
|
//
|
2018-09-12 15:47:56 +08:00
|
|
|
// Package excelize providing a set of functions that allow you to write to
|
2020-06-22 00:14:56 +08:00
|
|
|
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
|
2021-03-30 23:02:22 +08:00
|
|
|
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
|
2020-06-22 00:14:56 +08:00
|
|
|
// complex components by high compatibility, and provided streaming API for
|
|
|
|
// generating or reading data from a worksheet with huge amounts of data. This
|
2021-04-04 15:29:43 +08:00
|
|
|
// library needs Go version 1.15 or later.
|
2018-09-14 00:58:48 +08:00
|
|
|
|
2017-01-17 19:06:42 +08:00
|
|
|
package excelize
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-02-28 12:59:04 +08:00
|
|
|
"encoding/json"
|
2017-01-17 19:06:42 +08:00
|
|
|
"encoding/xml"
|
2019-12-20 00:30:48 +08:00
|
|
|
"fmt"
|
2017-01-22 16:16:03 +08:00
|
|
|
"image"
|
2019-12-20 00:30:48 +08:00
|
|
|
"io"
|
2017-01-17 19:06:42 +08:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// parseFormatPictureSet provides a function to parse the format settings of
|
|
|
|
// the picture with default value.
|
2018-05-27 11:25:55 +08:00
|
|
|
func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
|
2017-03-06 12:05:41 +08:00
|
|
|
format := formatPicture{
|
2017-02-28 12:59:04 +08:00
|
|
|
FPrintsWithSheet: true,
|
|
|
|
FLocksWithSheet: false,
|
|
|
|
NoChangeAspect: false,
|
2020-05-17 17:36:53 +08:00
|
|
|
Autofit: false,
|
2017-02-28 12:59:04 +08:00
|
|
|
OffsetX: 0,
|
|
|
|
OffsetY: 0,
|
|
|
|
XScale: 1.0,
|
|
|
|
YScale: 1.0,
|
|
|
|
}
|
2018-07-13 17:40:47 +08:00
|
|
|
err := json.Unmarshal(parseFormatSet(formatSet), &format)
|
2018-05-27 11:25:55 +08:00
|
|
|
return &format, err
|
2017-02-28 12:59:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddPicture provides the method to add picture in a sheet by given picture
|
|
|
|
// format set (such as offset, scale, aspect ratio setting and print settings)
|
|
|
|
// and file path. For example:
|
2017-01-17 19:06:42 +08:00
|
|
|
//
|
2017-01-22 19:20:33 +08:00
|
|
|
// package main
|
2017-01-22 16:16:03 +08:00
|
|
|
//
|
2017-01-22 19:20:33 +08:00
|
|
|
// import (
|
|
|
|
// _ "image/gif"
|
|
|
|
// _ "image/jpeg"
|
|
|
|
// _ "image/png"
|
2017-01-22 16:16:03 +08:00
|
|
|
//
|
2021-07-28 00:38:09 +08:00
|
|
|
// "github.com/xuri/excelize/v2"
|
2017-01-22 19:20:33 +08:00
|
|
|
// )
|
2017-01-22 16:16:03 +08:00
|
|
|
//
|
2017-01-22 19:20:33 +08:00
|
|
|
// func main() {
|
2019-04-20 14:57:50 +08:00
|
|
|
// f := excelize.NewFile()
|
2017-01-22 16:16:03 +08:00
|
|
|
// // Insert a picture.
|
2019-12-31 01:01:16 +08:00
|
|
|
// if err := f.AddPicture("Sheet1", "A2", "image.jpg", ""); err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2017-02-12 19:03:24 +08:00
|
|
|
// }
|
2018-02-03 15:02:37 +08:00
|
|
|
// // Insert a picture scaling in the cell with location hyperlink.
|
2019-12-31 01:01:16 +08:00
|
|
|
// if err := f.AddPicture("Sheet1", "D2", "image.png", `{"x_scale": 0.5, "y_scale": 0.5, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`); err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2017-02-12 19:03:24 +08:00
|
|
|
// }
|
2018-04-26 11:41:13 +08:00
|
|
|
// // Insert a picture offset in the cell with external hyperlink, printing and positioning support.
|
2021-07-28 00:38:09 +08:00
|
|
|
// 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 {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2017-01-22 16:16:03 +08:00
|
|
|
// }
|
2019-12-31 01:01:16 +08:00
|
|
|
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2017-01-22 16:16:03 +08:00
|
|
|
// }
|
2017-01-17 19:06:42 +08:00
|
|
|
// }
|
|
|
|
//
|
2021-08-26 00:48:18 +08:00
|
|
|
// The optional parameter "autofit" specifies if make image size auto fits the
|
|
|
|
// cell, the default value of that is 'false'.
|
|
|
|
//
|
|
|
|
// The optional parameter "hyperlink" specifies the hyperlink of the image.
|
|
|
|
//
|
|
|
|
// The optional parameter "hyperlink_type" defines two types of
|
|
|
|
// hyperlink "External" for website or "Location" for moving to one of the
|
|
|
|
// cells in this workbook. When the "hyperlink_type" is "Location",
|
|
|
|
// coordinates need to start with "#".
|
|
|
|
//
|
|
|
|
// The optional parameter "positioning" defines two types of the position of a
|
|
|
|
// image in an Excel spreadsheet, "oneCell" (Move but don't size with
|
|
|
|
// cells) or "absolute" (Don't move or size with cells). If you don't set this
|
|
|
|
// parameter, the default positioning is move and size with cells.
|
|
|
|
//
|
|
|
|
// The optional parameter "print_obj" indicates whether the image is printed
|
|
|
|
// when the worksheet is printed, the default value of that is 'true'.
|
|
|
|
//
|
|
|
|
// The optional parameter "lock_aspect_ratio" indicates whether lock aspect
|
|
|
|
// ratio for the image, the default value of that is 'false'.
|
|
|
|
//
|
|
|
|
// The optional parameter "locked" indicates whether lock the image. Locking
|
|
|
|
// an object has no effect unless the sheet is protected.
|
|
|
|
//
|
|
|
|
// The optional parameter "x_offset" specifies the horizontal offset of the
|
|
|
|
// image with the cell, the default value of that is 0.
|
|
|
|
//
|
|
|
|
// The optional parameter "x_scale" specifies the horizontal scale of images,
|
|
|
|
// the default value of that is 1.0 which presents 100%.
|
|
|
|
//
|
|
|
|
// The optional parameter "y_offset" specifies the vertical offset of the
|
|
|
|
// image with the cell, the default value of that is 0.
|
|
|
|
//
|
|
|
|
// The optional parameter "y_scale" specifies the vertical scale of images,
|
|
|
|
// the default value of that is 1.0 which presents 100%.
|
|
|
|
//
|
2017-02-28 12:59:04 +08:00
|
|
|
func (f *File) AddPicture(sheet, cell, picture, format string) error {
|
2017-01-17 19:06:42 +08:00
|
|
|
var err error
|
|
|
|
// Check picture exists first.
|
|
|
|
if _, err = os.Stat(picture); os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
2017-03-28 21:18:06 +08:00
|
|
|
ext, ok := supportImageTypes[path.Ext(picture)]
|
2017-01-17 19:06:42 +08:00
|
|
|
if !ok {
|
2021-05-10 00:09:24 +08:00
|
|
|
return ErrImgExt
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
2021-08-15 00:06:40 +08:00
|
|
|
file, _ := ioutil.ReadFile(filepath.Clean(picture))
|
2018-09-14 00:24:49 +08:00
|
|
|
_, name := filepath.Split(picture)
|
|
|
|
return f.AddPictureFromBytes(sheet, cell, format, name, ext, file)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddPictureFromBytes provides the method to add picture in a sheet by given
|
|
|
|
// picture format set (such as offset, scale, aspect ratio setting and print
|
|
|
|
// settings), file base name, extension name and file bytes. For example:
|
|
|
|
//
|
|
|
|
// package main
|
|
|
|
//
|
|
|
|
// import (
|
2020-02-19 00:08:10 +08:00
|
|
|
// "fmt"
|
2018-09-14 00:24:49 +08:00
|
|
|
// _ "image/jpeg"
|
|
|
|
// "io/ioutil"
|
|
|
|
//
|
2021-07-28 00:38:09 +08:00
|
|
|
// "github.com/xuri/excelize/v2"
|
2018-09-14 00:24:49 +08:00
|
|
|
// )
|
|
|
|
//
|
|
|
|
// func main() {
|
2019-04-20 14:57:50 +08:00
|
|
|
// f := excelize.NewFile()
|
2018-09-14 00:24:49 +08:00
|
|
|
//
|
2019-12-31 01:01:16 +08:00
|
|
|
// file, err := ioutil.ReadFile("image.jpg")
|
2018-09-14 00:24:49 +08:00
|
|
|
// if err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2018-09-14 00:24:49 +08:00
|
|
|
// }
|
2019-12-31 01:01:16 +08:00
|
|
|
// if err := f.AddPictureFromBytes("Sheet1", "A2", "", "Excel Logo", ".jpg", file); err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2018-09-14 00:24:49 +08:00
|
|
|
// }
|
2019-12-31 01:01:16 +08:00
|
|
|
// if err := f.SaveAs("Book1.xlsx"); err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2018-09-14 00:24:49 +08:00
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, file []byte) error {
|
|
|
|
var drawingHyperlinkRID int
|
|
|
|
var hyperlinkType string
|
|
|
|
ext, ok := supportImageTypes[extension]
|
|
|
|
if !ok {
|
2021-05-10 00:09:24 +08:00
|
|
|
return ErrImgExt
|
2018-09-14 00:24:49 +08:00
|
|
|
}
|
|
|
|
formatSet, err := parseFormatPictureSet(format)
|
2018-09-12 15:47:56 +08:00
|
|
|
if err != nil {
|
2018-08-20 16:53:51 +08:00
|
|
|
return err
|
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
img, _, err := image.DecodeConfig(bytes.NewReader(file))
|
2018-05-27 11:25:55 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-01-17 19:06:42 +08:00
|
|
|
// Read sheet data.
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-08-13 01:32:44 +08:00
|
|
|
ws.Lock()
|
2017-01-17 19:06:42 +08:00
|
|
|
// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
|
|
|
|
drawingID := f.countDrawings() + 1
|
|
|
|
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
|
2020-11-10 23:48:09 +08:00
|
|
|
drawingID, drawingXML = f.prepareDrawing(ws, drawingID, sheet, drawingXML)
|
2019-09-16 01:17:35 +08:00
|
|
|
drawingRels := "xl/drawings/_rels/drawing" + strconv.Itoa(drawingID) + ".xml.rels"
|
2019-03-24 09:07:57 +08:00
|
|
|
mediaStr := ".." + strings.TrimPrefix(f.addMedia(file, ext), "xl")
|
2019-09-16 01:17:35 +08:00
|
|
|
drawingRID := f.addRels(drawingRels, SourceRelationshipImage, mediaStr, hyperlinkType)
|
2018-02-03 15:02:37 +08:00
|
|
|
// Add picture with hyperlink.
|
|
|
|
if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" {
|
|
|
|
if formatSet.HyperlinkType == "External" {
|
|
|
|
hyperlinkType = formatSet.HyperlinkType
|
|
|
|
}
|
2019-09-16 01:17:35 +08:00
|
|
|
drawingHyperlinkRID = f.addRels(drawingRels, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
|
2018-02-03 15:02:37 +08:00
|
|
|
}
|
2021-08-13 01:32:44 +08:00
|
|
|
ws.Unlock()
|
2019-03-23 20:08:06 +08:00
|
|
|
err = f.addDrawingPicture(sheet, drawingXML, cell, name, img.Width, img.Height, drawingRID, drawingHyperlinkRID, formatSet)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-05-24 14:17:35 +08:00
|
|
|
f.addContentTypePart(drawingID, "drawings")
|
2020-07-18 15:15:16 +08:00
|
|
|
f.addSheetNameSpace(sheet, SourceRelationship)
|
2017-01-17 19:06:42 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// deleteSheetRelationships provides a function to delete relationships in
|
|
|
|
// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
|
|
|
|
// relationship index.
|
2017-07-24 10:26:02 +08:00
|
|
|
func (f *File) deleteSheetRelationships(sheet, rID string) {
|
2017-10-20 14:40:57 +08:00
|
|
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
|
|
|
if !ok {
|
|
|
|
name = strings.ToLower(sheet) + ".xml"
|
|
|
|
}
|
|
|
|
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
2019-09-16 01:17:35 +08:00
|
|
|
sheetRels := f.relsReader(rels)
|
2019-02-26 14:21:44 +08:00
|
|
|
if sheetRels == nil {
|
2019-09-16 01:17:35 +08:00
|
|
|
sheetRels = &xlsxRelationships{}
|
2019-02-26 14:21:44 +08:00
|
|
|
}
|
2021-07-06 00:31:04 +08:00
|
|
|
sheetRels.Lock()
|
|
|
|
defer sheetRels.Unlock()
|
2017-07-24 10:26:02 +08:00
|
|
|
for k, v := range sheetRels.Relationships {
|
2017-09-30 17:07:59 +08:00
|
|
|
if v.ID == rID {
|
|
|
|
sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
}
|
2021-07-04 12:13:06 +08:00
|
|
|
f.Relationships.Store(rels, sheetRels)
|
2017-07-24 10:26:02 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// addSheetLegacyDrawing provides a function to add legacy drawing element to
|
2017-09-13 22:00:33 +08:00
|
|
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
2017-05-13 13:28:21 +08:00
|
|
|
func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
|
2021-08-15 00:06:40 +08:00
|
|
|
ws, _ := f.workSheetReader(sheet)
|
|
|
|
ws.LegacyDrawing = &xlsxLegacyDrawing{
|
2017-05-13 13:28:21 +08:00
|
|
|
RID: "rId" + strconv.Itoa(rID),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// addSheetDrawing provides a function to add drawing element to
|
2017-09-13 22:00:33 +08:00
|
|
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
2017-01-17 19:06:42 +08:00
|
|
|
func (f *File) addSheetDrawing(sheet string, rID int) {
|
2021-08-15 00:06:40 +08:00
|
|
|
ws, _ := f.workSheetReader(sheet)
|
|
|
|
ws.Drawing = &xlsxDrawing{
|
2017-01-17 19:06:42 +08:00
|
|
|
RID: "rId" + strconv.Itoa(rID),
|
|
|
|
}
|
2017-01-24 18:29:02 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// addSheetPicture provides a function to add picture element to
|
2017-09-13 22:00:33 +08:00
|
|
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
2017-01-24 18:29:02 +08:00
|
|
|
func (f *File) addSheetPicture(sheet string, rID int) {
|
2021-08-15 00:06:40 +08:00
|
|
|
ws, _ := f.workSheetReader(sheet)
|
|
|
|
ws.Picture = &xlsxPicture{
|
2017-01-24 18:29:02 +08:00
|
|
|
RID: "rId" + strconv.Itoa(rID),
|
|
|
|
}
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// countDrawings provides a function to get drawing files count storage in the
|
2017-01-18 16:05:01 +08:00
|
|
|
// folder xl/drawings.
|
2021-09-10 23:19:59 +08:00
|
|
|
func (f *File) countDrawings() (count int) {
|
2021-07-05 00:03:56 +08:00
|
|
|
f.Pkg.Range(func(k, v interface{}) bool {
|
|
|
|
if strings.Contains(k.(string), "xl/drawings/drawing") {
|
2021-09-10 23:19:59 +08:00
|
|
|
count++
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
2021-07-05 00:03:56 +08:00
|
|
|
return true
|
|
|
|
})
|
2021-07-04 12:13:06 +08:00
|
|
|
f.Drawings.Range(func(rel, value interface{}) bool {
|
|
|
|
if strings.Contains(rel.(string), "xl/drawings/drawing") {
|
2021-09-10 23:19:59 +08:00
|
|
|
count++
|
2019-03-22 14:26:43 +08:00
|
|
|
}
|
2021-07-04 12:13:06 +08:00
|
|
|
return true
|
|
|
|
})
|
2021-09-10 23:19:59 +08:00
|
|
|
return
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// addDrawingPicture provides a function to add picture by given sheet,
|
2017-04-23 00:10:23 +08:00
|
|
|
// drawingXML, cell, file name, width, height relationship index and format
|
2017-05-24 14:17:35 +08:00
|
|
|
// sets.
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) error {
|
|
|
|
col, row, err := CellNameToCoordinates(cell)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-17 17:36:53 +08:00
|
|
|
if formatSet.Autofit {
|
|
|
|
width, height, col, row, err = f.drawingResize(sheet, cell, float64(width), float64(height), formatSet)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-08-18 16:30:32 +08:00
|
|
|
} else {
|
|
|
|
width = int(float64(width) * formatSet.XScale)
|
|
|
|
height = int(float64(height) * formatSet.YScale)
|
2020-05-17 17:36:53 +08:00
|
|
|
}
|
2019-03-20 16:52:33 +08:00
|
|
|
col--
|
|
|
|
row--
|
2020-10-18 00:01:33 +08:00
|
|
|
colStart, rowStart, colEnd, rowEnd, x2, y2 :=
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height)
|
2019-02-25 22:14:34 +08:00
|
|
|
content, cNvPrID := f.drawingParser(drawingXML)
|
2017-04-30 20:03:43 +08:00
|
|
|
twoCellAnchor := xdrCellAnchor{}
|
2018-04-26 11:41:13 +08:00
|
|
|
twoCellAnchor.EditAs = formatSet.Positioning
|
2017-01-17 19:06:42 +08:00
|
|
|
from := xlsxFrom{}
|
2017-01-22 16:16:03 +08:00
|
|
|
from.Col = colStart
|
2017-02-28 12:59:04 +08:00
|
|
|
from.ColOff = formatSet.OffsetX * EMU
|
2017-01-22 16:16:03 +08:00
|
|
|
from.Row = rowStart
|
2017-02-28 12:59:04 +08:00
|
|
|
from.RowOff = formatSet.OffsetY * EMU
|
2017-01-17 19:06:42 +08:00
|
|
|
to := xlsxTo{}
|
2017-01-22 16:16:03 +08:00
|
|
|
to.Col = colEnd
|
|
|
|
to.ColOff = x2 * EMU
|
|
|
|
to.Row = rowEnd
|
|
|
|
to.RowOff = y2 * EMU
|
2017-01-17 19:06:42 +08:00
|
|
|
twoCellAnchor.From = &from
|
|
|
|
twoCellAnchor.To = &to
|
|
|
|
pic := xlsxPic{}
|
2017-02-28 12:59:04 +08:00
|
|
|
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = formatSet.NoChangeAspect
|
2019-09-26 22:28:14 +08:00
|
|
|
pic.NvPicPr.CNvPr.ID = cNvPrID
|
2017-01-17 19:06:42 +08:00
|
|
|
pic.NvPicPr.CNvPr.Descr = file
|
|
|
|
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
|
2018-02-03 15:02:37 +08:00
|
|
|
if hyperlinkRID != 0 {
|
|
|
|
pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{
|
2020-07-18 15:15:16 +08:00
|
|
|
R: SourceRelationship.Value,
|
2018-02-03 15:02:37 +08:00
|
|
|
RID: "rId" + strconv.Itoa(hyperlinkRID),
|
|
|
|
}
|
|
|
|
}
|
2020-07-18 15:15:16 +08:00
|
|
|
pic.BlipFill.Blip.R = SourceRelationship.Value
|
2017-01-17 19:06:42 +08:00
|
|
|
pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID)
|
|
|
|
pic.SpPr.PrstGeom.Prst = "rect"
|
|
|
|
|
|
|
|
twoCellAnchor.Pic = &pic
|
2017-04-30 20:03:43 +08:00
|
|
|
twoCellAnchor.ClientData = &xdrClientData{
|
2017-02-28 12:59:04 +08:00
|
|
|
FLocksWithSheet: formatSet.FLocksWithSheet,
|
|
|
|
FPrintsWithSheet: formatSet.FPrintsWithSheet,
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
2021-07-06 00:31:04 +08:00
|
|
|
content.Lock()
|
|
|
|
defer content.Unlock()
|
2017-04-30 20:03:43 +08:00
|
|
|
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
|
2021-07-04 12:13:06 +08:00
|
|
|
f.Drawings.Store(drawingXML, content)
|
2019-03-23 20:08:06 +08:00
|
|
|
return err
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// countMedia provides a function to get media files count storage in the
|
|
|
|
// folder xl/media/image.
|
2017-01-17 19:06:42 +08:00
|
|
|
func (f *File) countMedia() int {
|
|
|
|
count := 0
|
2021-07-05 00:03:56 +08:00
|
|
|
f.Pkg.Range(func(k, v interface{}) bool {
|
|
|
|
if strings.Contains(k.(string), "xl/media/image") {
|
2017-01-17 19:06:42 +08:00
|
|
|
count++
|
|
|
|
}
|
2021-07-05 00:03:56 +08:00
|
|
|
return true
|
|
|
|
})
|
2017-01-17 19:06:42 +08:00
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
2019-03-24 09:07:57 +08:00
|
|
|
// addMedia provides a function to add a picture into folder xl/media/image by
|
|
|
|
// given file and extension name. Duplicate images are only actually stored once
|
|
|
|
// and drawings that use it will reference the same image.
|
|
|
|
func (f *File) addMedia(file []byte, ext string) string {
|
2017-01-17 19:06:42 +08:00
|
|
|
count := f.countMedia()
|
2021-07-05 00:03:56 +08:00
|
|
|
var name string
|
|
|
|
f.Pkg.Range(func(k, existing interface{}) bool {
|
|
|
|
if !strings.HasPrefix(k.(string), "xl/media/image") {
|
|
|
|
return true
|
2019-03-24 09:07:57 +08:00
|
|
|
}
|
2021-07-05 00:03:56 +08:00
|
|
|
if bytes.Equal(file, existing.([]byte)) {
|
|
|
|
name = k.(string)
|
|
|
|
return false
|
2019-03-24 09:07:57 +08:00
|
|
|
}
|
2021-07-05 00:03:56 +08:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
if name != "" {
|
|
|
|
return name
|
2019-03-24 09:07:57 +08:00
|
|
|
}
|
2017-01-17 19:06:42 +08:00
|
|
|
media := "xl/media/image" + strconv.Itoa(count+1) + ext
|
2021-07-05 00:03:56 +08:00
|
|
|
f.Pkg.Store(media, file)
|
2019-03-24 09:07:57 +08:00
|
|
|
return media
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// setContentTypePartImageExtensions provides a function to set the content
|
|
|
|
// type for relationship parts and the Main Document part.
|
2017-01-24 18:29:02 +08:00
|
|
|
func (f *File) setContentTypePartImageExtensions() {
|
2019-06-27 21:58:14 +08:00
|
|
|
var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false, "tiff": false}
|
2017-04-01 13:56:39 +08:00
|
|
|
content := f.contentTypesReader()
|
2021-07-06 00:31:04 +08:00
|
|
|
content.Lock()
|
|
|
|
defer content.Unlock()
|
2017-01-17 19:06:42 +08:00
|
|
|
for _, v := range content.Defaults {
|
|
|
|
_, ok := imageTypes[v.Extension]
|
|
|
|
if ok {
|
|
|
|
imageTypes[v.Extension] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k, v := range imageTypes {
|
|
|
|
if !v {
|
|
|
|
content.Defaults = append(content.Defaults, xlsxDefault{
|
|
|
|
Extension: k,
|
|
|
|
ContentType: "image/" + k,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2017-01-24 18:29:02 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// setContentTypePartVMLExtensions provides a function to set the content type
|
2017-05-13 13:28:21 +08:00
|
|
|
// for relationship parts and the Main Document part.
|
|
|
|
func (f *File) setContentTypePartVMLExtensions() {
|
|
|
|
vml := false
|
|
|
|
content := f.contentTypesReader()
|
2021-07-06 00:31:04 +08:00
|
|
|
content.Lock()
|
|
|
|
defer content.Unlock()
|
2017-05-13 13:28:21 +08:00
|
|
|
for _, v := range content.Defaults {
|
|
|
|
if v.Extension == "vml" {
|
|
|
|
vml = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !vml {
|
|
|
|
content.Defaults = append(content.Defaults, xlsxDefault{
|
|
|
|
Extension: "vml",
|
2020-03-28 23:47:26 +08:00
|
|
|
ContentType: ContentTypeVML,
|
2017-05-13 13:28:21 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// addContentTypePart provides a function to add content type part
|
|
|
|
// relationships in the file [Content_Types].xml by given index.
|
2017-05-24 14:17:35 +08:00
|
|
|
func (f *File) addContentTypePart(index int, contentType string) {
|
|
|
|
setContentType := map[string]func(){
|
|
|
|
"comments": f.setContentTypePartVMLExtensions,
|
|
|
|
"drawings": f.setContentTypePartImageExtensions,
|
|
|
|
}
|
|
|
|
partNames := map[string]string{
|
2020-04-06 00:23:27 +08:00
|
|
|
"chart": "/xl/charts/chart" + strconv.Itoa(index) + ".xml",
|
|
|
|
"chartsheet": "/xl/chartsheets/sheet" + strconv.Itoa(index) + ".xml",
|
|
|
|
"comments": "/xl/comments" + strconv.Itoa(index) + ".xml",
|
|
|
|
"drawings": "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml",
|
|
|
|
"table": "/xl/tables/table" + strconv.Itoa(index) + ".xml",
|
|
|
|
"pivotTable": "/xl/pivotTables/pivotTable" + strconv.Itoa(index) + ".xml",
|
|
|
|
"pivotCache": "/xl/pivotCache/pivotCacheDefinition" + strconv.Itoa(index) + ".xml",
|
|
|
|
"sharedStrings": "/xl/sharedStrings.xml",
|
2017-05-24 14:17:35 +08:00
|
|
|
}
|
|
|
|
contentTypes := map[string]string{
|
2020-04-06 00:23:27 +08:00
|
|
|
"chart": ContentTypeDrawingML,
|
|
|
|
"chartsheet": ContentTypeSpreadSheetMLChartsheet,
|
|
|
|
"comments": ContentTypeSpreadSheetMLComments,
|
|
|
|
"drawings": ContentTypeDrawing,
|
|
|
|
"table": ContentTypeSpreadSheetMLTable,
|
|
|
|
"pivotTable": ContentTypeSpreadSheetMLPivotTable,
|
|
|
|
"pivotCache": ContentTypeSpreadSheetMLPivotCacheDefinition,
|
|
|
|
"sharedStrings": ContentTypeSpreadSheetMLSharedStrings,
|
2017-05-24 14:17:35 +08:00
|
|
|
}
|
|
|
|
s, ok := setContentType[contentType]
|
|
|
|
if ok {
|
|
|
|
s()
|
2017-01-17 19:06:42 +08:00
|
|
|
}
|
2017-05-13 13:28:21 +08:00
|
|
|
content := f.contentTypesReader()
|
2021-07-06 00:31:04 +08:00
|
|
|
content.Lock()
|
|
|
|
defer content.Unlock()
|
2017-05-13 13:28:21 +08:00
|
|
|
for _, v := range content.Overrides {
|
2017-05-24 14:17:35 +08:00
|
|
|
if v.PartName == partNames[contentType] {
|
2017-05-13 13:28:21 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
2017-05-24 14:17:35 +08:00
|
|
|
PartName: partNames[contentType],
|
|
|
|
ContentType: contentTypes[contentType],
|
2017-05-13 13:28:21 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// getSheetRelationshipsTargetByID provides a function to get Target attribute
|
2017-09-13 22:00:33 +08:00
|
|
|
// value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
|
2017-01-18 16:05:01 +08:00
|
|
|
// relationship index.
|
2017-06-12 18:05:09 +08:00
|
|
|
func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
|
2017-10-31 16:33:36 +08:00
|
|
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
|
|
|
if !ok {
|
|
|
|
name = strings.ToLower(sheet) + ".xml"
|
|
|
|
}
|
|
|
|
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
2019-09-16 01:17:35 +08:00
|
|
|
sheetRels := f.relsReader(rels)
|
2019-02-26 14:21:44 +08:00
|
|
|
if sheetRels == nil {
|
2019-09-16 01:17:35 +08:00
|
|
|
sheetRels = &xlsxRelationships{}
|
2019-02-26 14:21:44 +08:00
|
|
|
}
|
2021-07-06 00:31:04 +08:00
|
|
|
sheetRels.Lock()
|
|
|
|
defer sheetRels.Unlock()
|
2017-01-17 19:06:42 +08:00
|
|
|
for _, v := range sheetRels.Relationships {
|
|
|
|
if v.ID == rID {
|
|
|
|
return v.Target
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2017-03-28 21:18:06 +08:00
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// GetPicture provides a function to get picture base name and raw content
|
2021-11-17 00:25:36 +08:00
|
|
|
// embed in spreadsheet by given worksheet and cell name. This function
|
|
|
|
// returns the file name in spreadsheet and file contents as []byte data
|
|
|
|
// types. For example:
|
2017-03-28 21:18:06 +08:00
|
|
|
//
|
2019-12-31 01:01:16 +08:00
|
|
|
// f, err := excelize.OpenFile("Book1.xlsx")
|
2017-03-28 21:18:06 +08:00
|
|
|
// if err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2017-10-20 14:40:57 +08:00
|
|
|
// return
|
2017-03-28 21:18:06 +08:00
|
|
|
// }
|
2021-11-17 00:25:36 +08:00
|
|
|
// defer func() {
|
|
|
|
// if err := f.Close(); err != nil {
|
|
|
|
// fmt.Println(err)
|
|
|
|
// }
|
|
|
|
// }()
|
2019-04-20 14:57:50 +08:00
|
|
|
// file, raw, err := f.GetPicture("Sheet1", "A2")
|
|
|
|
// if err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2017-10-20 14:40:57 +08:00
|
|
|
// return
|
2017-03-28 21:18:06 +08:00
|
|
|
// }
|
2019-12-31 01:01:16 +08:00
|
|
|
// if err := ioutil.WriteFile(file, raw, 0644); err != nil {
|
2020-02-19 00:08:10 +08:00
|
|
|
// fmt.Println(err)
|
2017-03-30 11:06:51 +08:00
|
|
|
// }
|
2017-03-28 21:18:06 +08:00
|
|
|
//
|
2019-03-23 20:08:06 +08:00
|
|
|
func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
|
|
|
|
col, row, err := CellNameToCoordinates(cell)
|
|
|
|
if err != nil {
|
2019-06-20 00:00:40 +08:00
|
|
|
return "", nil, err
|
2019-03-23 20:08:06 +08:00
|
|
|
}
|
2019-03-20 16:52:33 +08:00
|
|
|
col--
|
|
|
|
row--
|
2020-11-10 23:48:09 +08:00
|
|
|
ws, err := f.workSheetReader(sheet)
|
2019-04-15 11:22:57 +08:00
|
|
|
if err != nil {
|
2019-06-20 00:00:40 +08:00
|
|
|
return "", nil, err
|
2019-04-15 11:22:57 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
if ws.Drawing == nil {
|
2019-06-20 00:00:40 +08:00
|
|
|
return "", nil, err
|
2017-03-28 21:18:06 +08:00
|
|
|
}
|
2020-11-10 23:48:09 +08:00
|
|
|
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
|
2017-03-28 21:18:06 +08:00
|
|
|
drawingXML := strings.Replace(target, "..", "xl", -1)
|
2021-07-05 00:03:56 +08:00
|
|
|
if _, ok := f.Pkg.Load(drawingXML); !ok {
|
2019-06-20 00:00:40 +08:00
|
|
|
return "", nil, err
|
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
drawingRelationships := strings.Replace(
|
|
|
|
strings.Replace(target, "../drawings", "xl/drawings/_rels", -1), ".xml", ".xml.rels", -1)
|
|
|
|
|
2019-06-20 00:00:40 +08:00
|
|
|
return f.getPicture(row, col, drawingXML, drawingRelationships)
|
|
|
|
}
|
Huge refactorig for consistent col/row numbering (#356)
* Huge refactorig for consistent col/row numbering
Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes.
But have to go deeper, do fixes, after do related fixes and again and again.
Major improvements:
1. Tests made stronger again (But still be weak).
2. "Empty" returns for incorrect input replaces with panic.
3. Check for correct col/row/cell naming & addressing by default.
4. Removed huge amount of duplicated code.
5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all,
and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName().
6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc).
* Formatting fixes
2019-03-20 00:14:41 +08:00
|
|
|
|
2020-07-09 01:24:11 +08:00
|
|
|
// DeletePicture provides a function to delete charts in spreadsheet by given
|
2020-02-07 00:25:01 +08:00
|
|
|
// worksheet and cell name. Note that the image file won't be deleted from the
|
2020-01-22 01:08:18 +08:00
|
|
|
// document currently.
|
|
|
|
func (f *File) DeletePicture(sheet, cell string) (err error) {
|
|
|
|
col, row, err := CellNameToCoordinates(cell)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
col--
|
|
|
|
row--
|
|
|
|
ws, err := f.workSheetReader(sheet)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if ws.Drawing == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
drawingXML := strings.Replace(f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID), "..", "xl", -1)
|
|
|
|
return f.deleteDrawing(col, row, drawingXML, "Pic")
|
|
|
|
}
|
|
|
|
|
2019-06-20 00:00:40 +08:00
|
|
|
// getPicture provides a function to get picture base name and raw content
|
2020-07-09 01:24:11 +08:00
|
|
|
// embed in spreadsheet by given coordinates and drawing relationships.
|
2019-12-20 00:30:48 +08:00
|
|
|
func (f *File) getPicture(row, col int, drawingXML, drawingRelationships string) (ret string, buf []byte, err error) {
|
|
|
|
var (
|
|
|
|
wsDr *xlsxWsDr
|
|
|
|
ok bool
|
|
|
|
deWsDr *decodeWsDr
|
2019-12-20 01:00:15 +08:00
|
|
|
drawRel *xlsxRelationship
|
2019-12-20 00:30:48 +08:00
|
|
|
deTwoCellAnchor *decodeTwoCellAnchor
|
|
|
|
)
|
|
|
|
|
|
|
|
wsDr, _ = f.drawingParser(drawingXML)
|
2019-12-22 00:02:09 +08:00
|
|
|
if ret, buf = f.getPictureFromWsDr(row, col, drawingRelationships, wsDr); len(buf) > 0 {
|
|
|
|
return
|
2019-02-25 22:14:34 +08:00
|
|
|
}
|
2019-12-20 00:30:48 +08:00
|
|
|
deWsDr = new(decodeWsDr)
|
|
|
|
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(drawingXML)))).
|
|
|
|
Decode(deWsDr); err != nil && err != io.EOF {
|
|
|
|
err = fmt.Errorf("xml decode error: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = nil
|
|
|
|
for _, anchor := range deWsDr.TwoCellAnchor {
|
|
|
|
deTwoCellAnchor = new(decodeTwoCellAnchor)
|
2020-04-09 00:49:13 +08:00
|
|
|
if err = f.xmlNewDecoder(strings.NewReader("<decodeTwoCellAnchor>" + anchor.Content + "</decodeTwoCellAnchor>")).
|
2019-12-20 00:30:48 +08:00
|
|
|
Decode(deTwoCellAnchor); err != nil && err != io.EOF {
|
|
|
|
err = fmt.Errorf("xml decode error: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = nil; deTwoCellAnchor.From != nil && deTwoCellAnchor.Pic != nil {
|
|
|
|
if deTwoCellAnchor.From.Col == col && deTwoCellAnchor.From.Row == row {
|
2019-12-20 01:00:15 +08:00
|
|
|
drawRel = f.getDrawingRelationships(drawingRelationships, deTwoCellAnchor.Pic.BlipFill.Blip.Embed)
|
|
|
|
if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
|
2021-07-05 00:03:56 +08:00
|
|
|
ret = filepath.Base(drawRel.Target)
|
|
|
|
if buffer, _ := f.Pkg.Load(strings.Replace(drawRel.Target, "..", "xl", -1)); buffer != nil {
|
|
|
|
buf = buffer.([]byte)
|
|
|
|
}
|
2019-12-20 00:30:48 +08:00
|
|
|
return
|
2017-09-30 17:07:59 +08:00
|
|
|
}
|
2017-03-28 21:18:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-22 00:02:09 +08:00
|
|
|
return
|
|
|
|
}
|
2019-12-20 00:30:48 +08:00
|
|
|
|
2019-12-22 00:02:09 +08:00
|
|
|
// getPictureFromWsDr provides a function to get picture base name and raw
|
|
|
|
// content in worksheet drawing by given coordinates and drawing
|
|
|
|
// relationships.
|
|
|
|
func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsDr *xlsxWsDr) (ret string, buf []byte) {
|
|
|
|
var (
|
|
|
|
ok bool
|
|
|
|
anchor *xdrCellAnchor
|
|
|
|
drawRel *xlsxRelationship
|
|
|
|
)
|
2021-07-06 00:31:04 +08:00
|
|
|
wsDr.Lock()
|
|
|
|
defer wsDr.Unlock()
|
2019-12-22 00:02:09 +08:00
|
|
|
for _, anchor = range wsDr.TwoCellAnchor {
|
|
|
|
if anchor.From != nil && anchor.Pic != nil {
|
|
|
|
if anchor.From.Col == col && anchor.From.Row == row {
|
2020-07-11 02:31:02 +08:00
|
|
|
if drawRel = f.getDrawingRelationships(drawingRelationships,
|
|
|
|
anchor.Pic.BlipFill.Blip.Embed); drawRel != nil {
|
|
|
|
if _, ok = supportImageTypes[filepath.Ext(drawRel.Target)]; ok {
|
2021-07-05 00:03:56 +08:00
|
|
|
ret = filepath.Base(drawRel.Target)
|
|
|
|
if buffer, _ := f.Pkg.Load(strings.Replace(drawRel.Target, "..", "xl", -1)); buffer != nil {
|
|
|
|
buf = buffer.([]byte)
|
|
|
|
}
|
2020-07-11 02:31:02 +08:00
|
|
|
return
|
|
|
|
}
|
2019-12-22 00:02:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-20 00:30:48 +08:00
|
|
|
return
|
2017-03-28 21:18:06 +08:00
|
|
|
}
|
|
|
|
|
2018-08-06 10:21:24 +08:00
|
|
|
// getDrawingRelationships provides a function to get drawing relationships
|
|
|
|
// from xl/drawings/_rels/drawing%s.xml.rels by given file name and
|
|
|
|
// relationship ID.
|
2019-09-16 01:17:35 +08:00
|
|
|
func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship {
|
|
|
|
if drawingRels := f.relsReader(rels); drawingRels != nil {
|
2021-07-06 00:31:04 +08:00
|
|
|
drawingRels.Lock()
|
|
|
|
defer drawingRels.Unlock()
|
2019-02-25 22:14:34 +08:00
|
|
|
for _, v := range drawingRels.Relationships {
|
|
|
|
if v.ID == rID {
|
|
|
|
return &v
|
|
|
|
}
|
2017-03-28 21:18:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-02-25 22:14:34 +08:00
|
|
|
|
|
|
|
// drawingsWriter provides a function to save xl/drawings/drawing%d.xml after
|
|
|
|
// serialize structure.
|
|
|
|
func (f *File) drawingsWriter() {
|
2021-07-04 12:13:06 +08:00
|
|
|
f.Drawings.Range(func(path, d interface{}) bool {
|
2019-02-25 22:14:34 +08:00
|
|
|
if d != nil {
|
2021-07-04 12:13:06 +08:00
|
|
|
v, _ := xml.Marshal(d.(*xlsxWsDr))
|
|
|
|
f.saveFileList(path.(string), v)
|
2019-02-25 22:14:34 +08:00
|
|
|
}
|
2021-07-04 12:13:06 +08:00
|
|
|
return true
|
|
|
|
})
|
2019-02-25 22:14:34 +08:00
|
|
|
}
|
2020-05-17 17:36:53 +08:00
|
|
|
|
|
|
|
// drawingResize calculate the height and width after resizing.
|
2021-08-12 00:02:27 +08:00
|
|
|
func (f *File) drawingResize(sheet, cell string, width, height float64, formatSet *formatPicture) (w, h, c, r int, err error) {
|
2020-05-17 17:36:53 +08:00
|
|
|
var mergeCells []MergeCell
|
|
|
|
mergeCells, err = f.GetMergeCells(sheet)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var rng []int
|
|
|
|
var inMergeCell bool
|
|
|
|
if c, r, err = CellNameToCoordinates(cell); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cellWidth, cellHeight := f.getColWidth(sheet, c), f.getRowHeight(sheet, r)
|
|
|
|
for _, mergeCell := range mergeCells {
|
2020-12-14 20:56:51 +08:00
|
|
|
if inMergeCell {
|
|
|
|
continue
|
|
|
|
}
|
2020-05-17 17:36:53 +08:00
|
|
|
if inMergeCell, err = f.checkCellInArea(cell, mergeCell[0]); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if inMergeCell {
|
|
|
|
rng, _ = areaRangeToCoordinates(mergeCell.GetStartAxis(), mergeCell.GetEndAxis())
|
2021-02-15 00:09:35 +08:00
|
|
|
_ = sortCoordinates(rng)
|
2020-05-17 17:36:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if inMergeCell {
|
2020-05-21 22:57:58 +08:00
|
|
|
cellWidth, cellHeight = 0, 0
|
2020-05-17 17:36:53 +08:00
|
|
|
c, r = rng[0], rng[1]
|
2021-06-13 14:38:01 +08:00
|
|
|
for col := rng[0]; col <= rng[2]; col++ {
|
2020-05-17 17:36:53 +08:00
|
|
|
cellWidth += f.getColWidth(sheet, col)
|
|
|
|
}
|
2021-06-13 14:38:01 +08:00
|
|
|
for row := rng[1]; row <= rng[3]; row++ {
|
2020-05-17 17:36:53 +08:00
|
|
|
cellHeight += f.getRowHeight(sheet, row)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if float64(cellWidth) < width {
|
|
|
|
asp := float64(cellWidth) / width
|
|
|
|
width, height = float64(cellWidth), height*asp
|
|
|
|
}
|
|
|
|
if float64(cellHeight) < height {
|
|
|
|
asp := float64(cellHeight) / height
|
|
|
|
height, width = float64(cellHeight), width*asp
|
|
|
|
}
|
|
|
|
width, height = width-float64(formatSet.OffsetX), height-float64(formatSet.OffsetY)
|
|
|
|
w, h = int(width*formatSet.XScale), int(height*formatSet.YScale)
|
|
|
|
return
|
|
|
|
}
|