From 76219275737f28d1b58ac4c693c5e8f59daf53fd Mon Sep 17 00:00:00 2001 From: Ri Xu Date: Sat, 3 Feb 2018 15:02:37 +0800 Subject: [PATCH] - Add a hyperlink to an image support for the function `AddPicture()`, relate issue #185; - go test and document has been updated. --- chart.go | 2 +- excelize_test.go | 12 ++++++------ picture.go | 41 ++++++++++++++++++++++++++++++----------- xmlDrawing.go | 26 ++++++++++++++++++++++---- 4 files changed, 59 insertions(+), 22 deletions(-) diff --git a/chart.go b/chart.go index 4d0f763..b000683 100644 --- a/chart.go +++ b/chart.go @@ -258,7 +258,7 @@ func (f *File) AddChart(sheet, cell, format string) { chartID := f.countCharts() + 1 drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML) - drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml") + drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipChart, "../charts/chart"+strconv.Itoa(chartID)+".xml", "") f.addDrawingChart(sheet, drawingXML, cell, 480, 290, drawingRID, &formatSet.Format) f.addChart(formatSet) f.addContentTypePart(chartID, "chart") diff --git a/excelize_test.go b/excelize_test.go index c7fb5de..0b7b8bc 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -132,22 +132,22 @@ func TestAddPicture(t *testing.T) { if err != nil { t.Log(err) } - // Test add picture to sheet. - err = xlsx.AddPicture("Sheet2", "I9", "./test/images/excel.jpg", `{"x_offset": 140, "y_offset": 120}`) + // Test add picture to worksheet with offset and location hyperlink. + err = xlsx.AddPicture("Sheet2", "I9", "./test/images/excel.jpg", `{"x_offset": 140, "y_offset": 120, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`) if err != nil { t.Log(err) } - // Test add picture to sheet with offset. - err = xlsx.AddPicture("Sheet1", "F21", "./test/images/excel.png", `{"x_offset": 10, "y_offset": 10}`) + // Test add picture to worksheet with offset and external hyperlink. + err = xlsx.AddPicture("Sheet1", "F21", "./test/images/excel.png", `{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/360EntSecGroup-Skylar/excelize", "hyperlink_type": "External"}`) if err != nil { t.Log(err) } - // Test add picture to sheet with invalid file path. + // Test add picture to worksheet with invalid file path. err = xlsx.AddPicture("Sheet1", "G21", "./test/images/excel.icon", "") if err != nil { t.Log(err) } - // Test add picture to sheet with unsupport file type. + // Test add picture to worksheet with unsupport file type. err = xlsx.AddPicture("Sheet1", "G21", "./test/Workbook1.xlsx", "") if err != nil { t.Log(err) diff --git a/picture.go b/picture.go index fc8b6e7..f37cee6 100644 --- a/picture.go +++ b/picture.go @@ -52,13 +52,13 @@ func parseFormatPictureSet(formatSet string) *formatPicture { // if err != nil { // fmt.Println(err) // } -// // Insert a picture to sheet with scaling. -// err = xlsx.AddPicture("Sheet1", "D2", "./image1.png", `{"x_scale": 0.5, "y_scale": 0.5}`) +// // Insert a picture scaling in the cell with location hyperlink. +// err = xlsx.AddPicture("Sheet1", "D2", "./image1.png", `{"x_scale": 0.5, "y_scale": 0.5, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`) // if err != nil { // fmt.Println(err) // } -// // Insert a picture offset in the cell with printing support. -// err = xlsx.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`) +// // Insert a picture offset in the cell with external hyperlink and printing support. +// err = xlsx.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "hyperlink": "https://github.com/360EntSecGroup-Skylar/excelize", "hyperlink_type": "External", "print_obj": true, "lock_aspect_ratio": false, "locked": false}`) // if err != nil { // fmt.Println(err) // } @@ -68,8 +68,13 @@ func parseFormatPictureSet(formatSet string) *formatPicture { // } // } // +// LinkType defines two types of hyperlink "External" for web site or +// "Location" for moving to one of cell in this workbook. When the +// "hyperlink_type" is "Location", coordinates need to start with "#". func (f *File) AddPicture(sheet, cell, picture, format string) error { var err error + var drawingHyperlinkRID int + var hyperlinkType string // Check picture exists first. if _, err = os.Stat(picture); os.IsNotExist(err) { return err @@ -89,8 +94,15 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error { pictureID := f.countMedia() + 1 drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml" drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML) - drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext) - f.addDrawingPicture(sheet, drawingXML, cell, file, image.Width, image.Height, drawingRID, formatSet) + drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext, hyperlinkType) + // Add picture with hyperlink. + if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" { + if formatSet.HyperlinkType == "External" { + hyperlinkType = formatSet.HyperlinkType + } + drawingHyperlinkRID = f.addDrawingRelationships(drawingID, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType) + } + f.addDrawingPicture(sheet, drawingXML, cell, file, image.Width, image.Height, drawingRID, drawingHyperlinkRID, formatSet) f.addMedia(picture, ext) f.addContentTypePart(drawingID, "drawings") return err @@ -191,7 +203,7 @@ func (f *File) countDrawings() int { // addDrawingPicture provides function to add picture by given sheet, // drawingXML, cell, file name, width, height relationship index and format // sets. -func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID int, formatSet *formatPicture) { +func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) { cell = strings.ToUpper(cell) fromCol := string(strings.Map(letterOnlyMapF, cell)) fromRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell)) @@ -223,6 +235,12 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he pic.NvPicPr.CNvPr.ID = f.countCharts() + f.countMedia() + 1 pic.NvPicPr.CNvPr.Descr = file pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID) + if hyperlinkRID != 0 { + pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{ + R: SourceRelationship, + RID: "rId" + strconv.Itoa(hyperlinkRID), + } + } pic.BlipFill.Blip.R = SourceRelationship pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID) pic.SpPr.PrstGeom.Prst = "rect" @@ -240,7 +258,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he // addDrawingRelationships provides function to add image part relationships in // the file xl/drawings/_rels/drawing%d.xml.rels by given drawing index, // relationship type and target. -func (f *File) addDrawingRelationships(index int, relType, target string) int { +func (f *File) addDrawingRelationships(index int, relType, target, targetMode string) int { var rels = "xl/drawings/_rels/drawing" + strconv.Itoa(index) + ".xml.rels" var drawingRels xlsxWorkbookRels var rID = 1 @@ -256,9 +274,10 @@ func (f *File) addDrawingRelationships(index int, relType, target string) int { ID.WriteString(strconv.Itoa(rID)) } drawingRels.Relationships = append(drawingRels.Relationships, xlsxWorkbookRelation{ - ID: ID.String(), - Type: relType, - Target: target, + ID: ID.String(), + Type: relType, + Target: target, + TargetMode: targetMode, }) output, _ := xml.Marshal(drawingRels) f.saveFileList(rels, string(output)) diff --git a/xmlDrawing.go b/xmlDrawing.go index e61fe6a..600d83a 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -30,10 +30,26 @@ var supportImageTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpe // element specifies non-visual canvas properties. This allows for additional // information that does not affect the appearance of the picture to be stored. type xlsxCNvPr struct { - ID int `xml:"id,attr"` - Name string `xml:"name,attr"` - Descr string `xml:"descr,attr"` - Title string `xml:"title,attr,omitempty"` + ID int `xml:"id,attr"` + Name string `xml:"name,attr"` + Descr string `xml:"descr,attr"` + Title string `xml:"title,attr,omitempty"` + HlinkClick *xlsxHlinkClick `xml:"a:hlinkClick"` +} + +// xlsxHlinkClick (Click Hyperlink) Specifies the on-click hyperlink +// information to be applied to a run of text. When the hyperlink text is +// clicked the link is fetched. +type xlsxHlinkClick struct { + R string `xml:"xmlns:r,attr,omitempty"` + RID string `xml:"r:id,attr,omitempty"` + InvalidURL string `xml:"invalidUrl,attr,omitempty"` + Action string `xml:"action,attr,omitempty"` + TgtFrame string `xml:"tgtFrame,attr,omitempty"` + Tooltip string `xml:"tooltip,attr,omitempty"` + History bool `xml:"history,attr,omitempty"` + HighlightClick bool `xml:"highlightClick,attr,omitempty"` + EndSnd bool `xml:"endSnd,attr,omitempty"` } // xlsxPicLocks directly maps the picLocks (Picture Locks). This element @@ -342,6 +358,8 @@ type formatPicture struct { OffsetY int `json:"y_offset"` XScale float64 `json:"x_scale"` YScale float64 `json:"y_scale"` + Hyperlink string `json:"hyperlink"` + HyperlinkType string `json:"hyperlink_type"` } // formatShape directly maps the format settings of the shape.