Browse Source

- Add a hyperlink to an image support for the function `AddPicture()`, relate issue #185;
- go test and document has been updated.

Ri Xu 7 years ago
parent
commit
7621927573
4 changed files with 59 additions and 22 deletions
  1. 1 1
      chart.go
  2. 6 6
      excelize_test.go
  3. 30 11
      picture.go
  4. 22 4
      xmlDrawing.go

+ 1 - 1
chart.go

@@ -258,7 +258,7 @@ func (f *File) AddChart(sheet, cell, format string) {
 	chartID := f.countCharts() + 1
 	chartID := f.countCharts() + 1
 	drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
 	drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
 	drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML)
 	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.addDrawingChart(sheet, drawingXML, cell, 480, 290, drawingRID, &formatSet.Format)
 	f.addChart(formatSet)
 	f.addChart(formatSet)
 	f.addContentTypePart(chartID, "chart")
 	f.addContentTypePart(chartID, "chart")

+ 6 - 6
excelize_test.go

@@ -132,22 +132,22 @@ func TestAddPicture(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Log(err)
 		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 {
 	if err != nil {
 		t.Log(err)
 		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 {
 	if err != nil {
 		t.Log(err)
 		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", "")
 	err = xlsx.AddPicture("Sheet1", "G21", "./test/images/excel.icon", "")
 	if err != nil {
 	if err != nil {
 		t.Log(err)
 		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", "")
 	err = xlsx.AddPicture("Sheet1", "G21", "./test/Workbook1.xlsx", "")
 	if err != nil {
 	if err != nil {
 		t.Log(err)
 		t.Log(err)

+ 30 - 11
picture.go

@@ -52,13 +52,13 @@ func parseFormatPictureSet(formatSet string) *formatPicture {
 //        if err != nil {
 //        if err != nil {
 //            fmt.Println(err)
 //            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 {
 //        if err != nil {
 //            fmt.Println(err)
 //            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 {
 //        if err != nil {
 //            fmt.Println(err)
 //            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 {
 func (f *File) AddPicture(sheet, cell, picture, format string) error {
 	var err error
 	var err error
+	var drawingHyperlinkRID int
+	var hyperlinkType string
 	// Check picture exists first.
 	// Check picture exists first.
 	if _, err = os.Stat(picture); os.IsNotExist(err) {
 	if _, err = os.Stat(picture); os.IsNotExist(err) {
 		return err
 		return err
@@ -89,8 +94,15 @@ func (f *File) AddPicture(sheet, cell, picture, format string) error {
 	pictureID := f.countMedia() + 1
 	pictureID := f.countMedia() + 1
 	drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
 	drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
 	drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML)
 	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.addMedia(picture, ext)
 	f.addContentTypePart(drawingID, "drawings")
 	f.addContentTypePart(drawingID, "drawings")
 	return err
 	return err
@@ -191,7 +203,7 @@ func (f *File) countDrawings() int {
 // addDrawingPicture provides function to add picture by given sheet,
 // addDrawingPicture provides function to add picture by given sheet,
 // drawingXML, cell, file name, width, height relationship index and format
 // drawingXML, cell, file name, width, height relationship index and format
 // sets.
 // 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)
 	cell = strings.ToUpper(cell)
 	fromCol := string(strings.Map(letterOnlyMapF, cell))
 	fromCol := string(strings.Map(letterOnlyMapF, cell))
 	fromRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, 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.ID = f.countCharts() + f.countMedia() + 1
 	pic.NvPicPr.CNvPr.Descr = file
 	pic.NvPicPr.CNvPr.Descr = file
 	pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
 	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.R = SourceRelationship
 	pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID)
 	pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID)
 	pic.SpPr.PrstGeom.Prst = "rect"
 	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
 // addDrawingRelationships provides function to add image part relationships in
 // the file xl/drawings/_rels/drawing%d.xml.rels by given drawing index,
 // the file xl/drawings/_rels/drawing%d.xml.rels by given drawing index,
 // relationship type and target.
 // 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 rels = "xl/drawings/_rels/drawing" + strconv.Itoa(index) + ".xml.rels"
 	var drawingRels xlsxWorkbookRels
 	var drawingRels xlsxWorkbookRels
 	var rID = 1
 	var rID = 1
@@ -256,9 +274,10 @@ func (f *File) addDrawingRelationships(index int, relType, target string) int {
 		ID.WriteString(strconv.Itoa(rID))
 		ID.WriteString(strconv.Itoa(rID))
 	}
 	}
 	drawingRels.Relationships = append(drawingRels.Relationships, xlsxWorkbookRelation{
 	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)
 	output, _ := xml.Marshal(drawingRels)
 	f.saveFileList(rels, string(output))
 	f.saveFileList(rels, string(output))

+ 22 - 4
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
 // element specifies non-visual canvas properties. This allows for additional
 // information that does not affect the appearance of the picture to be stored.
 // information that does not affect the appearance of the picture to be stored.
 type xlsxCNvPr struct {
 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
 // xlsxPicLocks directly maps the picLocks (Picture Locks). This element
@@ -342,6 +358,8 @@ type formatPicture struct {
 	OffsetY          int     `json:"y_offset"`
 	OffsetY          int     `json:"y_offset"`
 	XScale           float64 `json:"x_scale"`
 	XScale           float64 `json:"x_scale"`
 	YScale           float64 `json:"y_scale"`
 	YScale           float64 `json:"y_scale"`
+	Hyperlink        string  `json:"hyperlink"`
+	HyperlinkType    string  `json:"hyperlink_type"`
 }
 }
 
 
 // formatShape directly maps the format settings of the shape.
 // formatShape directly maps the format settings of the shape.