├── .gitignore
├── README.md
├── docx
├── docx.go
├── escape.go
└── xml_config.go
├── main.go
├── template.docx
└── template_new.docx
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # smarterlab-docx
2 |
3 | 1. Support docx template substitution
4 | 2. Support OOXML
5 | 3. Support ms-word2007 and above version
6 |
7 |
8 | ### DEMO ###
9 | 查看 main.go
10 |
--------------------------------------------------------------------------------
/docx/docx.go:
--------------------------------------------------------------------------------
1 | package docx
2 |
3 | import (
4 | "archive/zip"
5 | "bufio"
6 | "bytes"
7 | "encoding/xml"
8 | "errors"
9 | "fmt"
10 | "io"
11 | "io/ioutil"
12 | "os"
13 | "strconv"
14 | "strings"
15 | )
16 |
17 | type ZipData interface {
18 | files() []*zip.File
19 | close() error
20 | }
21 |
22 | //Type for in memory zip files
23 | type ZipInMemory struct {
24 | data *zip.Reader
25 | }
26 |
27 | func (d ZipInMemory) files() []*zip.File {
28 | return d.data.File
29 | }
30 |
31 | //Since there is nothing to close for in memory, just nil the data and return nil
32 | func (d ZipInMemory) close() error {
33 | d.data = nil
34 | return nil
35 | }
36 |
37 | //Type for zip files read from disk
38 | type ZipFile struct {
39 | data *zip.ReadCloser
40 | }
41 |
42 | func (d ZipFile) files() []*zip.File {
43 | return d.data.File
44 | }
45 |
46 | func (d ZipFile) close() error {
47 | return d.data.Close()
48 | }
49 |
50 | type ReplaceDocx struct {
51 | ZipReader ZipData
52 | Content string
53 | }
54 |
55 | type Text struct {
56 | Words string `json:"word"`
57 | Color string `json:"color"`
58 | Size string `json:"size"`
59 | Isbold bool `json:"isbold"`
60 | IsCenter bool `json:"iscenter"`
61 | }
62 |
63 | type TableTHead struct {
64 | TData interface{} `json:"tdata"`
65 | TDW int `json:"tdw"`
66 | }
67 |
68 | //TableTD descripes every block of the table
69 | type TableTD struct {
70 | //TData refers block's element
71 | TData []interface{} `json:"tdata"`
72 | //TDBG refers block's background
73 | TDBG int `json:"tdbg"`
74 | TDW int `json:"tdw"`
75 | TDM int `json:"tdm"` // 0 - 无 1 - 开始 2 - 结束
76 | }
77 |
78 | //Table include table configuration.
79 | type Table struct {
80 | //Tbname is the name of the table
81 | Tbname string `json:"tbname"`
82 | //Text OR Image in the sanme line
83 | Inline bool `json:"inline"`
84 | //Table data except table head
85 | TableBody [][]*TableTD `json:"tablebody"`
86 | //Table head data
87 | TableHead []*TableTHead `json:"tablehead"`
88 | //Thcenter set table head center word
89 | Thcenter bool `json:"thcenter"`
90 | }
91 |
92 | func (r *ReplaceDocx) Editable() *Docx {
93 | return &Docx{
94 | Files: r.ZipReader.files(),
95 | Content: r.Content,
96 | }
97 | }
98 |
99 | func (r *ReplaceDocx) Close() error {
100 | return r.ZipReader.close()
101 | }
102 |
103 | type Docx struct {
104 | Files []*zip.File
105 | Content string
106 | }
107 |
108 | func ReadDocxFromMemory(data io.ReaderAt, size int64) (*ReplaceDocx, error) {
109 | reader, err := zip.NewReader(data, size)
110 | if err != nil {
111 | return nil, err
112 | }
113 | zipData := ZipInMemory{data: reader}
114 | return ReadDocx(zipData)
115 | }
116 |
117 | func ReadDocxFile(path string) (*ReplaceDocx, error) {
118 | reader, err := zip.OpenReader(path)
119 | if err != nil {
120 | return nil, err
121 | }
122 | zipData := ZipFile{data: reader}
123 | return ReadDocx(zipData)
124 | }
125 |
126 | func ReadDocx(reader ZipData) (*ReplaceDocx, error) {
127 | content, err := readText(reader.files())
128 | if err != nil {
129 | return nil, err
130 | }
131 |
132 | return &ReplaceDocx{ZipReader: reader, Content: content}, nil
133 | }
134 |
135 | func readText(files []*zip.File) (text string, err error) {
136 | var documentFile *zip.File
137 | documentFile, err = retrieveWordDoc(files)
138 | if err != nil {
139 | return text, err
140 | }
141 | var documentReader io.ReadCloser
142 | documentReader, err = documentFile.Open()
143 | if err != nil {
144 | return text, err
145 | }
146 |
147 | text, err = wordDocToString(documentReader)
148 | return
149 | }
150 |
151 | func wordDocToString(reader io.Reader) (string, error) {
152 | b, err := ioutil.ReadAll(reader)
153 | if err != nil {
154 | return "", err
155 | }
156 | return string(b), nil
157 | }
158 |
159 | func retrieveWordDoc(files []*zip.File) (file *zip.File, err error) {
160 | for _, f := range files {
161 | if f.Name == "word/document.xml" {
162 | file = f
163 | }
164 | }
165 | if file == nil {
166 | err = errors.New("document.xml file not found")
167 | }
168 | return
169 | }
170 |
171 | func (d *Docx) ReplaceRaw(oldString string, newString string, num int) {
172 | d.Content = strings.Replace(d.Content, fmt.Sprintf("{{%s}}", oldString), newString, num)
173 | }
174 |
175 | func (d *Docx) Replace(oldString string, newString string, num int) (err error) {
176 | oldString, err = encode(oldString)
177 | if err != nil {
178 | return err
179 | }
180 | newString, err = encode(newString)
181 | if err != nil {
182 | return err
183 | }
184 | d.Content = strings.Replace(d.Content, fmt.Sprintf("{{%s}}", oldString), newString, num)
185 |
186 | return nil
187 | }
188 |
189 | func (d *Docx) ReplaceXML(oldString string, newString string, num int) {
190 | d.Content = strings.Replace(d.Content, fmt.Sprintf("{{%s}}", oldString), newString, num)
191 | }
192 |
193 | //WriteTable ==表格的格式
194 | func (d *Docx) ReplaceTable(table *Table) error {
195 | XMLTable := bytes.Buffer{}
196 | inline := table.Inline
197 | tableBody := table.TableBody
198 | tableHead := table.TableHead
199 | var used bool
200 | used = false
201 |
202 | //handle TableHead :Split with TableBody
203 | if tableHead != nil {
204 | XMLTable.WriteString(XMLTableHead)
205 | XMLTable.WriteString(XMLTableGridBegin)
206 | for _, h := range tableHead {
207 | gcw := fmt.Sprintf(XMLTableGridCol, strconv.FormatInt(int64(h.TDW), 10))
208 | XMLTable.WriteString(gcw)
209 | }
210 | XMLTable.WriteString(XMLTableGridEnd)
211 |
212 | XMLTable.WriteString(XMLTableHeadTR)
213 | for _, rowdata := range tableHead {
214 | thw := fmt.Sprintf(XMLHeadTableTDBegin, strconv.FormatInt(int64(rowdata.TDW), 10))
215 | XMLTable.WriteString(thw)
216 | if table.Thcenter {
217 | XMLTable.WriteString(XMLHeadTableTDBegin2C)
218 | } else {
219 | XMLTable.WriteString(XMLHeadTableTDBegin2)
220 | }
221 | if text, ok := rowdata.TData.(*Text); ok {
222 | //not
223 | color := text.Color
224 | size := text.Size
225 | word := text.Words
226 | var data string
227 | if text.IsCenter {
228 | if text.Isbold {
229 | data = fmt.Sprintf(XMLHeadtableTDTextBC, color, size, size, word)
230 | } else {
231 | data = fmt.Sprintf(XMLHeadtableTDTextC, color, size, size, word)
232 | }
233 | } else {
234 | if text.Isbold {
235 | data = fmt.Sprintf(XMLHeadtableTDTextB, color, size, size, word)
236 | } else {
237 | data = fmt.Sprintf(XMLHeadtableTDText, color, size, size, word)
238 | }
239 | }
240 | XMLTable.WriteString(data)
241 | }
242 | XMLTable.WriteString(XMLIMGtail)
243 | XMLTable.WriteString(XMLHeadTableTDEnd)
244 | }
245 | XMLTable.WriteString(XMLTableEndTR)
246 | } else {
247 | XMLTable.WriteString(XMLTableNoHead)
248 | XMLTable.WriteString(XMLTableGridBegin)
249 | if len(tableBody) > 0 {
250 | for _, tb := range tableBody[0] {
251 | gcw := fmt.Sprintf(XMLTableGridCol, strconv.Itoa(tb.TDW))
252 | XMLTable.WriteString(gcw)
253 | }
254 | XMLTable.WriteString(XMLTableGridEnd)
255 | }
256 | }
257 | //Generate formation
258 | for _, v := range tableBody {
259 | XMLTable.WriteString(XMLTableTR)
260 | for _, vv := range v {
261 | //td bg
262 | var td string
263 | if vv.TDM == 1 {
264 | td = fmt.Sprintf(XMLTableMergeSTD, strconv.FormatInt(int64(vv.TDW), 10), strconv.FormatInt(int64(vv.TDBG), 10))
265 | } else if vv.TDM == 2 {
266 | td = fmt.Sprintf(XMLTableMergeCTD, strconv.FormatInt(int64(vv.TDW), 10), strconv.FormatInt(int64(vv.TDBG), 10))
267 | } else {
268 | td = fmt.Sprintf(XMLTableTD, strconv.FormatInt(int64(vv.TDW), 10), strconv.FormatInt(int64(vv.TDBG), 10))
269 | }
270 | XMLTable.WriteString(td)
271 | if vv.TDM < 2 {
272 | tds := 0
273 | for _, vvv := range vv.TData {
274 | table, ok := vvv.(*Table)
275 | if !inline && !ok {
276 | XMLTable.WriteString(XMLTableTD2)
277 | }
278 | if inline && !ok && tds == 0 {
279 | XMLTable.WriteString(XMLTableTD2)
280 | }
281 | //if td is a table
282 | if ok {
283 | //end with table
284 | used = true
285 | tablestr, err := replaceTableToBuffer(table)
286 | if err != nil {
287 | return err
288 | }
289 | XMLTable.WriteString(tablestr)
290 | // FIXME: magic operation
291 | XMLTable.WriteString(XMLMagicFooter)
292 | //image or text
293 | } else {
294 | if text, ko := vvv.(*Text); ko {
295 | if vv.TDM == 1 {
296 | text.IsCenter = true
297 | }
298 | if text.IsCenter {
299 | if text.Isbold {
300 | XMLTable.WriteString(XMLHeadtableTDTextBC)
301 | } else {
302 | XMLTable.WriteString(XMLHeadtableTDTextC)
303 | }
304 | } else {
305 | if text.Isbold {
306 | XMLTable.WriteString(XMLHeadtableTDTextB)
307 | } else {
308 | XMLTable.WriteString(XMLHeadtableTDText)
309 | }
310 | }
311 | }
312 | used = false
313 | if !inline {
314 | XMLTable.WriteString(XMLIMGtail)
315 | }
316 | }
317 | tds++
318 | }
319 | }
320 | if inline && !used {
321 | XMLTable.WriteString(XMLIMGtail)
322 | }
323 | XMLTable.WriteString(XMLHeadTableTDEnd)
324 | }
325 | XMLTable.WriteString(XMLTableEndTR)
326 | }
327 | XMLTable.WriteString(XMLTableFooter)
328 | //serialization
329 | var rows []interface{}
330 | for _, row := range tableBody {
331 | for _, rowdata := range row {
332 | for _, rowEle := range rowdata.TData {
333 | if _, ok := rowEle.([][][]interface{}); !ok {
334 | if text, ok := rowEle.(*Text); ok {
335 | tColor := text.Color
336 | tSize := text.Size
337 | tWord := text.Words
338 | rows = append(rows, tColor, tSize, tSize, tWord)
339 | }
340 | }
341 | }
342 | }
343 | }
344 |
345 | //data fill in
346 | tabledata := fmt.Sprintf(XMLTable.String(), rows...)
347 | // fmt.Printf("table XML内容:%+v", tabledata)
348 | d.ReplaceXML(table.Tbname, tabledata, -1)
349 | return nil
350 | }
351 |
352 | func replaceTableToBuffer(table *Table) (string, error) {
353 | tableHead := table.TableHead
354 | tableBody := table.TableBody
355 | inline := table.Inline
356 | XMLTable := bytes.Buffer{}
357 | var Bused bool
358 | Bused = false
359 | //handle TableHead :Split with TableBody
360 | if tableHead != nil {
361 | //表格中的表格为无边框形式
362 | XMLTable.WriteString(XMLTableInTableHead)
363 | XMLTable.WriteString(XMLTableHeadTR)
364 | for _, rowdata := range tableHead {
365 | thw := fmt.Sprintf(XMLHeadTableTDBegin, strconv.FormatInt(int64(rowdata.TDW), 10))
366 | XMLTable.WriteString(thw)
367 | if table.Thcenter {
368 | XMLTable.WriteString(XMLHeadTableTDBegin2C)
369 | } else {
370 | XMLTable.WriteString(XMLHeadTableTDBegin2)
371 | }
372 | if text, ok := rowdata.TData.(*Text); ok {
373 | color := text.Color
374 | size := text.Size
375 | word := text.Words
376 | var data string
377 | if text.IsCenter {
378 | if text.Isbold {
379 | data = fmt.Sprintf(XMLHeadtableTDTextBC, color, size, size, word)
380 | } else {
381 | data = fmt.Sprintf(XMLHeadtableTDTextC, color, size, size, word)
382 | }
383 | } else {
384 | if text.Isbold {
385 | data = fmt.Sprintf(XMLHeadtableTDTextB, color, size, size, word)
386 | } else {
387 | data = fmt.Sprintf(XMLHeadtableTDText, color, size, size, word)
388 | }
389 | }
390 | XMLTable.WriteString(data)
391 | }
392 | XMLTable.WriteString(XMLIMGtail)
393 | XMLTable.WriteString(XMLHeadTableTDEnd)
394 | }
395 | XMLTable.WriteString(XMLTableEndTR)
396 | } else {
397 | XMLTable.WriteString(XMLTableInTableNoHead)
398 | }
399 |
400 | //Generate formation
401 | for _, v := range tableBody {
402 | XMLTable.WriteString(XMLTableTR)
403 |
404 | for _, vv := range v {
405 | var ttd string
406 | if vv.TDM == 1 {
407 | ttd = fmt.Sprintf(XMLTableInTableMergeSTD, strconv.FormatInt(int64(vv.TDW), 10))
408 | } else if vv.TDM == 2 {
409 | ttd = fmt.Sprintf(XMLTableInTableMergeCTD, strconv.FormatInt(int64(vv.TDW), 10))
410 | } else {
411 | ttd = fmt.Sprintf(XMLTableInTableTD, strconv.FormatInt(int64(vv.TDW), 10))
412 | }
413 |
414 | XMLTable.WriteString(ttd)
415 | if vv.TDM < 2 {
416 | tds := 0
417 | if inline {
418 | XMLTable.WriteString(XMLTableTD2)
419 |
420 | }
421 | for _, vvv := range vv.TData {
422 | table, ok := vvv.(*Table)
423 | if !inline && !ok {
424 | XMLTable.WriteString(XMLTableTD2)
425 | }
426 | if ok {
427 | Bused = true
428 | tablestr, err := replaceTableToBuffer(table)
429 | if err != nil {
430 | return "", err
431 | }
432 | XMLTable.WriteString(tablestr)
433 | XMLTable.WriteString(XMLMagicFooter)
434 | } else {
435 | if text, ko := vvv.(*Text); ko {
436 | if vv.TDM == 1 {
437 | text.IsCenter = true
438 | }
439 | if text.IsCenter {
440 | if text.Isbold {
441 | XMLTable.WriteString(XMLHeadtableTDTextBC)
442 | } else {
443 | XMLTable.WriteString(XMLHeadtableTDTextC)
444 | }
445 | } else {
446 | if text.Isbold {
447 | XMLTable.WriteString(XMLHeadtableTDTextB)
448 | } else {
449 | fmt.Printf("model:%+v", vv)
450 | XMLTable.WriteString(XMLHeadtableTDText)
451 | }
452 | }
453 | }
454 | //not end with table
455 | Bused = false
456 | var next bool
457 | if tds < len(vv.TData)-1 {
458 | _, next = vv.TData[tds+1].(*Table)
459 | }
460 |
461 | if !inline {
462 | XMLTable.WriteString(XMLIMGtail)
463 | } else if inline && next {
464 | XMLTable.WriteString(XMLIMGtail)
465 | }
466 | }
467 | tds++
468 | }
469 | if inline && !Bused {
470 | XMLTable.WriteString(XMLIMGtail)
471 | }
472 | }
473 | XMLTable.WriteString(XMLHeadTableTDEnd)
474 | }
475 | XMLTable.WriteString(XMLTableEndTR)
476 | }
477 | XMLTable.WriteString(XMLTableFooter)
478 | //serialization
479 | var rows []interface{}
480 |
481 | for _, row := range tableBody {
482 | for _, rowdata := range row {
483 | for _, rowEle := range rowdata.TData {
484 | if _, ok := rowEle.([][][]interface{}); !ok {
485 | if text, ok := rowEle.(*Text); ok {
486 | tColor := text.Color
487 | tSize := text.Size
488 | tWord := text.Words
489 | rows = append(rows, tColor, tSize, tSize, tWord)
490 | }
491 | }
492 | }
493 | }
494 | }
495 |
496 | //data fill in
497 | tabledata := fmt.Sprintf(XMLTable.String(), rows...)
498 |
499 | return tabledata, nil
500 | }
501 |
502 | //NewTable create a table
503 | func NewTable(d *Docx, tbname string, inline bool, tableBody [][]*TableTD, tableHead []*TableTHead, headCenter bool) (*Table, error) {
504 | table := &Table{}
505 | table.Tbname = tbname
506 | table.Inline = inline
507 | table.TableBody = tableBody
508 | table.TableHead = tableHead
509 | table.Thcenter = headCenter
510 | err := d.ReplaceTable(table)
511 | return table, err
512 | }
513 |
514 | func NewTableTD(tdata []interface{}, tdproperty map[string]interface{}) *TableTD {
515 | Tabletd := &TableTD{
516 | TData: tdata,
517 | TDBG: 0,
518 | TDW: 0,
519 | TDM: 0,
520 | }
521 | if tdproperty != nil {
522 | if tdproperty["tdbg"] != nil {
523 | Tabletd.TDBG = tdproperty["tdbg"].(int)
524 | }
525 | if tdproperty["tdw"] != nil {
526 | Tabletd.TDW = tdproperty["tdw"].(int)
527 | }
528 | if tdproperty["tdm"] != nil {
529 | Tabletd.TDM = tdproperty["tdm"].(int)
530 | }
531 | }
532 | return Tabletd
533 | }
534 |
535 | func NewText(words string) *Text {
536 | words, _ = encode(words)
537 | text := &Text{}
538 | text.Words = words
539 | text.Color = "000000"
540 | text.Size = "19"
541 | text.Isbold = false
542 | text.IsCenter = false
543 | return text
544 | }
545 |
546 | func (d *Docx) Write(ioWriter io.Writer) (err error) {
547 | w := zip.NewWriter(ioWriter)
548 | for _, file := range d.Files {
549 | var writer io.Writer
550 | var readCloser io.ReadCloser
551 |
552 | writer, err = w.Create(file.Name)
553 | if err != nil {
554 | return err
555 | }
556 | readCloser, err = file.Open()
557 | if err != nil {
558 | return err
559 | }
560 | if file.Name == "word/document.xml" {
561 | writer.Write([]byte(d.Content))
562 | } else {
563 | writer.Write(streamToByte(readCloser))
564 | }
565 | }
566 | w.Close()
567 | return
568 | }
569 |
570 | func (d *Docx) WriteToFile(path string) (err error) {
571 | var target *os.File
572 | target, err = os.Create(path)
573 | if err != nil {
574 | return
575 | }
576 | defer target.Close()
577 | err = d.Write(target)
578 | return
579 | }
580 |
581 | func streamToByte(stream io.Reader) []byte {
582 | buf := new(bytes.Buffer)
583 | buf.ReadFrom(stream)
584 | return buf.Bytes()
585 | }
586 |
587 | func encode(s string) (string, error) {
588 | var b bytes.Buffer
589 | enc := xml.NewEncoder(bufio.NewWriter(&b))
590 | if err := enc.Encode(s); err != nil {
591 | return s, err
592 | }
593 | output := strings.Replace(b.String(), "", "", 1) // remove string tag
594 | output = strings.Replace(output, "", "", 1)
595 | output = strings.Replace(output, "
", "", -1) // \r\n => newline
596 | return output, nil
597 | }
598 |
--------------------------------------------------------------------------------
/docx/escape.go:
--------------------------------------------------------------------------------
1 | package docx
2 |
3 | import (
4 | "bytes"
5 | "strings"
6 | )
7 |
8 | type writer interface {
9 | WriteString(string) (int, error)
10 | }
11 |
12 | const escapedChars = `&'<>"`
13 |
14 | func escape(w writer, s string) error {
15 | i := strings.IndexAny(s, escapedChars)
16 | for i != -1 {
17 | if _, err := w.WriteString(s[:i]); err != nil {
18 | return err
19 | }
20 | var esc string
21 | switch s[i] {
22 | case '&':
23 | esc = "&"
24 | case '\'':
25 | esc = "'"
26 | case '<':
27 | esc = "<"
28 | case '>':
29 | esc = ">"
30 | case '"':
31 | esc = """
32 | default:
33 | panic("unrecognized escape character")
34 | }
35 | s = s[i+1:]
36 | if _, err := w.WriteString(esc); err != nil {
37 | return err
38 | }
39 | i = strings.IndexAny(s, escapedChars)
40 | }
41 | _, err := w.WriteString(s)
42 | return err
43 | }
44 |
45 | // Escape escapes special HTML characters.
46 | //
47 | // It can be used by helpers that return a SafeString and that need to escape some content by themselves.
48 | func Escape(s string) string {
49 | if strings.IndexAny(s, escapedChars) == -1 {
50 | return s
51 | }
52 | var buf bytes.Buffer
53 | escape(&buf, s)
54 | return buf.String()
55 | }
56 |
--------------------------------------------------------------------------------
/docx/xml_config.go:
--------------------------------------------------------------------------------
1 | package docx
2 |
3 | const (
4 |
5 | //XMLText == 正文
6 | XMLText = `
7 |
8 |
9 |
10 |
11 |
12 |
13 | %s
14 |
15 |
16 | `
17 | //XMLCenterText == 居中正文
18 | XMLCenterText = `
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | %s
29 |
30 |
31 | `
32 | //XMLCenterBoldText 居中粗体
33 | XMLCenterBoldText = `
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | %s
46 |
47 |
48 | `
49 | //XMLBoldText ==粗体
50 | XMLBoldText = `
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | %s
59 |
60 |
61 | `
62 | //XMLInlineText == 不换行的正文
63 | XMLInlineText = `
64 | %s
65 |
66 | `
67 | //XMLFontStyle defines fontStyle
68 | XMLFontStyle = `
69 |
70 |
71 |
72 |
73 | `
74 | //XMLTableHead ...
75 | XMLTableHead = `
76 |
77 |
78 |
79 |
80 | `
81 | //XMLTableNoHead == 没有表头的样式把table top line remove掉
82 | XMLTableNoHead = `
83 |
84 |
85 |
86 |
87 | `
88 | //XMLTableInTableHead == 表中表的样式头
89 | XMLTableInTableHead = `
90 |
91 |
92 |
93 |
94 |
95 | `
96 | //XMLTableInTableNoHead ...
97 | XMLTableInTableNoHead = `
98 |
99 |
100 |
101 |
102 |
103 | `
104 | //XMLTableTR ...
105 | XMLTableTR = `
106 | `
107 | //XMLTableHeadTR ...
108 | XMLTableHeadTR = `
109 | `
110 | //XMLTableTD ...
111 | XMLTableTD = `
112 |
113 |
114 |
115 |
116 | `
117 | // XMLTableMergeSTD ...
118 | XMLTableMergeSTD = `
119 |
120 |
121 |
122 |
123 |
124 |
125 | `
126 | // XMLTableMergeCTD ...
127 | XMLTableMergeCTD = `
128 |
129 |
130 |
131 |
132 |
133 |
134 | `
135 |
136 | //XMLTableInTableTD ...
137 | XMLTableInTableTD = `
138 |
139 |
140 |
141 | `
142 | // XMLTableInTableMergeSTD ...
143 | XMLTableInTableMergeSTD = `
144 |
145 |
146 |
147 |
148 |
149 | `
150 | // XMLTableInTableMergeCTD ...
151 | XMLTableInTableMergeCTD = `
152 |
153 |
154 |
155 |
156 |
157 | `
158 |
159 | //XMLTableTD2 ...
160 | XMLTableTD2 = `
161 |
162 |
163 |
164 |
165 |
166 |
167 | `
168 | //XMLHeadTableTDBegin ...
169 | XMLHeadTableTDBegin = `
170 |
171 |
172 |
173 | `
174 | //XMLHeadTableInTableTDBegin ...
175 | XMLHeadTableInTableTDBegin = `
176 |
177 |
178 |
179 | `
180 | //XMLHeadTableTDBegin2 ...
181 | XMLHeadTableTDBegin2 = `
182 |
183 |
184 |
185 | `
186 |
187 | //XMLHeadTableTDBegin2C ...
188 | XMLHeadTableTDBegin2C = `
189 |
190 |
191 |
192 | `
193 |
194 | //XMLHeadtableTDTextB ...
195 | XMLHeadtableTDTextB = `
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 | %s
204 |
205 | `
206 | //XMLHeadtableTDTextC ...
207 | XMLHeadtableTDTextC = `
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 | %s
217 |
218 | `
219 | //XMLHeadtableTDTextBC ...
220 | XMLHeadtableTDTextBC = `
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | %s
231 |
232 | `
233 | //XMLHeadtableTDText ...
234 | XMLHeadtableTDText = `
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 | %s
245 |
246 | `
247 |
248 | // XMLTableGridBegin ...
249 | XMLTableGridBegin = `
250 | `
251 |
252 | // XMLTableGridCol ...
253 | XMLTableGridCol = `
254 | `
255 |
256 | // XMLTableGridEnd ...
257 | XMLTableGridEnd = `
258 |
259 | `
260 |
261 | //XMLHeadTableTDEnd ...
262 | XMLHeadTableTDEnd = `
263 |
264 | `
265 | //XMLTableEndTR ...
266 | XMLTableEndTR = `
267 | `
268 | //XMLMagicFooter HACK:I struggle for a long time,at last ,I find it is necessary,and don't konw why.
269 | XMLMagicFooter = `
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 | `
281 |
282 | //XMLTableFooter ...
283 | XMLTableFooter = `
284 |
285 | `
286 |
287 | //XMLIMGtail ...
288 | XMLIMGtail = `
289 | `
290 | //XMLBr == 换行
291 | XMLBr = `
292 | `
293 | )
294 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 |
7 | "github.com/CR903/smarterlab-docx/docx"
8 | )
9 |
10 | func main() {
11 |
12 | r, err := docx.ReadDocxFile("template.docx")
13 | if err != nil {
14 | panic(err)
15 | }
16 |
17 | docxObj := r.Editable()
18 | docxObj.Replace("DETECTNUMBER", "JC2018001", -1)
19 |
20 | // table1
21 | // row1TD1Key := docx.NewTableTD([]interface{}{docx.NewText("委托单位")}, map[string]interface{}{"tdbg": 1, "tdw": 2095})
22 | // row1TD1Value := docx.NewTableTD([]interface{}{docx.NewText("立为科技")}, map[string]interface{}{"tdbg": 1, "tdw": 2095})
23 | // row1TD2Key := docx.NewTableTD([]interface{}{docx.NewText("联系人")}, map[string]interface{}{"tdbg": 1, "tdw": 2095})
24 | // row1TD2Value := docx.NewTableTD([]interface{}{docx.NewText("联系人")}, map[string]interface{}{"tdbg": 1, "tdw": 2095})
25 |
26 | // row2TD1Key := docx.NewTableTD([]interface{}{docx.NewText("委托单位地址")}, nil)
27 | // row2TD1Value := docx.NewTableTD([]interface{}{docx.NewText("地址1")}, nil)
28 | // row2TD2Key := docx.NewTableTD([]interface{}{docx.NewText("电 话")}, nil)
29 | // row2TD2Value := docx.NewTableTD([]interface{}{docx.NewText("13600000000")}, nil)
30 |
31 | // row3TD1Key := docx.NewTableTD([]interface{}{docx.NewText("付款单位")}, nil)
32 | // row3TD1Value := docx.NewTableTD([]interface{}{docx.NewText("")}, nil)
33 | // row3TD2Key := docx.NewTableTD([]interface{}{docx.NewText("税号")}, nil)
34 | // row3TD2Value := docx.NewTableTD([]interface{}{docx.NewText("")}, nil)
35 |
36 | // row4TD1Key := docx.NewTableTD([]interface{}{docx.NewText("受测单位")}, nil)
37 | // row4TD1Value := docx.NewTableTD([]interface{}{docx.NewText("垃圾场")}, nil)
38 | // row4TD2Key := docx.NewTableTD([]interface{}{docx.NewText("联系人")}, nil)
39 | // row4TD2Value := docx.NewTableTD([]interface{}{docx.NewText("赵六")}, nil)
40 |
41 | // row5TD1Key := docx.NewTableTD([]interface{}{docx.NewText("通讯地址")}, nil)
42 | // row5TD1Value := docx.NewTableTD([]interface{}{docx.NewText("地址4")}, nil)
43 | // row5TD2Key := docx.NewTableTD([]interface{}{docx.NewText("电 话")}, nil)
44 | // row5TD2Value := docx.NewTableTD([]interface{}{docx.NewText("13996966666")}, nil)
45 |
46 | // row6TD1Key := docx.NewTableTD([]interface{}{docx.NewText("样品名称")}, nil)
47 | // row6TD1Value := docx.NewTableTD([]interface{}{docx.NewText("水")}, nil)
48 | // row6TD2Key := docx.NewTableTD([]interface{}{docx.NewText("样品数量")}, nil)
49 | // row6TD2Value := docx.NewTableTD([]interface{}{docx.NewText("12.00")}, nil)
50 |
51 | // row7TD1Key := docx.NewTableTD([]interface{}{docx.NewText("样品名称")}, nil)
52 | // row7TD1Value := docx.NewTableTD([]interface{}{docx.NewText("气")}, nil)
53 | // row7TD2Key := docx.NewTableTD([]interface{}{docx.NewText("样品数量")}, nil)
54 | // row7TD2Value := docx.NewTableTD([]interface{}{docx.NewText("12.00")}, nil)
55 |
56 | // row8TD1Key := docx.NewTableTD([]interface{}{docx.NewText("检测项目")}, map[string]interface{}{"tdm": 1})
57 | // row8TD1Value := docx.NewTableTD([]interface{}{docx.NewText("水:砷, ph")}, map[string]interface{}{"tdbg": 3})
58 |
59 | // row81TD1Key := docx.NewTableTD([]interface{}{docx.NewText("")}, map[string]interface{}{"tdm": 2})
60 | // row81TD1Value := docx.NewTableTD([]interface{}{docx.NewText("气:测试检测项, 四氯化碳")}, map[string]interface{}{"tdbg": 3})
61 |
62 | // row82TD1Key := docx.NewTableTD([]interface{}{docx.NewText("")}, map[string]interface{}{"tdm": 2})
63 | // row82TD1Value := docx.NewTableTD([]interface{}{docx.NewText("气:测试检测项, 四氯化碳")}, map[string]interface{}{"tdbg": 3})
64 |
65 | // row9TD1Key := docx.NewTableTD([]interface{}{docx.NewText("检测标准/检测方法")}, nil)
66 | // row9TD1Value := docx.NewTableTD([]interface{}{docx.NewText("标准1/标准1, 标准1/标准1, 测试检测标准一号/测试检测标准一号, 标准1/标准1")}, map[string]interface{}{"tdbg": 3, "tdw": 0})
67 |
68 | // row10TD1Key := docx.NewTableTD([]interface{}{docx.NewText("具体检测项目及执行标准/检测方法以检测项目附件单为准(需双方签字确认)")}, map[string]interface{}{"tdbg": 4, "tdw": 0})
69 |
70 | // row11TD1Key := docx.NewTableTD([]interface{}{docx.NewText("服务类型")}, nil)
71 | // row11TD1Value := docx.NewTableTD([]interface{}{docx.NewText("☑ 标准服务:7个工作日"), docx.NewText("☐ 加急服务:3.5个工作日 加收100%附加费")}, map[string]interface{}{"tdbg": 3, "tdw": 0})
72 |
73 | // row12TD1Key := docx.NewTableTD([]interface{}{docx.NewText("报告份数")}, nil)
74 | // row12TD1Value := docx.NewTableTD([]interface{}{docx.NewText("")}, nil)
75 | // row12TD2Key := docx.NewTableTD([]interface{}{docx.NewText("预计完成时间")}, nil)
76 | // row12TD2Value := docx.NewTableTD([]interface{}{docx.NewText("2018-10-31")}, nil)
77 |
78 | // row13TD1Key := docx.NewTableTD([]interface{}{docx.NewText("取报告方式")}, nil)
79 | // row13TD1Value := docx.NewTableTD([]interface{}{docx.NewText("☑ 自取"), docx.NewText("☐ 快递")}, nil)
80 | // row13TD2Key := docx.NewTableTD([]interface{}{docx.NewText("总费用")}, nil)
81 | // row13TD2Value := docx.NewTableTD([]interface{}{docx.NewText("200.000000")}, nil)
82 |
83 | // row14TD1Key := docx.NewTableTD([]interface{}{docx.NewText("说明:"),
84 | // docx.NewText("1)委托单位如对检测方法有特殊要求,请在执行标准/检测方法中详细说明。"),
85 | // docx.NewText("2)委托单位如对盖资质章有要求,请在备注中说明。"),
86 | // docx.NewText("3)若双方另有其他要求可附页说明。")}, map[string]interface{}{"tdbg": 4, "tdw": 0})
87 |
88 | // row15TD1Key := docx.NewTableTD([]interface{}{docx.NewText("备注:"),
89 | // docx.NewText("")}, map[string]interface{}{"tdbg": 4, "tdw": 0})
90 |
91 | // table := [][]*docx.TableTD{
92 | // {row1TD1Key, row1TD1Value, row1TD2Key, row1TD2Value},
93 | // {row2TD1Key, row2TD1Value, row2TD2Key, row2TD2Value},
94 | // {row3TD1Key, row3TD1Value, row3TD2Key, row3TD2Value},
95 | // {row4TD1Key, row4TD1Value, row4TD2Key, row4TD2Value},
96 | // {row5TD1Key, row5TD1Value, row5TD2Key, row5TD2Value},
97 | // {row6TD1Key, row6TD1Value, row6TD2Key, row6TD2Value},
98 | // {row7TD1Key, row7TD1Value, row7TD2Key, row7TD2Value},
99 | // {row8TD1Key, row8TD1Value},
100 | // {row81TD1Key, row81TD1Value},
101 | // {row82TD1Key, row82TD1Value},
102 | // {row9TD1Key, row9TD1Value},
103 | // {row10TD1Key},
104 | // {row11TD1Key, row11TD1Value},
105 | // {row12TD1Key, row12TD1Value, row12TD2Key, row12TD2Value},
106 | // {row13TD1Key, row13TD1Value, row13TD2Key, row13TD2Value},
107 | // {row14TD1Key},
108 | // {row15TD1Key},
109 | // }
110 | // _, err1 := docx.NewTable(docxObj, "DETECTCONTENT", false, table, nil, false)
111 | // if err1 != nil {
112 | // panic(err1)
113 | // }
114 |
115 | // Table2
116 | table := make([][]*docx.TableTD, 0)
117 | tableHead := []*docx.TableTHead{
118 | &docx.TableTHead{TData: docx.NewText("样品编号"), TDW: 823},
119 | &docx.TableTHead{TData: docx.NewText("样品名称"), TDW: 823},
120 | &docx.TableTHead{TData: docx.NewText("检测项"), TDW: 823},
121 | &docx.TableTHead{TData: docx.NewText("检测标准"), TDW: 823},
122 | &docx.TableTHead{TData: docx.NewText("限值要求"), TDW: 823},
123 | &docx.TableTHead{TData: docx.NewText("截止日期"), TDW: 823},
124 | &docx.TableTHead{TData: docx.NewText("检验组"), TDW: 823},
125 | &docx.TableTHead{TData: docx.NewText("检验人"), TDW: 823},
126 | &docx.TableTHead{TData: docx.NewText("备注"), TDW: 823},
127 | &docx.TableTHead{TData: docx.NewText("状态"), TDW: 823},
128 | }
129 |
130 | for t := 1; t <= 10; t++ {
131 |
132 | var td = make([]*docx.TableTD, 0)
133 | for i := 1; i <= 10; i++ {
134 | tdValue := fmt.Sprintf("列:%s", strconv.Itoa(i))
135 | row1TD1Key := docx.NewTableTD([]interface{}{docx.NewText(tdValue)}, map[string]interface{}{"tdbg": 1, "tdw": 823})
136 | td = append(td, row1TD1Key)
137 | }
138 | table = append(table, td)
139 | }
140 |
141 | _, err1 := docx.NewTable(docxObj, "DETECTCONTENT", true, table, tableHead, true)
142 | if err1 != nil {
143 | panic(err1)
144 | }
145 | docxObj.WriteToFile("template_new.docx")
146 | r.Close()
147 | fmt.Println("Success")
148 | }
149 |
--------------------------------------------------------------------------------
/template.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CR903/smarterlab-docx/a6e77e7daf438eee1fa75bc3801c188151c7ef01/template.docx
--------------------------------------------------------------------------------
/template_new.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CR903/smarterlab-docx/a6e77e7daf438eee1fa75bc3801c188151c7ef01/template_new.docx
--------------------------------------------------------------------------------