mirror of
https://github.com/fumiama/go-docx.git
synced 2026-06-06 00:00:24 +08:00
First commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
docxlib
|
||||
23
LICENSE
Normal file
23
LICENSE
Normal file
@@ -0,0 +1,23 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 gingfrederik
|
||||
Copyright (c) 2021 Gonzalo Fernandez-Victorio
|
||||
Copyright (c) 2021 Basement Crowd Ltd (https://www.basementcrowd.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
61
README.md
Normal file
61
README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Docx library
|
||||
|
||||
Yet another library to manipulate .docx (Microsoft Word) files in Go.
|
||||
|
||||
## Introduction
|
||||
|
||||
As part of my work for [Basement Crowd](https://www.basementcrowd.com) y [FromCounsel](https://www.fromcounsel.com), we were in need of a basic library to manipulate (both read and write)
|
||||
|
||||
The difference with other projects is the following:
|
||||
|
||||
- [UniOffice](https://github.com/unidoc/unioffice) is probably the most complete but it is also commercial (you need to pay). It also very complete, but too much for my needs.
|
||||
|
||||
- [gingfrederik/docx](https://github.com/gingfrederik/docx) only allows to write.
|
||||
|
||||
There are also a couple of other projects [kingzbauer/docx](https://github.com/kingzbauer/docx) and [nguyenthenguyen/docx](https://github.com/nguyenthenguyen/docx)
|
||||
|
||||
[gingfrederik/docx](https://github.com/gingfrederik/docx) was a heavy influence (the original structures and the main method come from that project).
|
||||
|
||||
However, the structures didn't handle reading and extending them was particularly difficult due to Go xml parser being limited and [6 year old bug](https://github.com/golang/go/issues/9519).
|
||||
|
||||
Additionally, my requirements go beyond the original structure and a hard fork seemed more sensible.
|
||||
|
||||
The plan is to evolve the library, so the API is likely to change according to my company's needs. But please do feel free to send patches, reports and PRs or fork.
|
||||
|
||||
In the mean time, shared as an example.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Install
|
||||
|
||||
Go modules supported
|
||||
|
||||
```sh
|
||||
go get github.com/gonfva/docxlib
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
See [main](main/main.go) for an example
|
||||
|
||||
```
|
||||
$ ./docxlib
|
||||
Preparing new document to write at /tmp/new-file.docx
|
||||
Document writen.
|
||||
Now trying to read it
|
||||
We've found a new run with the text ->test
|
||||
We've found a new run with the text ->test font size
|
||||
We've found a new run with the text ->test color
|
||||
We've found a new run with the text ->test font size and color
|
||||
End of main
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ go build -o docxlib ./main
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT. See [LICENSE](LICENSE)
|
||||
389
constants.go
Normal file
389
constants.go
Normal file
@@ -0,0 +1,389 @@
|
||||
package docxlib
|
||||
|
||||
const (
|
||||
TEMP_REL = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
|
||||
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
||||
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
||||
</Relationships>`
|
||||
TEMP_DOCPROPS_APP = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>Go DOCX</Application></Properties>`
|
||||
TEMP_DOCPROPS_CORE = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></cp:coreProperties>`
|
||||
TEMP_CONTENT = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
||||
<Default Extension="xml" ContentType="application/xml"/>
|
||||
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
||||
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
|
||||
<Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
|
||||
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
||||
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
|
||||
</Types>`
|
||||
TEMP_WORD_STYLE = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:styles xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
||||
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
|
||||
xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
|
||||
xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid"
|
||||
xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid">
|
||||
<w:docDefaults>
|
||||
<w:rPrDefault>
|
||||
<w:rPr>
|
||||
<w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorEastAsia" w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi"/>
|
||||
<w:kern w:val="2"/>
|
||||
<w:sz w:val="24"/>
|
||||
<w:szCs w:val="24"/>
|
||||
<w:lang w:val="en-US" w:eastAsia="zh-TW" w:bidi="ar-SA"/>
|
||||
</w:rPr>
|
||||
</w:rPrDefault>
|
||||
<w:pPrDefault/>
|
||||
</w:docDefaults>
|
||||
<w:style w:type="paragraph" w:default="1" w:styleId="a">
|
||||
<w:name w:val="Normal"/>
|
||||
<w:qFormat/>
|
||||
<w:rsid w:val="00A955DD"/>
|
||||
<w:pPr>
|
||||
<w:widowControl w:val="0"/>
|
||||
</w:pPr>
|
||||
<w:rPr>
|
||||
<w:szCs w:val="22"/>
|
||||
</w:rPr>
|
||||
</w:style>
|
||||
<w:style w:type="character" w:default="1" w:styleId="a0">
|
||||
<w:name w:val="Default Paragraph Font"/>
|
||||
<w:uiPriority w:val="1"/>
|
||||
<w:semiHidden/>
|
||||
<w:unhideWhenUsed/>
|
||||
</w:style>
|
||||
<w:style w:type="character" w:styleId="a1">
|
||||
<w:name w:val="Hyperlink"/>
|
||||
<w:basedOn w:val="a0"/>
|
||||
<w:uiPriority w:val="99"/>
|
||||
<w:unhideWhenUsed/>
|
||||
<w:rsid w:val="003B745A"/>
|
||||
<w:rPr>
|
||||
<w:color w:val="0563C1" w:themeColor="hyperlink"/>
|
||||
<w:u w:val="single"/>
|
||||
</w:rPr>
|
||||
</w:style>
|
||||
</w:styles>`
|
||||
TEMP_WORD_THEME_THEME = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office-Design">
|
||||
<a:themeElements>
|
||||
<a:clrScheme name="Office">
|
||||
<a:dk1>
|
||||
<a:sysClr val="windowText" lastClr="000000"/>
|
||||
</a:dk1>
|
||||
<a:lt1>
|
||||
<a:sysClr val="window" lastClr="FFFFFF"/>
|
||||
</a:lt1>
|
||||
<a:dk2>
|
||||
<a:srgbClr val="1F497D"/>
|
||||
</a:dk2>
|
||||
<a:lt2>
|
||||
<a:srgbClr val="EEECE1"/>
|
||||
</a:lt2>
|
||||
<a:accent1>
|
||||
<a:srgbClr val="4F81BD"/>
|
||||
</a:accent1>
|
||||
<a:accent2>
|
||||
<a:srgbClr val="C0504D"/>
|
||||
</a:accent2>
|
||||
<a:accent3>
|
||||
<a:srgbClr val="9BBB59"/>
|
||||
</a:accent3>
|
||||
<a:accent4>
|
||||
<a:srgbClr val="8064A2"/>
|
||||
</a:accent4>
|
||||
<a:accent5>
|
||||
<a:srgbClr val="4BACC6"/>
|
||||
</a:accent5>
|
||||
<a:accent6>
|
||||
<a:srgbClr val="F79646"/>
|
||||
</a:accent6>
|
||||
<a:hlink>
|
||||
<a:srgbClr val="0000FF"/>
|
||||
</a:hlink>
|
||||
<a:folHlink>
|
||||
<a:srgbClr val="800080"/>
|
||||
</a:folHlink>
|
||||
</a:clrScheme>
|
||||
<a:fontScheme name="Office">
|
||||
<a:majorFont>
|
||||
<a:latin typeface="Cambria"/>
|
||||
<a:ea typeface=""/>
|
||||
<a:cs typeface=""/>
|
||||
<a:font script="Jpan" typeface="MS Pゴシック"/>
|
||||
<a:font script="Hang" typeface="맑은 고딕"/>
|
||||
<a:font script="Hans" typeface="宋体"/>
|
||||
<a:font script="Hant" typeface="新細明體"/>
|
||||
<a:font script="Arab" typeface="Times New Roman"/>
|
||||
<a:font script="Hebr" typeface="Times New Roman"/>
|
||||
<a:font script="Thai" typeface="Tahoma"/>
|
||||
<a:font script="Ethi" typeface="Nyala"/>
|
||||
<a:font script="Beng" typeface="Vrinda"/>
|
||||
<a:font script="Gujr" typeface="Shruti"/>
|
||||
<a:font script="Khmr" typeface="MoolBoran"/>
|
||||
<a:font script="Knda" typeface="Tunga"/>
|
||||
<a:font script="Guru" typeface="Raavi"/>
|
||||
<a:font script="Cans" typeface="Euphemia"/>
|
||||
<a:font script="Cher" typeface="Plantagenet Cherokee"/>
|
||||
<a:font script="Yiii" typeface="Microsoft Yi Baiti"/>
|
||||
<a:font script="Tibt" typeface="Microsoft Himalaya"/>
|
||||
<a:font script="Thaa" typeface="MV Boli"/>
|
||||
<a:font script="Deva" typeface="Mangal"/>
|
||||
<a:font script="Telu" typeface="Gautami"/>
|
||||
<a:font script="Taml" typeface="Latha"/>
|
||||
<a:font script="Syrc" typeface="Estrangelo Edessa"/>
|
||||
<a:font script="Orya" typeface="Kalinga"/>
|
||||
<a:font script="Mlym" typeface="Kartika"/>
|
||||
<a:font script="Laoo" typeface="DokChampa"/>
|
||||
<a:font script="Sinh" typeface="Iskoola Pota"/>
|
||||
<a:font script="Mong" typeface="Mongolian Baiti"/>
|
||||
<a:font script="Viet" typeface="Times New Roman"/>
|
||||
<a:font script="Uigh" typeface="Microsoft Uighur"/>
|
||||
<a:font script="Geor" typeface="Sylfaen"/>
|
||||
</a:majorFont>
|
||||
<a:minorFont>
|
||||
<a:latin typeface="Arial"/>
|
||||
<a:ea typeface=""/>
|
||||
<a:cs typeface=""/>
|
||||
<a:font script="Jpan" typeface="MS Pゴシック"/>
|
||||
<a:font script="Hang" typeface="맑은 고딕"/>
|
||||
<a:font script="Hans" typeface="宋体"/>
|
||||
<a:font script="Hant" typeface="新細明體"/>
|
||||
<a:font script="Arab" typeface="Arial"/>
|
||||
<a:font script="Hebr" typeface="Arial"/>
|
||||
<a:font script="Thai" typeface="Tahoma"/>
|
||||
<a:font script="Ethi" typeface="Nyala"/>
|
||||
<a:font script="Beng" typeface="Vrinda"/>
|
||||
<a:font script="Gujr" typeface="Shruti"/>
|
||||
<a:font script="Khmr" typeface="DaunPenh"/>
|
||||
<a:font script="Knda" typeface="Tunga"/>
|
||||
<a:font script="Guru" typeface="Raavi"/>
|
||||
<a:font script="Cans" typeface="Euphemia"/>
|
||||
<a:font script="Cher" typeface="Plantagenet Cherokee"/>
|
||||
<a:font script="Yiii" typeface="Microsoft Yi Baiti"/>
|
||||
<a:font script="Tibt" typeface="Microsoft Himalaya"/>
|
||||
<a:font script="Thaa" typeface="MV Boli"/>
|
||||
<a:font script="Deva" typeface="Mangal"/>
|
||||
<a:font script="Telu" typeface="Gautami"/>
|
||||
<a:font script="Taml" typeface="Latha"/>
|
||||
<a:font script="Syrc" typeface="Estrangelo Edessa"/>
|
||||
<a:font script="Orya" typeface="Kalinga"/>
|
||||
<a:font script="Mlym" typeface="Kartika"/>
|
||||
<a:font script="Laoo" typeface="DokChampa"/>
|
||||
<a:font script="Sinh" typeface="Iskoola Pota"/>
|
||||
<a:font script="Mong" typeface="Mongolian Baiti"/>
|
||||
<a:font script="Viet" typeface="Arial"/>
|
||||
<a:font script="Uigh" typeface="Microsoft Uighur"/>
|
||||
<a:font script="Geor" typeface="Sylfaen"/>
|
||||
</a:minorFont>
|
||||
</a:fontScheme>
|
||||
<a:fmtScheme name="Office">
|
||||
<a:fillStyleLst>
|
||||
<a:solidFill>
|
||||
<a:schemeClr val="phClr"/>
|
||||
</a:solidFill>
|
||||
<a:gradFill rotWithShape="1">
|
||||
<a:gsLst>
|
||||
<a:gs pos="0">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="50000"/>
|
||||
<a:satMod val="300000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
<a:gs pos="35000">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="37000"/>
|
||||
<a:satMod val="300000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
<a:gs pos="100000">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="15000"/>
|
||||
<a:satMod val="350000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
</a:gsLst>
|
||||
<a:lin ang="16200000" scaled="1"/>
|
||||
</a:gradFill>
|
||||
<a:gradFill rotWithShape="1">
|
||||
<a:gsLst>
|
||||
<a:gs pos="0">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="100000"/>
|
||||
<a:shade val="100000"/>
|
||||
<a:satMod val="130000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
<a:gs pos="100000">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="50000"/>
|
||||
<a:shade val="100000"/>
|
||||
<a:satMod val="350000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
</a:gsLst>
|
||||
<a:lin ang="16200000" scaled="0"/>
|
||||
</a:gradFill>
|
||||
</a:fillStyleLst>
|
||||
<a:lnStyleLst>
|
||||
<a:ln w="9525" cap="flat" cmpd="sng" algn="ctr">
|
||||
<a:solidFill>
|
||||
<a:schemeClr val="phClr">
|
||||
<a:shade val="95000"/>
|
||||
<a:satMod val="105000"/>
|
||||
</a:schemeClr>
|
||||
</a:solidFill>
|
||||
<a:prstDash val="solid"/>
|
||||
</a:ln>
|
||||
<a:ln w="25400" cap="flat" cmpd="sng" algn="ctr">
|
||||
<a:solidFill>
|
||||
<a:schemeClr val="phClr"/>
|
||||
</a:solidFill>
|
||||
<a:prstDash val="solid"/>
|
||||
</a:ln>
|
||||
<a:ln w="38100" cap="flat" cmpd="sng" algn="ctr">
|
||||
<a:solidFill>
|
||||
<a:schemeClr val="phClr"/>
|
||||
</a:solidFill>
|
||||
<a:prstDash val="solid"/>
|
||||
</a:ln>
|
||||
</a:lnStyleLst>
|
||||
<a:effectStyleLst>
|
||||
<a:effectStyle>
|
||||
<a:effectLst>
|
||||
<a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0">
|
||||
<a:srgbClr val="000000">
|
||||
<a:alpha val="38000"/>
|
||||
</a:srgbClr>
|
||||
</a:outerShdw>
|
||||
</a:effectLst>
|
||||
</a:effectStyle>
|
||||
<a:effectStyle>
|
||||
<a:effectLst>
|
||||
<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
|
||||
<a:srgbClr val="000000">
|
||||
<a:alpha val="35000"/>
|
||||
</a:srgbClr>
|
||||
</a:outerShdw>
|
||||
</a:effectLst>
|
||||
</a:effectStyle>
|
||||
<a:effectStyle>
|
||||
<a:effectLst>
|
||||
<a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
|
||||
<a:srgbClr val="000000">
|
||||
<a:alpha val="35000"/>
|
||||
</a:srgbClr>
|
||||
</a:outerShdw>
|
||||
</a:effectLst>
|
||||
<a:scene3d>
|
||||
<a:camera prst="orthographicFront">
|
||||
<a:rot lat="0" lon="0" rev="0"/>
|
||||
</a:camera>
|
||||
<a:lightRig rig="threePt" dir="t">
|
||||
<a:rot lat="0" lon="0" rev="1200000"/>
|
||||
</a:lightRig>
|
||||
</a:scene3d>
|
||||
<a:sp3d>
|
||||
<a:bevelT w="63500" h="25400"/>
|
||||
</a:sp3d>
|
||||
</a:effectStyle>
|
||||
</a:effectStyleLst>
|
||||
<a:bgFillStyleLst>
|
||||
<a:solidFill>
|
||||
<a:schemeClr val="phClr"/>
|
||||
</a:solidFill>
|
||||
<a:gradFill rotWithShape="1">
|
||||
<a:gsLst>
|
||||
<a:gs pos="0">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="40000"/>
|
||||
<a:satMod val="350000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
<a:gs pos="40000">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="45000"/>
|
||||
<a:shade val="99000"/>
|
||||
<a:satMod val="350000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
<a:gs pos="100000">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:shade val="20000"/>
|
||||
<a:satMod val="255000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
</a:gsLst>
|
||||
<a:path path="circle">
|
||||
<a:fillToRect l="50000" t="-80000" r="50000" b="180000"/>
|
||||
</a:path>
|
||||
</a:gradFill>
|
||||
<a:gradFill rotWithShape="1">
|
||||
<a:gsLst>
|
||||
<a:gs pos="0">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:tint val="80000"/>
|
||||
<a:satMod val="300000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
<a:gs pos="100000">
|
||||
<a:schemeClr val="phClr">
|
||||
<a:shade val="30000"/>
|
||||
<a:satMod val="200000"/>
|
||||
</a:schemeClr>
|
||||
</a:gs>
|
||||
</a:gsLst>
|
||||
<a:path path="circle">
|
||||
<a:fillToRect l="50000" t="50000" r="50000" b="50000"/>
|
||||
</a:path>
|
||||
</a:gradFill>
|
||||
</a:bgFillStyleLst>
|
||||
</a:fmtScheme>
|
||||
</a:themeElements>
|
||||
<a:objectDefaults>
|
||||
<a:spDef>
|
||||
<a:spPr/>
|
||||
<a:bodyPr/>
|
||||
<a:lstStyle/>
|
||||
<a:style>
|
||||
<a:lnRef idx="1">
|
||||
<a:schemeClr val="accent1"/>
|
||||
</a:lnRef>
|
||||
<a:fillRef idx="3">
|
||||
<a:schemeClr val="accent1"/>
|
||||
</a:fillRef>
|
||||
<a:effectRef idx="2">
|
||||
<a:schemeClr val="accent1"/>
|
||||
</a:effectRef>
|
||||
<a:fontRef idx="minor">
|
||||
<a:schemeClr val="lt1"/>
|
||||
</a:fontRef>
|
||||
</a:style>
|
||||
</a:spDef>
|
||||
<a:lnDef>
|
||||
<a:spPr/>
|
||||
<a:bodyPr/>
|
||||
<a:lstStyle/>
|
||||
<a:style>
|
||||
<a:lnRef idx="2">
|
||||
<a:schemeClr val="accent1"/>
|
||||
</a:lnRef>
|
||||
<a:fillRef idx="0">
|
||||
<a:schemeClr val="accent1"/>
|
||||
</a:fillRef>
|
||||
<a:effectRef idx="1">
|
||||
<a:schemeClr val="accent1"/>
|
||||
</a:effectRef>
|
||||
<a:fontRef idx="minor">
|
||||
<a:schemeClr val="tx1"/>
|
||||
</a:fontRef>
|
||||
</a:style>
|
||||
</a:lnDef>
|
||||
</a:objectDefaults>
|
||||
<a:extraClrSchemeLst/>
|
||||
</a:theme>`
|
||||
)
|
||||
20
document.go
Normal file
20
document.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package docxlib
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
const (
|
||||
XMLNS_W = `http://schemas.openxmlformats.org/wordprocessingml/2006/main`
|
||||
XMLNS_R = `http://schemas.openxmlformats.org/officeDocument/2006/relationships`
|
||||
)
|
||||
|
||||
type Body struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main body"`
|
||||
Paragraphs []*Paragraph `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main p"`
|
||||
}
|
||||
|
||||
type Document struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main document"`
|
||||
XMLW string `xml:"xmlns:w,attr"`
|
||||
XMLR string `xml:"xmlns:r,attr"`
|
||||
Body *Body
|
||||
}
|
||||
37
docx.go
Normal file
37
docx.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package docxlib
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Docx struct {
|
||||
Document Document
|
||||
DocRelation Relationships
|
||||
|
||||
rId int
|
||||
}
|
||||
|
||||
// New generates a new empty docx file that we can manipulate and
|
||||
// later on, save
|
||||
func New() *Docx {
|
||||
return emptyFile()
|
||||
}
|
||||
|
||||
// Parse generates a new docx file in memory from a reader
|
||||
func Parse(reader io.ReaderAt, size int64) (doc *Docx, err error) {
|
||||
zipReader, err := zip.NewReader(reader, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doc, err = unpack(zipReader)
|
||||
return
|
||||
}
|
||||
|
||||
// Write allows to save a docx to a writer
|
||||
func (f *Docx) Write(writer io.Writer) (err error) {
|
||||
zipWriter := zip.NewWriter(writer)
|
||||
defer zipWriter.Close()
|
||||
|
||||
return f.pack(zipWriter)
|
||||
}
|
||||
48
empty.go
Normal file
48
empty.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package docxlib
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
func emptyRelationships() []*Relationship {
|
||||
defaultRel := []*Relationship{
|
||||
{
|
||||
ID: "rId1",
|
||||
Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles`,
|
||||
Target: "styles.xml",
|
||||
},
|
||||
{
|
||||
ID: "rId2",
|
||||
Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme`,
|
||||
Target: "theme/theme1.xml",
|
||||
},
|
||||
{
|
||||
ID: "rId3",
|
||||
Type: `http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable`,
|
||||
Target: "fontTable.xml",
|
||||
},
|
||||
}
|
||||
return defaultRel
|
||||
}
|
||||
|
||||
func emptyFile() *Docx {
|
||||
docx := &Docx{
|
||||
Document: Document{
|
||||
XMLName: xml.Name{
|
||||
Space: "w",
|
||||
},
|
||||
XMLW: XMLNS_W,
|
||||
XMLR: XMLNS_R,
|
||||
Body: &Body{
|
||||
XMLName: xml.Name{
|
||||
Space: "w",
|
||||
},
|
||||
Paragraphs: make([]*Paragraph, 0),
|
||||
},
|
||||
},
|
||||
DocRelation: Relationships{
|
||||
Xmlns: XMLNS,
|
||||
Relationships: emptyRelationships(),
|
||||
},
|
||||
rId: 4,
|
||||
}
|
||||
return docx
|
||||
}
|
||||
38
link.go
Normal file
38
link.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package docxlib
|
||||
|
||||
import "strconv"
|
||||
|
||||
func (f *Docx) addLinkRelation(link string) string {
|
||||
rel := &Relationship{
|
||||
ID: "rId" + strconv.Itoa(f.rId),
|
||||
Type: REL_HYPERLINK,
|
||||
Target: link,
|
||||
TargetMode: REL_TARGETMODE,
|
||||
}
|
||||
|
||||
f.rId += 1
|
||||
|
||||
f.DocRelation.Relationships = append(f.DocRelation.Relationships, rel)
|
||||
|
||||
return rel.ID
|
||||
}
|
||||
|
||||
// AddLink add hyperlink to paragraph
|
||||
func (p *Paragraph) AddLink(text string, link string) *Hyperlink {
|
||||
rId := p.file.addLinkRelation(link)
|
||||
hyperlink := &Hyperlink{
|
||||
ID: rId,
|
||||
Run: Run{
|
||||
RunProperties: &RunProperties{
|
||||
RunStyle: &RunStyle{
|
||||
Val: HYPERLINK_STYLE,
|
||||
},
|
||||
},
|
||||
InstrText: text,
|
||||
},
|
||||
}
|
||||
|
||||
p.Data = append(p.Data, ParagraphChild{Link: hyperlink})
|
||||
|
||||
return hyperlink
|
||||
}
|
||||
56
main/main.go
Normal file
56
main/main.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gonfva/docxlib"
|
||||
)
|
||||
|
||||
const FILE_PATH = "/tmp/new-file.docx"
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Preparing new document to write at %s\n", FILE_PATH)
|
||||
|
||||
w := docxlib.New()
|
||||
// add new paragraph
|
||||
para1 := w.AddParagraph()
|
||||
// add text
|
||||
para1.AddText("test")
|
||||
|
||||
para1.AddText("test font size").Size(22)
|
||||
para1.AddText("test color").Color("808080")
|
||||
para2 := w.AddParagraph()
|
||||
para2.AddText("test font size and color").Size(22).Color("ff0000")
|
||||
|
||||
nextPara := w.AddParagraph()
|
||||
nextPara.AddLink("google", `http://google.com`)
|
||||
|
||||
f, err := os.Create(FILE_PATH)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
w.Write(f)
|
||||
fmt.Println("Document writen. \nNow trying to read it")
|
||||
// Now let's try to read the file
|
||||
readFile, err := os.Open(FILE_PATH)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileinfo, err := readFile.Stat()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
size := fileinfo.Size()
|
||||
doc, err := docxlib.Parse(readFile, int64(size))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, para := range doc.Paragraphs() {
|
||||
for _, run := range para.Runs() {
|
||||
fmt.Printf("\tWe've found a new run with the text ->%s\n", run.Text.Text)
|
||||
}
|
||||
}
|
||||
fmt.Println("End of main")
|
||||
}
|
||||
51
pack.go
Normal file
51
pack.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package docxlib
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (f *Docx) pack(zipWriter *zip.Writer) (err error) {
|
||||
files := map[string]string{}
|
||||
|
||||
files["_rels/.rels"] = TEMP_REL
|
||||
files["docProps/app.xml"] = TEMP_DOCPROPS_APP
|
||||
files["docProps/core.xml"] = TEMP_DOCPROPS_CORE
|
||||
files["word/theme/theme1.xml"] = TEMP_WORD_THEME_THEME
|
||||
files["word/styles.xml"] = TEMP_WORD_STYLE
|
||||
files["[Content_Types].xml"] = TEMP_CONTENT
|
||||
files["word/_rels/document.xml.rels"], err = marshal(f.DocRelation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files["word/document.xml"], err = marshal(f.Document)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for path, data := range files {
|
||||
w, err := zipWriter.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func marshal(data interface{}) (out string, err error) {
|
||||
body, err := xml.Marshal(data)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
out = xml.Header + string(body)
|
||||
return
|
||||
}
|
||||
42
paragraph.go
Normal file
42
paragraph.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package docxlib
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
type ParagraphChild struct {
|
||||
Link *Hyperlink `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main hyperlink"`
|
||||
Run *Run `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main r"`
|
||||
}
|
||||
|
||||
type Paragraph struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main p"`
|
||||
Data []ParagraphChild
|
||||
|
||||
file *Docx
|
||||
}
|
||||
|
||||
// AddParagraph adds a new paragraph
|
||||
func (f *Docx) AddParagraph() *Paragraph {
|
||||
p := &Paragraph{
|
||||
Data: make([]ParagraphChild, 0),
|
||||
file: f,
|
||||
}
|
||||
|
||||
f.Document.Body.Paragraphs = append(f.Document.Body.Paragraphs, p)
|
||||
return p
|
||||
}
|
||||
|
||||
func (f *Docx) Paragraphs() []*Paragraph {
|
||||
return f.Document.Body.Paragraphs
|
||||
}
|
||||
|
||||
func (p *Paragraph) Runs() (ret []*Run) {
|
||||
data := p.Data
|
||||
for _, d := range data {
|
||||
if d.Run != nil {
|
||||
ret = append(ret, d.Run)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
24
relationship.go
Normal file
24
relationship.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package docxlib
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
const (
|
||||
XMLNS = `http://schemas.openxmlformats.org/package/2006/relationships`
|
||||
REL_HYPERLINK = `http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink`
|
||||
|
||||
REL_TARGETMODE = "External"
|
||||
)
|
||||
|
||||
type Relationships struct {
|
||||
XMLName xml.Name `xml:"Relationships"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
Relationships []*Relationship `xml:"Relationship"`
|
||||
}
|
||||
|
||||
type Relationship struct {
|
||||
XMLName xml.Name `xml:"Relationship"`
|
||||
ID string `xml:"Id,attr"`
|
||||
Type string `xml:"Type,attr"`
|
||||
Target string `xml:"Target,attr"`
|
||||
TargetMode string `xml:"TargetMode,attr,omitempty"`
|
||||
}
|
||||
58
run.go
Normal file
58
run.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package docxlib
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// A Run is part of a paragraph that has its own style. It could be
|
||||
// a piece of text in bold, or a link
|
||||
type Run struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main r"`
|
||||
RunProperties *RunProperties `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr,omitempty"`
|
||||
InstrText string `xml:"w:instrText,omitempty"`
|
||||
Text *Text
|
||||
}
|
||||
|
||||
// The Text object contains the actual text
|
||||
type Text struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main t"`
|
||||
XMLSpace string `xml:"xml:space,attr,omitempty"`
|
||||
Text string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type Hyperlink struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main hyperlink"`
|
||||
ID string `xml:"http://schemas.openxmlformats.org/officeDocument/2006/relationships id,attr"`
|
||||
Run Run
|
||||
}
|
||||
|
||||
// Color allows to set run color
|
||||
func (r *Run) Color(color string) *Run {
|
||||
r.RunProperties.Color = &Color{
|
||||
Val: color,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Size allows to set run size
|
||||
func (r *Run) Size(size int) *Run {
|
||||
r.RunProperties.Size = &Size{
|
||||
Val: size * 2,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// AddText add text to paragraph
|
||||
func (p *Paragraph) AddText(text string) *Run {
|
||||
t := &Text{
|
||||
Text: text,
|
||||
}
|
||||
|
||||
run := &Run{
|
||||
Text: t,
|
||||
RunProperties: &RunProperties{},
|
||||
}
|
||||
|
||||
p.Data = append(p.Data, ParagraphChild{Run: run})
|
||||
|
||||
return run
|
||||
}
|
||||
29
run_properties.go
Normal file
29
run_properties.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package docxlib
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
const (
|
||||
HYPERLINK_STYLE = "a1"
|
||||
)
|
||||
|
||||
type RunProperties struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rPr"`
|
||||
Color *Color `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main color,omitempty"`
|
||||
Size *Size `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main sz,omitempty"`
|
||||
RunStyle *RunStyle `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rStyle,omitempty"`
|
||||
}
|
||||
|
||||
type RunStyle struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main rStyle"`
|
||||
Val string `xml:"w:val,attr"`
|
||||
}
|
||||
|
||||
type Color struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main color"`
|
||||
Val string `xml:"w:val,attr"`
|
||||
}
|
||||
|
||||
type Size struct {
|
||||
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/wordprocessingml/2006/main sz"`
|
||||
Val int `xml:"w:val,attr"`
|
||||
}
|
||||
77
unpack.go
Normal file
77
unpack.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package docxlib
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func unpack(zipReader *zip.Reader) (docx *Docx, err error) {
|
||||
var doc *Document
|
||||
var relations *Relationships
|
||||
for _, f := range zipReader.File {
|
||||
if f.Name == "word/_rels/document.xml.rels" {
|
||||
relations, err = processRelations(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if f.Name == "word/document.xml" {
|
||||
doc, err = processDoc(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
docx = &Docx{
|
||||
Document: *doc,
|
||||
DocRelation: *relations,
|
||||
}
|
||||
return docx, nil
|
||||
}
|
||||
|
||||
func processDoc(file *zip.File) (*Document, error) {
|
||||
filebytes, err := readZipFile(file)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading from internal zip file")
|
||||
return nil, err
|
||||
}
|
||||
doc := Document{
|
||||
XMLW: XMLNS_W,
|
||||
XMLR: XMLNS_R,
|
||||
XMLName: xml.Name{Space: XMLNS_W, Local: "document"}}
|
||||
err = xml.Unmarshal(filebytes, &doc)
|
||||
//r := bytes.NewReader(filebytes)
|
||||
//err = decode(r)
|
||||
if err != nil {
|
||||
fmt.Println("Error unmarshalling doc")
|
||||
fmt.Println(string(filebytes))
|
||||
return nil, err
|
||||
}
|
||||
return &doc, nil
|
||||
}
|
||||
|
||||
func processRelations(file *zip.File) (*Relationships, error) {
|
||||
filebytes, err := readZipFile(file)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading from internal zip file")
|
||||
return nil, err
|
||||
}
|
||||
rels := Relationships{Xmlns: "none"}
|
||||
err = xml.Unmarshal(filebytes, &rels)
|
||||
if err != nil {
|
||||
fmt.Println("Error unmarshalling relationships")
|
||||
return nil, err
|
||||
}
|
||||
return &rels, nil
|
||||
}
|
||||
|
||||
func readZipFile(zf *zip.File) ([]byte, error) {
|
||||
f, err := zf.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
Reference in New Issue
Block a user