Skip to content

Commit fe742ed

Browse files
authored
Merge pull request #27 from TeCHiScy/develop
feat: support image replace
2 parents ec3bf4a + 725e66f commit fe742ed

File tree

8 files changed

+57
-1
lines changed

8 files changed

+57
-1
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ func main() {
7777
panic(err)
7878
}
7979

80+
// read the replacement image
81+
bs, err := os.ReadFile("./test/cameraman.jpg")
82+
if err != nil {
83+
panic(err)
84+
}
85+
86+
// replace the lenna image with the cameraman image
87+
// image attributes, e.g. size, position, keep unchanged
88+
err = doc.SetFile("word/media/image1.jpg", bs)
89+
if err != nil {
90+
panic(err)
91+
}
92+
8093
// write out a new file
8194
err = doc.WriteToFile("replaced.docx")
8295
if err != nil {
@@ -98,6 +111,13 @@ Although I do not recommend to do that as the WordprocessingML spec is somewhat
98111

99112
But, for whatever reason there might be, you can do that.
100113

114+
#### Image replace
115+
Image replacing is slightly different from text replacing. To replace an image, you need to know its path within the docx archive, rather than using a placeholder.
116+
117+
To find the path, use unzip or similar tools to extract the contents of a docx file, then locate the image to be replaced inside the `word/media/` folder. Assume the path is `word/media/image1.jpg`, then you can use the `SetFile()` to overwrite the old image with a new one. It should be noted that:
118+
- The image format (encoding) should keep the same during the replacement.
119+
- Since the metadata of the image is not changed, only the image file itself is replaced, the new image will appear in its original location, with its original size. In other words, the image attributes keep unchanged.
120+
101121
### ➤ Terminology
102122
To not cause too much confusion, here is a list of terms which you might come across.
103123

document.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ var (
2626
HeaderPathRegex = regexp.MustCompile(`word/header[0-9]*.xml`)
2727
// FooterPathRegex matches all footer files inside the docx-archive.
2828
FooterPathRegex = regexp.MustCompile(`word/footer[0-9]*.xml`)
29+
// MediaPathRegex matches all media files inside the docx-archive.
30+
MediaPathRegex = regexp.MustCompile(`word/media/*`)
2931
)
3032

3133
// Document exposes the main API of the library. It represents the actual docx document which is going to be modified.
@@ -42,6 +44,8 @@ type Document struct {
4244
headerFiles []string
4345
// paths to all footer files inside the zip archive
4446
footerFiles []string
47+
// paths to all media files inside the zip archive
48+
mediaFiles []string
4549
// The document contains multiple files which eventually need a parser each.
4650
// The map key is the file path inside the document to which the parser belongs.
4751
runParsers map[string]*RunParser
@@ -168,7 +172,7 @@ func (d *Document) GetPlaceHoldersList() ([]string, error) {
168172
for file := range d.files {
169173
if _, ok := d.runParsers[file]; !ok {
170174
return nil, fmt.Errorf("no parser for file %s", file)
171-
}
175+
}
172176
replacer := d.fileReplacers[file]
173177
placeholders := replacer.placeholders
174178
for _, placeholder := range placeholders {
@@ -178,6 +182,7 @@ func (d *Document) GetPlaceHoldersList() ([]string, error) {
178182

179183
return placeholdersTextList, nil
180184
}
185+
181186
// replace will create a parser on the given bytes, execute it and replace every placeholders found with the data
182187
// from the placeholderMap.
183188
func (d *Document) replace(placeholderMap PlaceholderMap, file string) ([]byte, error) {
@@ -294,6 +299,7 @@ func (d *Document) SetFile(fileName string, fileBytes []byte) error {
294299
// - word/document.xml
295300
// - word/header*.xml
296301
// - word/footer*.xml
302+
// - word/media/*
297303
func (d *Document) parseArchive() error {
298304
readZipFile := func(file *zip.File) []byte {
299305
readCloser, err := file.Open()
@@ -320,6 +326,10 @@ func (d *Document) parseArchive() error {
320326
d.files[file.Name] = readZipFile(file)
321327
d.footerFiles = append(d.footerFiles, file.Name)
322328
}
329+
if MediaPathRegex.MatchString(file.Name) {
330+
d.files[file.Name] = readZipFile(file)
331+
d.mediaFiles = append(d.mediaFiles, file.Name)
332+
}
323333
}
324334
return nil
325335
}
@@ -402,6 +412,7 @@ func (d *Document) Write(writer io.Writer) error {
402412
// isModifiedFile will look through all modified files and check if the searchFileName exists
403413
func (d *Document) isModifiedFile(searchFileName string) bool {
404414
allFiles := append(d.headerFiles, d.footerFiles...)
415+
allFiles = append(allFiles, d.mediaFiles...)
405416
allFiles = append(allFiles, DocumentXml)
406417

407418
for _, file := range allFiles {

examples/simple/cameraman.jpg

11.9 KB
Loading

examples/simple/main.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"flag"
55
"log"
6+
"os"
67
"time"
78

89
"github.com/lukasjarosch/go-docx"
@@ -48,6 +49,18 @@ func main() {
4849

4950
log.Printf("replace took: %s", time.Since(startTime))
5051

52+
bs, err := os.ReadFile("./cameraman.jpg")
53+
if err != nil {
54+
panic(err)
55+
}
56+
57+
err = doc.SetFile("word/media/image1.jpg", bs)
58+
if err != nil {
59+
panic(err)
60+
}
61+
62+
log.Printf("replace image took: %s", time.Since(startTime))
63+
5164
err = doc.WriteToFile(outputPath)
5265
if err != nil {
5366
panic(err)

examples/simple/template.docx

158 KB
Binary file not shown.

replace_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ func TestReplacer_Replace(t *testing.T) {
3232
return
3333
}
3434

35+
bs, err := os.ReadFile("./test/cameraman.jpg")
36+
if err != nil {
37+
t.Error(err)
38+
return
39+
}
40+
41+
err = doc.SetFile("word/media/image1.jpg", bs)
42+
if err != nil {
43+
t.Error("replacing image failed", err)
44+
return
45+
}
46+
3547
err = doc.WriteToFile("./test/out.docx")
3648
if err != nil {
3749
t.Error("unable to write", err)

test/cameraman.jpg

11.9 KB
Loading

test/template.docx

161 KB
Binary file not shown.

0 commit comments

Comments
 (0)