瀏覽代碼

- Get an images in a cell supported, new function `GetPicture` added;
- go test updated

Ri Xu 8 年之前
父節點
當前提交
02b81b7efe
共有 5 個文件被更改,包括 271 次插入4 次删除
  1. 26 0
      excelize_test.go
  2. 80 2
      picture.go
  3. 1 2
      sheet.go
  4. 162 0
      xmlDecodeDrawing.go
  5. 2 0
      xmlDrawing.go

+ 26 - 0
excelize_test.go

@@ -4,6 +4,7 @@ import (
 	_ "image/gif"
 	_ "image/jpeg"
 	_ "image/png"
+	"io/ioutil"
 	"strconv"
 	"testing"
 )
@@ -376,3 +377,28 @@ func TestSetDeleteSheet(t *testing.T) {
 		t.Log(err)
 	}
 }
+
+func TestGetPicture(t *testing.T) {
+	xlsx, err := OpenFile("./test/Workbook_2.xlsx")
+	if err != nil {
+		t.Log(err)
+	}
+	file, raw := xlsx.GetPicture("Sheet1", "F21")
+	if file == "" {
+		err = ioutil.WriteFile(file, raw, 0644)
+		if err != nil {
+			t.Log(err)
+		}
+	}
+	// Try to get picture from a worksheet that doesn't contain any images.
+	file, raw = xlsx.GetPicture("Sheet3", "I9")
+	if file != "" {
+		err = ioutil.WriteFile(file, raw, 0644)
+		if err != nil {
+			t.Log(err)
+		}
+	}
+	// Try to get picture from a cell that doesn't contain an image.
+	file, raw = xlsx.GetPicture("Sheet2", "A2")
+	t.Log(file, len(raw))
+}

+ 80 - 2
picture.go

@@ -72,13 +72,12 @@ func parseFormatPictureSet(formatSet string) *formatPicture {
 //    }
 //
 func (f *File) AddPicture(sheet, cell, picture, format string) error {
-	var supportTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png"}
 	var err error
 	// Check picture exists first.
 	if _, err = os.Stat(picture); os.IsNotExist(err) {
 		return err
 	}
-	ext, ok := supportTypes[path.Ext(picture)]
+	ext, ok := supportImageTypes[path.Ext(picture)]
 	if !ok {
 		return errors.New("Unsupported image extension")
 	}
@@ -360,3 +359,82 @@ func (f *File) getSheetRelationshipsTargetByID(sheet string, rID string) string
 	}
 	return ""
 }
+
+// GetPicture provides function to get picture base name and raw content embed
+// in XLSX by given worksheet and cell name. This function returns the file name
+// in XLSX and file contents as []byte data types. For example:
+//
+//    xlsx, err := excelize.OpenFile("/tmp/Workbook.xlsx")
+//    if err != nil {
+//        fmt.Println(err)
+//        os.Exit(1)
+//    }
+//    file, raw := xlsx.GetPicture("Sheet1", "A2")
+//    if file == "" {
+//        os.Exit(1)
+//    }
+// 	  err := ioutil.WriteFile(file, raw, 0644)
+// 	  if err != nil {
+//        fmt.Println(err)
+//        os.Exit(1)
+// 	  }
+//
+func (f *File) GetPicture(sheet, cell string) (string, []byte) {
+	xlsx := f.workSheetReader(sheet)
+	if xlsx.Drawing == nil {
+		return "", []byte{}
+	}
+	target := f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
+	drawingXML := strings.Replace(target, "..", "xl", -1)
+
+	_, ok := f.XLSX[drawingXML]
+	if !ok {
+		return "", []byte{}
+	}
+	decodeWsDr := decodeWsDr{}
+	xml.Unmarshal([]byte(f.readXML(drawingXML)), &decodeWsDr)
+
+	cell = strings.ToUpper(cell)
+	fromCol := string(strings.Map(letterOnlyMapF, cell))
+	fromRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
+	row := fromRow - 1
+	col := titleToNumber(fromCol)
+
+	drawingRelationships := strings.Replace(strings.Replace(target, "../drawings", "xl/drawings/_rels", -1), ".xml", ".xml.rels", -1)
+
+	for _, anchor := range decodeWsDr.TwoCellAnchor {
+		decodeTwoCellAnchor := decodeTwoCellAnchor{}
+		xml.Unmarshal([]byte("<decodeTwoCellAnchor>"+anchor.Content+"</decodeTwoCellAnchor>"), &decodeTwoCellAnchor)
+		if decodeTwoCellAnchor.From == nil || decodeTwoCellAnchor.Pic == nil {
+			continue
+		}
+		if decodeTwoCellAnchor.From.Col == col && decodeTwoCellAnchor.From.Row == row {
+			xlsxWorkbookRelation := f.getDrawingRelationships(drawingRelationships, decodeTwoCellAnchor.Pic.BlipFill.Blip.Embed)
+			_, ok := supportImageTypes[filepath.Ext(xlsxWorkbookRelation.Target)]
+			if !ok {
+				continue
+			}
+
+			return filepath.Base(xlsxWorkbookRelation.Target), []byte(f.XLSX[strings.Replace(xlsxWorkbookRelation.Target, "..", "xl", -1)])
+		}
+	}
+	return "", []byte{}
+}
+
+// getDrawingRelationships provides function to get drawing relationships from
+// xl/drawings/_rels/drawing%s.xml.rels by given file name and relationship ID.
+func (f *File) getDrawingRelationships(rels, rID string) *xlsxWorkbookRelation {
+	_, ok := f.XLSX[rels]
+	if !ok {
+		return nil
+	}
+	var drawingRels xlsxWorkbookRels
+	xml.Unmarshal([]byte(f.readXML(rels)), &drawingRels)
+	for _, v := range drawingRels.Relationships {
+		if v.ID != rID {
+			continue
+		}
+		return &v
+	}
+	return nil
+}

+ 1 - 2
sheet.go

@@ -257,13 +257,12 @@ func (f *File) GetSheetMap() map[int]string {
 // SetSheetBackground provides function to set background picture by given sheet
 // index.
 func (f *File) SetSheetBackground(sheet, picture string) error {
-	var supportTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png"}
 	var err error
 	// Check picture exists first.
 	if _, err = os.Stat(picture); os.IsNotExist(err) {
 		return err
 	}
-	ext, ok := supportTypes[path.Ext(picture)]
+	ext, ok := supportImageTypes[path.Ext(picture)]
 	if !ok {
 		return errors.New("Unsupported image extension")
 	}

+ 162 - 0
xmlDecodeDrawing.go

@@ -22,3 +22,165 @@ type decodeWsDr struct {
 	TwoCellAnchor []*decodeCellAnchor `xml:"twoCellAnchor,omitempty"`
 	XMLName       xml.Name            `xml:"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing wsDr,omitempty"`
 }
+
+// decodeTwoCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape
+// Size) and twoCellAnchor (Two Cell Anchor Shape Size). This element specifies
+// a two cell anchor placeholder for a group, a shape, or a drawing element. It
+// moves with cells and its extents are in EMU units.
+type decodeTwoCellAnchor struct {
+	From       *decodeFrom       `xml:"from"`
+	To         *decodeTo         `xml:"to"`
+	Pic        *decodePic        `xml:"pic,omitempty"`
+	ClientData *decodeClientData `xml:"clientData"`
+}
+
+// decodeCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This
+// element specifies non-visual canvas properties. This allows for additional
+// information that does not affect the appearance of the picture to be stored.
+type decodeCNvPr struct {
+	ID    int    `xml:"id,attr"`
+	Name  string `xml:"name,attr"`
+	Descr string `xml:"descr,attr"`
+	Title string `xml:"title,attr,omitempty"`
+}
+
+// decodePicLocks directly maps the picLocks (Picture Locks). This element
+// specifies all locking properties for a graphic frame. These properties inform
+// the generating application about specific properties that have been
+// previously locked and thus should not be changed.
+type decodePicLocks struct {
+	NoAdjustHandles    bool `xml:"noAdjustHandles,attr,omitempty"`
+	NoChangeArrowheads bool `xml:"noChangeArrowheads,attr,omitempty"`
+	NoChangeAspect     bool `xml:"noChangeAspect,attr"`
+	NoChangeShapeType  bool `xml:"noChangeShapeType,attr,omitempty"`
+	NoCrop             bool `xml:"noCrop,attr,omitempty"`
+	NoEditPoints       bool `xml:"noEditPoints,attr,omitempty"`
+	NoGrp              bool `xml:"noGrp,attr,omitempty"`
+	NoMove             bool `xml:"noMove,attr,omitempty"`
+	NoResize           bool `xml:"noResize,attr,omitempty"`
+	NoRot              bool `xml:"noRot,attr,omitempty"`
+	NoSelect           bool `xml:"noSelect,attr,omitempty"`
+}
+
+// decodeBlip directly maps the blip element in the namespace
+// http://purl.oclc.org/ooxml/officeDoc ument/relationships - This element
+// specifies the existence of an image (binary large image or picture) and
+// contains a reference to the image data.
+type decodeBlip struct {
+	Embed  string `xml:"embed,attr"`
+	Cstate string `xml:"cstate,attr,omitempty"`
+	R      string `xml:"r,attr"`
+}
+
+// decodeStretch directly maps the stretch element. This element specifies that
+// a BLIP should be stretched to fill the target rectangle. The other option is
+// a tile where a BLIP is tiled to fill the available area.
+type decodeStretch struct {
+	FillRect string `xml:"fillRect"`
+}
+
+// decodeOff directly maps the colOff and rowOff element. This element is used
+// to specify the column offset within a cell.
+type decodeOff struct {
+	X int `xml:"x,attr"`
+	Y int `xml:"y,attr"`
+}
+
+// decodeExt directly maps the ext element.
+type decodeExt struct {
+	Cx int `xml:"cx,attr"`
+	Cy int `xml:"cy,attr"`
+}
+
+// decodePrstGeom directly maps the prstGeom (Preset geometry). This element
+// specifies when a preset geometric shape should be used instead of a custom
+// geometric shape. The generating application should be able to render all
+// preset geometries enumerated in the ST_ShapeType list.
+type decodePrstGeom struct {
+	Prst string `xml:"prst,attr"`
+}
+
+// decodeXfrm directly maps the xfrm (2D Transform for Graphic Frame). This
+// element specifies the transform to be applied to the corresponding graphic
+// frame. This transformation is applied to the graphic frame just as it would
+// be for a shape or group shape.
+type decodeXfrm struct {
+	Off decodeOff `xml:"off"`
+	Ext decodeExt `xml:"ext"`
+}
+
+// decodeCNvPicPr directly maps the cNvPicPr (Non-Visual Picture Drawing
+// Properties). This element specifies the non-visual properties for the picture
+// canvas. These properties are to be used by the generating application to
+// determine how certain properties are to be changed for the picture object in
+// question.
+type decodeCNvPicPr struct {
+	PicLocks decodePicLocks `xml:"picLocks"`
+}
+
+// directly maps the nvPicPr (Non-Visual Properties for a Picture). This element
+// specifies all non-visual properties for a picture. This element is a
+// container for the non-visual identification properties, shape properties and
+// application properties that are to be associated with a picture. This allows
+// for additional information that does not affect the appearance of the picture
+// to be stored.
+type decodeNvPicPr struct {
+	CNvPr    decodeCNvPr    `xml:"cNvPr"`
+	CNvPicPr decodeCNvPicPr `xml:"cNvPicPr"`
+}
+
+// decodeBlipFill directly maps the blipFill (Picture Fill). This element
+// specifies the kind of picture fill that the picture object has. Because a
+// picture has a picture fill already by default, it is possible to have two
+// fills specified for a picture object.
+type decodeBlipFill struct {
+	Blip    decodeBlip    `xml:"blip"`
+	Stretch decodeStretch `xml:"stretch"`
+}
+
+// decodeSpPr directly maps the spPr (Shape Properties). This element specifies
+// the visual shape properties that can be applied to a picture. These are the
+// same properties that are allowed to describe the visual properties of a shape
+// but are used here to describe the visual appearance of a picture within a
+// document.
+type decodeSpPr struct {
+	Xfrm     decodeXfrm     `xml:"a:xfrm"`
+	PrstGeom decodePrstGeom `xml:"a:prstGeom"`
+}
+
+// decodePic elements encompass the definition of pictures within the DrawingML
+// framework. While pictures are in many ways very similar to shapes they have
+// specific properties that are unique in order to optimize for picture-
+// specific scenarios.
+type decodePic struct {
+	NvPicPr  decodeNvPicPr  `xml:"nvPicPr"`
+	BlipFill decodeBlipFill `xml:"blipFill"`
+	SpPr     decodeSpPr     `xml:"spPr"`
+}
+
+// decodeFrom specifies the starting anchor.
+type decodeFrom struct {
+	Col    int `xml:"col"`
+	ColOff int `xml:"colOff"`
+	Row    int `xml:"row"`
+	RowOff int `xml:"rowOff"`
+}
+
+// decodeTo directly specifies the ending anchor.
+type decodeTo struct {
+	Col    int `xml:"col"`
+	ColOff int `xml:"colOff"`
+	Row    int `xml:"row"`
+	RowOff int `xml:"rowOff"`
+}
+
+// decodeClientData directly maps the clientData element. An empty element which
+// specifies (via attributes) certain properties related to printing and
+// selection of the drawing object. The fLocksWithSheet attribute (either true
+// or false) determines whether to disable selection when the sheet is
+// protected, and fPrintsWithSheet attribute (either true or false) determines
+// whether the object is printed when the sheet is printed.
+type decodeClientData struct {
+	FLocksWithSheet  bool `xml:"fLocksWithSheet,attr"`
+	FPrintsWithSheet bool `xml:"fPrintsWithSheet,attr"`
+}

+ 2 - 0
xmlDrawing.go

@@ -12,6 +12,8 @@ const (
 	NameSpaceXML                = "http://www.w3.org/XML/1998/namespace"
 )
 
+var supportImageTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png"}
+
 // xlsxCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This
 // element specifies non-visual canvas properties. This allows for additional
 // information that does not affect the appearance of the picture to be stored.