├── .gitattributes
├── .github
├── FUNDING.yml
└── workflows
│ ├── codeql-analysis.yml
│ └── dotnet.yml
├── .gitignore
├── LICENSE
├── MiniWord.sln
├── README.md
├── README.zh-CN.md
├── README.zh-Hant.md
├── release-note
├── README.md
├── README.zh-CN.md
└── README.zh-Hant.md
├── samples
└── docx
│ ├── DemoExpenseMeeting01.png
│ ├── DemoExpenseMeeting02.png
│ ├── DemoLogo.png
│ ├── TestBasicFill.docx
│ ├── TestBasicFill
│ ├── [Content_Types].xml
│ ├── _rels
│ │ └── .rels
│ ├── docProps
│ │ ├── app.xml
│ │ └── core.xml
│ └── word
│ │ ├── _rels
│ │ └── document.xml.rels
│ │ ├── document.xml
│ │ ├── fontTable.xml
│ │ ├── settings.xml
│ │ ├── styles.xml
│ │ ├── theme
│ │ └── theme1.xml
│ │ └── webSettings.xml
│ ├── TestBasicImage.docx
│ ├── TestBasicImage.png
│ ├── TestDemo01.docx
│ ├── TestDemo02.docx
│ ├── TestDemo03.docx
│ ├── TestDemo04.docx
│ ├── TestExpenseDemo.docx
│ ├── TestForeachInTablesDemo.docx
│ ├── TestForeachInTablesWithIfStatementDemo.docx
│ ├── TestIfStatement.docx
│ ├── TestIssue11.docx
│ ├── TestIssue17.docx
│ ├── TestIssue18.docx
│ ├── TestIssue37.docx
│ ├── TestIssue43.docx
│ ├── TestIssue47.docx
│ └── demo01.png
├── src
└── MiniWord
│ ├── Common
│ └── Enums
│ │ └── Extension.cs
│ ├── Extensions
│ ├── ObjectExtension.cs
│ ├── OpenXmlExtension.cs
│ └── StringExtension.cs
│ ├── MiniWord.Implment.cs
│ ├── MiniWord.cs
│ ├── MiniWord.csproj
│ ├── MiniWordColorText.cs
│ ├── MiniWordForeach.cs
│ ├── MiniWordHyperLink.cs
│ ├── MiniWordPicture.cs
│ ├── Utility
│ ├── Helpers.cs
│ └── TargetFrameType.cs
│ └── icon.png
└── tests
├── AspNetCoreDemo
├── AspNetCoreDemo.sln
└── AspNetCoreDemo
│ ├── AspNetCoreDemo.csproj
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── TestTemplateComplex.docx
│ ├── appsettings.Development.json
│ └── appsettings.json
├── MiniWordTests
├── Helpers.cs
├── IssueTests.cs
├── IssueTestsAsync.cs
├── MiniWordTestAsync.cs
├── MiniWordTests.cs
├── MiniWordTests.csproj
└── PathHelper.cs
└── linqpads
├── basic_fill.linq
├── miniword_draft.linq
└── object to N level Dictionary.linq
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
3 | *.doc diff=astextplain
4 | *.DOC diff=astextplain
5 | *.docx diff=astextplain
6 | *.DOCX diff=astextplain
7 | *.dot diff=astextplain
8 | *.DOT diff=astextplain
9 | *.pdf diff=astextplain
10 | *.PDF diff=astextplain
11 | *.rtf diff=astextplain
12 | *.RTF diff=astextplain
13 |
14 | *.jpg binary
15 | *.png binary
16 | *.gif binary
17 |
18 | *.cs -text diff=csharp
19 | *.vb -text
20 | *.c -text
21 | *.cpp -text
22 | *.cxx -text
23 | *.h -text
24 | *.hxx -text
25 | *.py -text
26 | *.rb -text
27 | *.java -text
28 | *.html -text
29 | *.htm -text
30 | *.css -text
31 | *.scss -text
32 | *.sass -text
33 | *.less -text
34 | *.js -text
35 | *.lisp -text
36 | *.clj -text
37 | *.sql -text
38 | *.php -text
39 | *.lua -text
40 | *.m -text
41 | *.asm -text
42 | *.erl -text
43 | *.fs -text
44 | *.fsx -text
45 | *.hs -text
46 |
47 | *.csproj -text merge=union
48 | *.vbproj -text merge=union
49 | *.fsproj -text merge=union
50 | *.dbproj -text merge=union
51 | *.sln -text merge=union
52 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | #github: [shps951023]
4 | #patreon: # Replace with a single Patreon username
5 | #open_collective: MiniExcel # Replace with a single Open Collective username
6 | #ko_fi: # Replace with a single Ko-fi username
7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | #liberapay: # Replace with a single Liberapay username
10 | #issuehunt: # Replace with a single IssueHunt username
11 | #otechie: # Replace with a single Otechie username
12 | #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: ['https://miniexcel.github.io'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '18 0 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'csharp' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: .NET
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Setup .NET
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 6.0.x
20 | - name: Restore dependencies
21 | run: dotnet restore
22 | - name: Build
23 | run: dotnet build --no-restore
24 | - name: Test
25 | run: dotnet test --no-build --verbosity normal
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
262 |
263 | /BenchmarkDotNet.Artifacts
264 | /tests/MiniExcel.Tests.AspNetMvc/packages
265 | /TestTemplate
266 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/MiniWord.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32328.378
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniWord", "src\MiniWord\MiniWord.csproj", "{6DDDB819-3988-41C7-8275-DD8F3291BDB1}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{4EAD1BCD-0F7F-4463-8890-C07ACA8164B7}"
9 | ProjectSection(SolutionItems) = preProject
10 | README.md = README.md
11 | README.zh-CN.md = README.zh-CN.md
12 | README.zh-Hant.md = README.zh-Hant.md
13 | EndProjectSection
14 | EndProject
15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniWordTests", "tests\MiniWordTests\MiniWordTests.csproj", "{7CB52636-4C20-4FE8-8C69-F794ABBB86A6}"
16 | EndProject
17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "release-note", "release-note", "{BEA78871-F09C-47A6-98D0-1ED6E0E8476D}"
18 | ProjectSection(SolutionItems) = preProject
19 | release-note\README.md = release-note\README.md
20 | release-note\README.zh-CN.md = release-note\README.zh-CN.md
21 | release-note\README.zh-Hant.md = release-note\README.zh-Hant.md
22 | EndProjectSection
23 | EndProject
24 | Global
25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
26 | Debug|Any CPU = Debug|Any CPU
27 | Release|Any CPU = Release|Any CPU
28 | EndGlobalSection
29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
30 | {6DDDB819-3988-41C7-8275-DD8F3291BDB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {6DDDB819-3988-41C7-8275-DD8F3291BDB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {6DDDB819-3988-41C7-8275-DD8F3291BDB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {6DDDB819-3988-41C7-8275-DD8F3291BDB1}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {7CB52636-4C20-4FE8-8C69-F794ABBB86A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {7CB52636-4C20-4FE8-8C69-F794ABBB86A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {7CB52636-4C20-4FE8-8C69-F794ABBB86A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {7CB52636-4C20-4FE8-8C69-F794ABBB86A6}.Release|Any CPU.Build.0 = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | GlobalSection(ExtensibilityGlobals) = postSolution
43 | SolutionGuid = {C39CDB06-1E12-454D-95FA-8524A9C7D823}
44 | EndGlobalSection
45 | EndGlobal
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | ---
10 |
11 |
14 |
15 | ---
16 |
17 |
18 | Your
Star and
dotnate can make MiniWord better
19 |
20 |
21 | ---
22 |
23 | ## Introduction
24 |
25 | MiniWord is an easy and effective .NET Word Template library.
26 |
27 | 
28 |
29 |
30 |
31 | ## Getting Started
32 |
33 | ### Installation
34 |
35 | - nuget link : https://www.nuget.org/packages/MiniWord
36 |
37 | ### Quick Start
38 |
39 | Template follow "WHAT you see is what you get" design,and the template tag styles are completely preserved.
40 |
41 | ```csharp
42 | var value = new Dictionary(){["title"] = "Hello MiniWord"};
43 | MiniSoftware.MiniWord.SaveAsByTemplate(outputPath, templatePath, value);
44 | ```
45 |
46 | 
47 |
48 | ### Input, Output
49 |
50 | - Input support file path, byte[]
51 | - Output support file path, byte[], stream
52 |
53 | ```csharp
54 | SaveAsByTemplate(string path, string templatePath, Dictionary value)
55 | SaveAsByTemplate(string path, byte[] templateBytes, Dictionary value)
56 | SaveAsByTemplate(this Stream stream, string templatePath, Dictionary value)
57 | SaveAsByTemplate(this Stream stream, byte[] templateBytes, Dictionary value)
58 | ```
59 |
60 |
61 |
62 | ## Tags
63 |
64 | MiniWord template format string like Vue, React `{{tag}}`,users only need to make sure tag and value parameter key same then system will replace them automatically.
65 |
66 | ### Text
67 |
68 | ```csharp
69 | {{tag}}
70 | ```
71 |
72 | ##### Example
73 |
74 | ```csharp
75 | var value = new Dictionary()
76 | {
77 | ["Name"] = "Jack",
78 | ["Department"] = "IT Department",
79 | ["Purpose"] = "Shanghai site needs a new system to control HR system.",
80 | ["StartDate"] = DateTime.Parse("2022-09-07 08:30:00"),
81 | ["EndDate"] = DateTime.Parse("2022-09-15 15:30:00"),
82 | ["Approved"] = true,
83 | ["Total_Amount"] = 123456,
84 | };
85 | MiniWord.SaveAsByTemplate(path, templatePath, value);
86 | ```
87 |
88 | ##### Template
89 |
90 | 
91 |
92 | ##### Result
93 |
94 | 
95 |
96 | ### Image
97 |
98 | Value type is `MiniWordPicture`
99 |
100 | ##### Example
101 |
102 | ```csharp
103 | var value = new Dictionary()
104 | {
105 | ["Logo"] = new MiniWordPicture() { Path= PathHelper.GetFile("DemoLogo.png"), Width= 180, Height= 180 }
106 | };
107 | MiniWord.SaveAsByTemplate(path, templatePath, value);
108 | ```
109 |
110 |
111 |
112 | ##### Template
113 |
114 | 
115 |
116 | ##### Result
117 |
118 | 
119 |
120 | ### List
121 |
122 | tag value is `string[]` or `IList` type
123 |
124 | ##### Example
125 |
126 | ```csharp
127 | var value = new Dictionary()
128 | {
129 | ["managers"] = new[] { "Jack" ,"Alan"},
130 | ["employees"] = new[] { "Mike" ,"Henry"},
131 | };
132 | MiniWord.SaveAsByTemplate(path, templatePath, value);
133 | ```
134 |
135 | Template
136 |
137 | 
138 |
139 | ##### Result
140 |
141 | 
142 |
143 | ### Table
144 |
145 | Tag value is `IEmerable>` type
146 |
147 | ##### Example
148 |
149 | ```csharp
150 | var value = new Dictionary()
151 | {
152 | ["TripHs"] = new List>
153 | {
154 | new Dictionary
155 | {
156 | { "sDate",DateTime.Parse("2022-09-08 08:30:00")},
157 | { "eDate",DateTime.Parse("2022-09-08 15:00:00")},
158 | { "How","Discussion requirement part1"},
159 | { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting02.png"), Width = 160, Height = 90 }},
160 | },
161 | new Dictionary
162 | {
163 | { "sDate",DateTime.Parse("2022-09-09 08:30:00")},
164 | { "eDate",DateTime.Parse("2022-09-09 17:00:00")},
165 | { "How","Discussion requirement part2 and development"},
166 | { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting01.png"), Width = 160, Height = 90 }},
167 | },
168 | }
169 | };
170 | MiniWord.SaveAsByTemplate(path, templatePath, value);
171 | ```
172 |
173 | ##### Template
174 |
175 | 
176 |
177 |
178 | ##### Result
179 |
180 | 
181 |
182 | ### List inside list
183 |
184 | Tag value is `IEnumerable` type. Adding `{{foreach` and `endforeach}}` tags to template is required.
185 |
186 | ##### Example
187 |
188 | ```csharp
189 | var value = new Dictionary()
190 | {
191 | ["TripHs"] = new List>
192 | {
193 | new Dictionary
194 | {
195 | { "sDate", DateTime.Parse("2022-09-08 08:30:00") },
196 | { "eDate", DateTime.Parse("2022-09-08 15:00:00") },
197 | { "How", "Discussion requirement part1" },
198 | {
199 | "Details", new List()
200 | {
201 | new MiniWordForeach()
202 | {
203 | Value = new Dictionary()
204 | {
205 | {"Text", "Air"},
206 | {"Value", "Airplane"}
207 | },
208 | Separator = " | "
209 | },
210 | new MiniWordForeach()
211 | {
212 | Value = new Dictionary()
213 | {
214 | {"Text", "Parking"},
215 | {"Value", "Car"}
216 | },
217 | Separator = " / "
218 | }
219 | }
220 | }
221 | }
222 | }
223 | };
224 | MiniWord.SaveAsByTemplate(path, templatePath, value);
225 | ```
226 |
227 | ##### Template
228 |
229 | 
230 |
231 |
232 |
233 | ##### Result
234 |
235 | 
236 |
237 |
238 |
239 | ### If statement inside template
240 |
241 | For multip paragraph, use @if and @endif tags.
242 | For single paragraph and inside foreach, use `{{if` and `endif}}` tags to template is required.
243 |
244 | ##### Example
245 |
246 | ```csharp
247 | var value = new Dictionary()
248 | {
249 | ["Name"] = new List(){
250 | new MiniWordHyperLink(){
251 | Url = "https://google.com",
252 | Text = "測試連結22!!"
253 | },
254 | new MiniWordHyperLink(){
255 | Url = "https://google1.com",
256 | Text = "測試連結11!!"
257 | }
258 | },
259 | ["Company_Name"] = "MiniSofteware",
260 | ["CreateDate"] = new DateTime(2021, 01, 01),
261 | ["VIP"] = true,
262 | ["Points"] = 123,
263 | ["APP"] = "Demo APP",
264 | };
265 | MiniWord.SaveAsByTemplate(path, templatePath, value);
266 | ```
267 |
268 | ##### Template For Multi Paragraph
269 |
270 | 
271 |
272 | ##### Result Of Multi Paragraph
273 |
274 | 
275 |
276 | ##### Template For Single Paragraph
277 |
278 |
279 |
280 | ##### Result Of Single Paragraph
281 |
282 |
283 |
284 | ### ColorText
285 |
286 | ##### Example
287 |
288 | ```csharp
289 | var value = new
290 | {
291 | Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB", },
292 | Name = new[] {
293 | new MiniWordColorText { Text = "Ja", HighlightColor = "#eb70AB" },
294 | new MiniWordColorText { Text = "ck", HighlightColor = "#a56abe" }
295 | },
296 | CreateDate = new MiniWordColorText
297 | {
298 | Text = new DateTime(2021, 01, 01).ToString(),
299 | HighlightColor = "#eb70AB",
300 | FontColor = "#ffffff",
301 | },
302 | VIP = true,
303 | Points = 123,
304 | APP = "Demo APP",
305 | };
306 | MiniWord.SaveAsByTemplate(path, templatePath, value);
307 | ```
308 |
309 |
310 | ## Other
311 |
312 | ### POCO or dynamic parameter
313 |
314 | v0.5.0 support POCO or dynamic parameter
315 |
316 | ```csharp
317 | var value = new { title = "Hello MiniWord" };
318 | MiniWord.SaveAsByTemplate(outputPath, templatePath, value);
319 | ```
320 |
321 | ### FontColor and HighlightColor
322 | ```csharp
323 | var value = new
324 | {
325 | Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB" },
326 | Name = new MiniWordColorText { Text = "Jack", HighlightColor = "#eb70AB" },
327 | CreateDate = new MiniWordColorText { Text = new DateTime(2021, 01, 01).ToString(), HighlightColor = "#eb70AB", FontColor = "#ffffff" },
328 | VIP = true,
329 | Points = 123,
330 | APP = "Demo APP",
331 | };
332 | ```
333 |
334 | ### HyperLink
335 |
336 | If value type is `MiniWordHyperLink` system will replace template string by hyperlink.
337 |
338 | * Url: HyperLink URI target path
339 | * Text:Description
340 |
341 | ```csharp
342 | var value = new
343 | {
344 | ["Name"] = new MiniWordHyperLink(){
345 | Url = "https://google.com",
346 | Text = "Test Link!!"
347 | },
348 | ["Company_Name"] = "MiniSofteware",
349 | ["CreateDate"] = new DateTime(2021, 01, 01),
350 | ["VIP"] = true,
351 | ["Points"] = 123,
352 | ["APP"] = "Demo APP",
353 | };
354 | MiniWord.SaveAsByTemplate(path, templatePath, value);
355 | ```
356 |
357 |
358 |
359 | ## Examples
360 |
361 |
362 |
363 | #### ASP.NET Core 3.1 API Export
364 |
365 | ```cs
366 | using Microsoft.AspNetCore.Builder;
367 | using Microsoft.AspNetCore.Hosting;
368 | using Microsoft.AspNetCore.Mvc;
369 | using Microsoft.Extensions.DependencyInjection;
370 | using Microsoft.Extensions.Hosting;
371 | using System;
372 | using System.Collections.Generic;
373 | using System.IO;
374 | using System.Net;
375 | using MiniSoftware;
376 |
377 | public class Program
378 | {
379 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
380 |
381 | public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup());
382 | }
383 |
384 | public class Startup
385 | {
386 | public void ConfigureServices(IServiceCollection services) => services.AddMvc();
387 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
388 | {
389 | app.UseStaticFiles();
390 | app.UseRouting();
391 | app.UseEndpoints(endpoints =>
392 | {
393 | endpoints.MapControllerRoute(
394 | name: "default",
395 | pattern: "{controller=api}/{action=Index}/{id?}");
396 | });
397 | }
398 | }
399 |
400 | public class ApiController : Controller
401 | {
402 | public IActionResult Index()
403 | {
404 | return new ContentResult
405 | {
406 | ContentType = "text/html",
407 | StatusCode = (int)HttpStatusCode.OK,
408 | Content = @"
409 | DownloadWordFromTemplatePath
410 | DownloadWordFromTemplateBytes
411 | "
412 | };
413 | }
414 |
415 | static Dictionary defaultValue = new Dictionary()
416 | {
417 | ["title"] = "FooCompany",
418 | ["managers"] = new List> {
419 | new Dictionary{{"name","Jack"},{ "department", "HR" } },
420 | new Dictionary {{ "name", "Loan"},{ "department", "IT" } }
421 | },
422 | ["employees"] = new List> {
423 | new Dictionary{{ "name", "Wade" },{ "department", "HR" } },
424 | new Dictionary {{ "name", "Felix" },{ "department", "HR" } },
425 | new Dictionary{{ "name", "Eric" },{ "department", "IT" } },
426 | new Dictionary {{ "name", "Keaton" },{ "department", "IT" } }
427 | }
428 | };
429 |
430 | public IActionResult DownloadWordFromTemplatePath()
431 | {
432 | string templatePath = "TestTemplateComplex.docx";
433 |
434 | Dictionary value = defaultValue;
435 |
436 | MemoryStream memoryStream = new MemoryStream();
437 | MiniWord.SaveAsByTemplate(memoryStream, templatePath, value);
438 | memoryStream.Seek(0, SeekOrigin.Begin);
439 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
440 | {
441 | FileDownloadName = "demo.docx"
442 | };
443 | }
444 |
445 | private static Dictionary TemplateBytesCache = new Dictionary();
446 |
447 | static ApiController()
448 | {
449 | string templatePath = "TestTemplateComplex.docx";
450 | byte[] bytes = System.IO.File.ReadAllBytes(templatePath);
451 | TemplateBytesCache.Add(templatePath, bytes);
452 | }
453 |
454 | public IActionResult DownloadWordFromTemplateBytes()
455 | {
456 | byte[] bytes = TemplateBytesCache["TestTemplateComplex.docx"];
457 |
458 | Dictionary value = defaultValue;
459 |
460 | MemoryStream memoryStream = new MemoryStream();
461 | MiniWord.SaveAsByTemplate(memoryStream, bytes, value);
462 | memoryStream.Seek(0, SeekOrigin.Begin);
463 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
464 | {
465 | FileDownloadName = "demo.docx"
466 | };
467 | }
468 | }
469 | ```
470 |
471 |
472 |
473 |
474 |
475 |
476 | ## Support : [Donate Link](https://miniexcel.github.io/)
--------------------------------------------------------------------------------
/README.zh-CN.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | ---
10 |
11 |
14 |
15 | ---
16 |
17 |
18 | 您的
Star 和
赞助 可以让 MiniWord 走更远
19 |
20 |
21 | ---
22 |
23 | ## QQ群(1群) : [813100564](https://qm.qq.com/cgi-bin/qm/qr?k=3OkxuL14sXhJsUimWK8wx_Hf28Wl49QE&jump_from=webapi) / QQ群(2群) : [579033769](https://jq.qq.com/?_wv=1027&k=UxTdB8pR)
24 |
25 | ----
26 |
27 | ## 介绍
28 |
29 | MiniWord .NET Word模板引擎,藉由Word模板和数据简单、快速生成文件。
30 |
31 | 
32 |
33 |
34 |
35 | ## Getting Started
36 |
37 | ### 安装
38 |
39 | - nuget link : https://www.nuget.org/packages/MiniWord
40 |
41 | ### 快速入门
42 |
43 | 模板遵循“所见即所得”的设计,模板和标签的样式会被完全保留
44 |
45 | ```csharp
46 | var value = new Dictionary(){["title"] = "Hello MiniWord"};
47 | MiniSoftware.MiniWord.SaveAsByTemplate(outputPath, templatePath, value);
48 | ```
49 |
50 | 
51 |
52 | ### 输入、输出
53 |
54 | - 输入系统支持模版路径或是Byte[]
55 | - 输出支持文件路径、Byte[]、Stream
56 |
57 | ```csharp
58 | SaveAsByTemplate(string path, string templatePath, Dictionary value)
59 | SaveAsByTemplate(string path, byte[] templateBytes, Dictionary value)
60 | SaveAsByTemplate(this Stream stream, string templatePath, Dictionary value)
61 | SaveAsByTemplate(this Stream stream, byte[] templateBytes, Dictionary value)
62 | ```
63 |
64 |
65 |
66 | ## 标签
67 |
68 | MiniWord 使用类似 Vue, React 的模版字串 `{{tag}}`,只需要确保 tag 与 value 参数的 key 一样`(大小写敏感)`,系统会自动替换字串。
69 |
70 | ### 文本
71 |
72 | ```csharp
73 | {{tag}}
74 | ```
75 |
76 |
77 |
78 | ##### 代码例子
79 |
80 | ```csharp
81 | var value = new Dictionary()
82 | {
83 | ["Name"] = "Jack",
84 | ["Department"] = "IT Department",
85 | ["Purpose"] = "Shanghai site needs a new system to control HR system.",
86 | ["StartDate"] = DateTime.Parse("2022-09-07 08:30:00"),
87 | ["EndDate"] = DateTime.Parse("2022-09-15 15:30:00"),
88 | ["Approved"] = true,
89 | ["Total_Amount"] = 123456,
90 | };
91 | MiniWord.SaveAsByTemplate(path, templatePath, value);
92 | ```
93 |
94 | ##### 模版
95 |
96 | 
97 |
98 | ##### 导出
99 |
100 | 
101 |
102 | ### 图片
103 |
104 | 标签值为 `MiniWordPicture` 类别
105 |
106 | ##### 代码例子
107 |
108 | ```csharp
109 | var value = new Dictionary()
110 | {
111 | ["Logo"] = new MiniWordPicture() { Path= PathHelper.GetFile("DemoLogo.png"), Width= 180, Height= 180 }
112 | };
113 | MiniWord.SaveAsByTemplate(path, templatePath, value);
114 | ```
115 |
116 |
117 |
118 | ##### 模版
119 |
120 | 
121 |
122 | ##### 导出
123 |
124 | 
125 |
126 | ### 列表
127 |
128 | 标签值为 `string[]` 或是 `IList`类别
129 |
130 | ##### 代码例子
131 |
132 | ```csharp
133 | var value = new Dictionary()
134 | {
135 | ["managers"] = new[] { "Jack" ,"Alan"},
136 | ["employees"] = new[] { "Mike" ,"Henry"},
137 | };
138 | MiniWord.SaveAsByTemplate(path, templatePath, value);
139 | ```
140 |
141 | ##### 模版
142 |
143 | 
144 |
145 | ##### 导出
146 |
147 | 
148 |
149 | ### 表格
150 |
151 | 标签值为 `IEmerable>`类别
152 |
153 | ##### 代码例子
154 |
155 | ```csharp
156 | var value = new Dictionary()
157 | {
158 | ["TripHs"] = new List>
159 | {
160 | new Dictionary
161 | {
162 | { "sDate",DateTime.Parse("2022-09-08 08:30:00")},
163 | { "eDate",DateTime.Parse("2022-09-08 15:00:00")},
164 | { "How","Discussion requirement part1"},
165 | { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting02.png"), Width = 160, Height = 90 }},
166 | },
167 | new Dictionary
168 | {
169 | { "sDate",DateTime.Parse("2022-09-09 08:30:00")},
170 | { "eDate",DateTime.Parse("2022-09-09 17:00:00")},
171 | { "How","Discussion requirement part2 and development"},
172 | { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting01.png"), Width = 160, Height = 90 }},
173 | },
174 | }
175 | };
176 | MiniWord.SaveAsByTemplate(path, templatePath, value);
177 | ```
178 |
179 | ##### 模版
180 |
181 | 
182 |
183 |
184 | ##### 导出
185 |
186 | 
187 |
188 | ### 二级列表
189 |
190 | Tag 是 `IEnumerable` 类别. 使用方式`{{foreach` 和 `endforeach}}`.
191 |
192 | ##### Example
193 |
194 | ```csharp
195 | var value = new Dictionary()
196 | {
197 | ["TripHs"] = new List>
198 | {
199 | new Dictionary
200 | {
201 | { "sDate", DateTime.Parse("2022-09-08 08:30:00") },
202 | { "eDate", DateTime.Parse("2022-09-08 15:00:00") },
203 | { "How", "Discussion requirement part1" },
204 | {
205 | "Details", new List()
206 | {
207 | new MiniWordForeach()
208 | {
209 | Value = new Dictionary()
210 | {
211 | {"Text", "Air"},
212 | {"Value", "Airplane"}
213 | },
214 | Separator = " | "
215 | },
216 | new MiniWordForeach()
217 | {
218 | Value = new Dictionary()
219 | {
220 | {"Text", "Parking"},
221 | {"Value", "Car"}
222 | },
223 | Separator = " / "
224 | }
225 | }
226 | }
227 | }
228 | }
229 | };
230 | MiniWord.SaveAsByTemplate(path, templatePath, value);
231 | ```
232 |
233 | ##### Template
234 |
235 | 
236 |
237 | ##### Result
238 |
239 | 
240 |
241 | ### 条件判断
242 |
243 | `@if` 和 `@endif` tags .
244 |
245 | ##### Example
246 |
247 | ```csharp
248 | var value = new Dictionary()
249 | {
250 | ["Name"] = new List(){
251 | new MiniWordHyperLink(){
252 | Url = "https://google.com",
253 | Text = "測試連結22!!"
254 | },
255 | new MiniWordHyperLink(){
256 | Url = "https://google1.com",
257 | Text = "測試連結11!!"
258 | }
259 | },
260 | ["Company_Name"] = "MiniSofteware",
261 | ["CreateDate"] = new DateTime(2021, 01, 01),
262 | ["VIP"] = true,
263 | ["Points"] = 123,
264 | ["APP"] = "Demo APP",
265 | };
266 | MiniWord.SaveAsByTemplate(path, templatePath, value);
267 | ```
268 |
269 | ##### Template
270 |
271 | 
272 |
273 | ##### Result
274 |
275 | 
276 |
277 | ### 循环
278 |
279 | `@foreach` 和 `@endforeach` tags .
280 |
281 | ##### Example
282 |
283 | ```csharp
284 | var value = new
285 | {
286 | LoopData = new List()
287 | {
288 | new {
289 | Type="类型A",
290 | Items = new List() {new {Name = "A-1"}, new {Name = "A-2"},}
291 | },
292 | new
293 | {
294 | Type="类型B",
295 | Items = new List() {new {Name = "B-1"}, new {Name = "B-2"}, new {Name = "B-3"},}
296 | },
297 | }
298 | };
299 | MiniWord.SaveAsByTemplate(path, templatePath, value);
300 | ```
301 |
302 | ##### Template
303 |
304 | 
305 |
306 | ##### Result
307 |
308 | 
309 |
310 | ### 多彩字体
311 |
312 | ##### 代码例子
313 |
314 | ```csharp
315 | var value = new
316 | {
317 | Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB", },
318 | Name = new[] {
319 | new MiniWordColorText { Text = "Ja", HighlightColor = "#eb70AB" },
320 | new MiniWordColorText { Text = "ck", HighlightColor = "#a56abe" }
321 | },
322 | CreateDate = new MiniWordColorText
323 | {
324 | Text = new DateTime(2021, 01, 01).ToString(),
325 | HighlightColor = "#eb70AB",
326 | FontColor = "#ffffff",
327 | },
328 | VIP = true,
329 | Points = 123,
330 | APP = "Demo APP",
331 | };
332 | MiniWord.SaveAsByTemplate(path, templatePath, value);
333 | ```
334 |
335 |
336 |
337 |
338 |
339 | ## 其他
340 |
341 | ### POCO or dynamic 参数
342 |
343 | v0.5.0 支持 POCO 或 dynamic parameter
344 |
345 | ```csharp
346 | var value = new { title = "Hello MiniWord" };
347 | MiniWord.SaveAsByTemplate(outputPath, templatePath, value);
348 | ```
349 |
350 | ### 字体FontColor和HighlightColor
351 | ```csharp
352 | var value = new
353 | {
354 | Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB" },
355 | Name = new MiniWordColorText { Text = "Jack", HighlightColor = "#eb70AB" },
356 | CreateDate = new MiniWordColorText { Text = new DateTime(2021, 01, 01).ToString(), HighlightColor = "#eb70AB", FontColor = "#ffffff" },
357 | VIP = true,
358 | Points = 123,
359 | APP = "Demo APP",
360 | };
361 | ```
362 |
363 | ### HyperLink
364 |
365 | 我们可以尝试使用 `MiniWodrHyperLink` 类,用模板测试替换为超链接。
366 |
367 | `MiniWordHyperLink` 提供了两个主要参数。
368 |
369 | * Url: HyperLink URI 目标路径
370 | * 文字:超链接文字
371 |
372 | ```csharp
373 | var value = new
374 | {
375 | ["Name"] = new MiniWordHyperLink(){
376 | Url = "https://google.com",
377 | Text = "測試連結!!"
378 | },
379 | ["Company_Name"] = "MiniSofteware",
380 | ["CreateDate"] = new DateTime(2021, 01, 01),
381 | ["VIP"] = true,
382 | ["Points"] = 123,
383 | ["APP"] = "Demo APP",
384 | };
385 | MiniWord.SaveAsByTemplate(path, templatePath, value);
386 | ```
387 |
388 | ### 浮动图像
389 |
390 | 可以通过MiniWordPicture扩展参数配置图片悬浮环绕在文字上或文字下
391 | `MiniWordPicture` 扩展参数。
392 | * WrappingType: MiniWordPictureWrappingType.Anchor 浮动图像
393 | * HorizontalPositionOffset: 设置图片相对于锚点的水平偏移量(以像素为单位)
394 | * VerticalPositionOffset:设置图片相对于锚点的垂直偏移量(以像素为单位)
395 | * BehindDoc: 控制图片是否显示在文档文字的后方
396 | * AllowOverlap: 控制图片是否允许与其他图片或对象重叠
397 |
398 |
399 | ## 例子
400 |
401 |
402 |
403 | #### ASP.NET Core 3.1 API Export
404 |
405 | ```cs
406 | using Microsoft.AspNetCore.Builder;
407 | using Microsoft.AspNetCore.Hosting;
408 | using Microsoft.AspNetCore.Mvc;
409 | using Microsoft.Extensions.DependencyInjection;
410 | using Microsoft.Extensions.Hosting;
411 | using System;
412 | using System.Collections.Generic;
413 | using System.IO;
414 | using System.Net;
415 | using MiniSoftware;
416 |
417 | public class Program
418 | {
419 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
420 |
421 | public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup());
422 | }
423 |
424 | public class Startup
425 | {
426 | public void ConfigureServices(IServiceCollection services) => services.AddMvc();
427 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
428 | {
429 | app.UseStaticFiles();
430 | app.UseRouting();
431 | app.UseEndpoints(endpoints =>
432 | {
433 | endpoints.MapControllerRoute(
434 | name: "default",
435 | pattern: "{controller=api}/{action=Index}/{id?}");
436 | });
437 | }
438 | }
439 |
440 | public class ApiController : Controller
441 | {
442 | public IActionResult Index()
443 | {
444 | return new ContentResult
445 | {
446 | ContentType = "text/html",
447 | StatusCode = (int)HttpStatusCode.OK,
448 | Content = @"
449 | DownloadWordFromTemplatePath
450 | DownloadWordFromTemplateBytes
451 | "
452 | };
453 | }
454 |
455 | static Dictionary defaultValue = new Dictionary()
456 | {
457 | ["title"] = "FooCompany",
458 | ["managers"] = new List> {
459 | new Dictionary{{"name","Jack"},{ "department", "HR" } },
460 | new Dictionary {{ "name", "Loan"},{ "department", "IT" } }
461 | },
462 | ["employees"] = new List> {
463 | new Dictionary{{ "name", "Wade" },{ "department", "HR" } },
464 | new Dictionary {{ "name", "Felix" },{ "department", "HR" } },
465 | new Dictionary{{ "name", "Eric" },{ "department", "IT" } },
466 | new Dictionary {{ "name", "Keaton" },{ "department", "IT" } }
467 | }
468 | };
469 |
470 | public IActionResult DownloadWordFromTemplatePath()
471 | {
472 | string templatePath = "TestTemplateComplex.docx";
473 |
474 | Dictionary value = defaultValue;
475 |
476 | MemoryStream memoryStream = new MemoryStream();
477 | MiniWord.SaveAsByTemplate(memoryStream, templatePath, value);
478 | memoryStream.Seek(0, SeekOrigin.Begin);
479 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
480 | {
481 | FileDownloadName = "demo.docx"
482 | };
483 | }
484 |
485 | private static Dictionary TemplateBytesCache = new Dictionary();
486 |
487 | static ApiController()
488 | {
489 | string templatePath = "TestTemplateComplex.docx";
490 | byte[] bytes = System.IO.File.ReadAllBytes(templatePath);
491 | TemplateBytesCache.Add(templatePath, bytes);
492 | }
493 |
494 | public IActionResult DownloadWordFromTemplateBytes()
495 | {
496 | byte[] bytes = TemplateBytesCache["TestTemplateComplex.docx"];
497 |
498 | Dictionary value = defaultValue;
499 |
500 | MemoryStream memoryStream = new MemoryStream();
501 | MiniWord.SaveAsByTemplate(memoryStream, bytes, value);
502 | memoryStream.Seek(0, SeekOrigin.Begin);
503 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
504 | {
505 | FileDownloadName = "demo.docx"
506 | };
507 | }
508 | }
509 | ```
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 | ## 支持 : [Donate Link](https://miniexcel.github.io/)
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
--------------------------------------------------------------------------------
/README.zh-Hant.md:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 | ---
11 |
12 |
15 |
16 | ---
17 |
18 |
19 | 您的
Star 和
贊助 可以讓 MiniWord 走更遠
20 |
21 |
22 | ---
23 |
24 |
25 | ## 介紹
26 |
27 | MiniWord .NET Word模板引擎,藉由Word模板和數據簡單、快速生成文件。
28 |
29 | 
30 |
31 |
32 |
33 | ## Getting Started
34 |
35 | ### 安裝
36 |
37 | - nuget link : https://www.nuget.org/packages/MiniWord
38 |
39 | ### 快速入門
40 |
41 | 模板遵循“所見即所得”的設計,模板和標籤的樣式會被完全保留
42 |
43 | ```csharp
44 | var value = new Dictionary(){["title"] = "Hello MiniWord"};
45 | MiniSoftware.MiniWord.SaveAsByTemplate(outputPath, templatePath, value);
46 | ```
47 |
48 | 
49 |
50 | ### 輸入、輸出
51 |
52 | - 輸入系統支持模版路徑或是Byte[]
53 | - 輸出支持文件路徑、Byte[]、Stream
54 |
55 | ```csharp
56 | SaveAsByTemplate(string path, string templatePath, Dictionary value)
57 | SaveAsByTemplate(string path, byte[] templateBytes, Dictionary value)
58 | SaveAsByTemplate(this Stream stream, string templatePath, Dictionary value)
59 | SaveAsByTemplate(this Stream stream, byte[] templateBytes, Dictionary value)
60 | ```
61 |
62 |
63 |
64 | ## 標籤
65 |
66 | MiniWord 使用類似 Vue, React 的模版字串 `{{tag}}`,只需要確保 tag 與 value 參數的 key 一樣`(大小寫敏感)`,系統會自動替換字串。
67 |
68 | ### 文本
69 |
70 | ```csharp
71 | {{tag}}
72 | ```
73 |
74 | ##### 代碼例子
75 |
76 | ```csharp
77 | var value = new Dictionary()
78 | {
79 | ["Name"] = "Jack",
80 | ["Department"] = "IT Department",
81 | ["Purpose"] = "Shanghai site needs a new system to control HR system.",
82 | ["StartDate"] = DateTime.Parse("2022-09-07 08:30:00"),
83 | ["EndDate"] = DateTime.Parse("2022-09-15 15:30:00"),
84 | ["Approved"] = true,
85 | ["Total_Amount"] = 123456,
86 | };
87 | MiniWord.SaveAsByTemplate(path, templatePath, value);
88 | ```
89 |
90 | ##### 模版
91 |
92 | 
93 |
94 | ##### 導出
95 |
96 | 
97 |
98 |
99 |
100 | ### 圖片
101 |
102 | 標籤值為 `MiniWordPicture` 類別
103 |
104 | ##### 代碼例子
105 |
106 | ```csharp
107 | var value = new Dictionary()
108 | {
109 | ["Logo"] = new MiniWordPicture() { Path= PathHelper.GetFile("DemoLogo.png"), Width= 180, Height= 180 }
110 | };
111 | MiniWord.SaveAsByTemplate(path, templatePath, value);
112 | ```
113 |
114 |
115 |
116 | ##### 模版
117 |
118 | 
119 |
120 | ##### 導出
121 |
122 | 
123 |
124 | ### 列表
125 |
126 | 標籤值為 `string[]` 或是 `IList`類別
127 |
128 | ##### 代碼例子
129 |
130 | ```csharp
131 | var value = new Dictionary()
132 | {
133 | ["managers"] = new[] { "Jack" ,"Alan"},
134 | ["employees"] = new[] { "Mike" ,"Henry"},
135 | };
136 | MiniWord.SaveAsByTemplate(path, templatePath, value);
137 | ```
138 |
139 | ##### 模版
140 |
141 | 
142 |
143 | ##### 導出
144 |
145 | 
146 |
147 | ### 表格
148 |
149 | 標籤值為 `IEmerable>`類別
150 |
151 | ##### 代碼例子
152 |
153 | ```csharp
154 | var value = new Dictionary()
155 | {
156 | ["TripHs"] = new List>
157 | {
158 | new Dictionary
159 | {
160 | { "sDate",DateTime.Parse("2022-09-08 08:30:00")},
161 | { "eDate",DateTime.Parse("2022-09-08 15:00:00")},
162 | { "How","Discussion requirement part1"},
163 | { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting02.png"), Width = 160, Height = 90 }},
164 | },
165 | new Dictionary
166 | {
167 | { "sDate",DateTime.Parse("2022-09-09 08:30:00")},
168 | { "eDate",DateTime.Parse("2022-09-09 17:00:00")},
169 | { "How","Discussion requirement part2 and development"},
170 | { "Photo",new MiniWordPicture() { Path = PathHelper.GetFile("DemoExpenseMeeting01.png"), Width = 160, Height = 90 }},
171 | },
172 | }
173 | };
174 | MiniWord.SaveAsByTemplate(path, templatePath, value);
175 | ```
176 |
177 | ##### 模版
178 |
179 | 
180 |
181 |
182 | ##### 導出
183 |
184 | 
185 |
186 |
187 |
188 | ### 二级列表
189 |
190 | Tag 是 `IEnumerable` 类别. 使用方式`{{foreach` 和 `endforeach}}`.
191 |
192 | ##### Example
193 |
194 | ```csharp
195 | var value = new Dictionary()
196 | {
197 | ["TripHs"] = new List>
198 | {
199 | new Dictionary
200 | {
201 | { "sDate", DateTime.Parse("2022-09-08 08:30:00") },
202 | { "eDate", DateTime.Parse("2022-09-08 15:00:00") },
203 | { "How", "Discussion requirement part1" },
204 | {
205 | "Details", new List()
206 | {
207 | new MiniWordForeach()
208 | {
209 | Value = new Dictionary()
210 | {
211 | {"Text", "Air"},
212 | {"Value", "Airplane"}
213 | },
214 | Separator = " | "
215 | },
216 | new MiniWordForeach()
217 | {
218 | Value = new Dictionary()
219 | {
220 | {"Text", "Parking"},
221 | {"Value", "Car"}
222 | },
223 | Separator = " / "
224 | }
225 | }
226 | }
227 | }
228 | }
229 | };
230 | MiniWord.SaveAsByTemplate(path, templatePath, value);
231 | ```
232 |
233 | ##### Template
234 |
235 | 
236 |
237 | ##### Result
238 |
239 | 
240 |
241 | ### 条件判断
242 |
243 | `@if` 和 `@endif` tags .
244 |
245 | ##### Example
246 |
247 | ```csharp
248 | var value = new Dictionary()
249 | {
250 | ["Name"] = new List(){
251 | new MiniWordHyperLink(){
252 | Url = "https://google.com",
253 | Text = "測試連結22!!"
254 | },
255 | new MiniWordHyperLink(){
256 | Url = "https://google1.com",
257 | Text = "測試連結11!!"
258 | }
259 | },
260 | ["Company_Name"] = "MiniSofteware",
261 | ["CreateDate"] = new DateTime(2021, 01, 01),
262 | ["VIP"] = true,
263 | ["Points"] = 123,
264 | ["APP"] = "Demo APP",
265 | };
266 | MiniWord.SaveAsByTemplate(path, templatePath, value);
267 | ```
268 |
269 | ##### Template
270 |
271 | 
272 |
273 | ##### Result
274 |
275 | 
276 |
277 | ### 多彩字體
278 |
279 | ##### 例子
280 |
281 | ```csharp
282 | var value = new
283 | {
284 | Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB", },
285 | Name = new[] {
286 | new MiniWordColorText { Text = "Ja", HighlightColor = "#eb70AB" },
287 | new MiniWordColorText { Text = "ck", HighlightColor = "#a56abe" }
288 | },
289 | CreateDate = new MiniWordColorText
290 | {
291 | Text = new DateTime(2021, 01, 01).ToString(),
292 | HighlightColor = "#eb70AB",
293 | FontColor = "#ffffff",
294 | },
295 | VIP = true,
296 | Points = 123,
297 | APP = "Demo APP",
298 | };
299 | MiniWord.SaveAsByTemplate(path, templatePath, value);
300 | ```
301 |
302 |
303 |
304 | ## 其他
305 |
306 | ### POCO or dynamic 參數
307 |
308 | v0.5.0 支持 POCO 或 dynamic parameter
309 |
310 | ```csharp
311 | var value = new { title = "Hello MiniWord" };
312 | MiniWord.SaveAsByTemplate(outputPath, templatePath, value);
313 | ```
314 |
315 | ### 字体FontColor和HighlightColor
316 | ```csharp
317 | var value = new
318 | {
319 | Company_Name = new MiniWordColorText { Text = "MiniSofteware", FontColor = "#eb70AB" },
320 | Name = new MiniWordColorText { Text = "Jack", HighlightColor = "#eb70AB" },
321 | CreateDate = new MiniWordColorText { Text = new DateTime(2021, 01, 01).ToString(), HighlightColor = "#eb70AB", FontColor = "#ffffff" },
322 | VIP = true,
323 | Points = 123,
324 | APP = "Demo APP",
325 | };
326 | ```
327 |
328 | ### HyperLink
329 |
330 | 我們可以嘗試使用 `MiniWordHyperLink` 類,它將模板文本替換為超鏈接。
331 |
332 | `MiniWordHyperLink` 提供了兩個主要參數。
333 |
334 | * Url: HyperLink URI 目標路徑
335 | * 文字:超鏈接文字
336 |
337 | ```csharp
338 | var value = new
339 | {
340 | ["Name"] = new MiniWordHyperLink(){
341 | Url = "https://google.com",
342 | Text = "測試連結!!"
343 | },
344 | ["Company_Name"] = "MiniSofteware",
345 | ["CreateDate"] = new DateTime(2021, 01, 01),
346 | ["VIP"] = true,
347 | ["Points"] = 123,
348 | ["APP"] = "Demo APP",
349 | };
350 | MiniWord.SaveAsByTemplate(path, templatePath, value);
351 | ```
352 |
353 | ## 例子
354 |
355 |
356 |
357 | #### ASP.NET Core 3.1 API Export
358 |
359 | ```
360 | using Microsoft.AspNetCore.Builder;
361 | using Microsoft.AspNetCore.Hosting;
362 | using Microsoft.AspNetCore.Mvc;
363 | using Microsoft.Extensions.DependencyInjection;
364 | using Microsoft.Extensions.Hosting;
365 | using System;
366 | using System.Collections.Generic;
367 | using System.IO;
368 | using System.Net;
369 | using MiniSoftware;
370 |
371 | public class Program
372 | {
373 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
374 |
375 | public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup());
376 | }
377 |
378 | public class Startup
379 | {
380 | public void ConfigureServices(IServiceCollection services) => services.AddMvc();
381 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
382 | {
383 | app.UseStaticFiles();
384 | app.UseRouting();
385 | app.UseEndpoints(endpoints =>
386 | {
387 | endpoints.MapControllerRoute(
388 | name: "default",
389 | pattern: "{controller=api}/{action=Index}/{id?}");
390 | });
391 | }
392 | }
393 |
394 | public class ApiController : Controller
395 | {
396 | public IActionResult Index()
397 | {
398 | return new ContentResult
399 | {
400 | ContentType = "text/html",
401 | StatusCode = (int)HttpStatusCode.OK,
402 | Content = @"
403 | DownloadWordFromTemplatePath
404 | DownloadWordFromTemplateBytes
405 | "
406 | };
407 | }
408 |
409 | static Dictionary defaultValue = new Dictionary()
410 | {
411 | ["title"] = "FooCompany",
412 | ["managers"] = new List> {
413 | new Dictionary{{"name","Jack"},{ "department", "HR" } },
414 | new Dictionary {{ "name", "Loan"},{ "department", "IT" } }
415 | },
416 | ["employees"] = new List> {
417 | new Dictionary{{ "name", "Wade" },{ "department", "HR" } },
418 | new Dictionary {{ "name", "Felix" },{ "department", "HR" } },
419 | new Dictionary{{ "name", "Eric" },{ "department", "IT" } },
420 | new Dictionary {{ "name", "Keaton" },{ "department", "IT" } }
421 | }
422 | };
423 |
424 | public IActionResult DownloadWordFromTemplatePath()
425 | {
426 | string templatePath = "TestTemplateComplex.docx";
427 |
428 | Dictionary value = defaultValue;
429 |
430 | MemoryStream memoryStream = new MemoryStream();
431 | MiniWord.SaveAsByTemplate(memoryStream, templatePath, value);
432 | memoryStream.Seek(0, SeekOrigin.Begin);
433 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
434 | {
435 | FileDownloadName = "demo.docx"
436 | };
437 | }
438 |
439 | private static Dictionary TemplateBytesCache = new Dictionary();
440 |
441 | static ApiController()
442 | {
443 | string templatePath = "TestTemplateComplex.docx";
444 | byte[] bytes = System.IO.File.ReadAllBytes(templatePath);
445 | TemplateBytesCache.Add(templatePath, bytes);
446 | }
447 |
448 | public IActionResult DownloadWordFromTemplateBytes()
449 | {
450 | byte[] bytes = TemplateBytesCache["TestTemplateComplex.docx"];
451 |
452 | Dictionary value = defaultValue;
453 |
454 | MemoryStream memoryStream = new MemoryStream();
455 | MiniWord.SaveAsByTemplate(memoryStream, bytes, value);
456 | memoryStream.Seek(0, SeekOrigin.Begin);
457 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
458 | {
459 | FileDownloadName = "demo.docx"
460 | };
461 | }
462 | }
463 | ```
464 |
465 |
466 |
467 |
468 |
469 | ## 支持 : [Donate Link](https://miniexcel.github.io/)
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
--------------------------------------------------------------------------------
/release-note/README.md:
--------------------------------------------------------------------------------
1 | ## Release Notes
2 |
3 |
9 |
10 |
11 | ---
12 |
13 |
16 |
17 | ---
18 |
19 |
20 | Your
Star and
Donate can make MiniWord better
21 |
22 |
23 | ---
24 |
25 | ### 0.9.1-0.9.2
26 | - [Bug] Fix async (via @isdaniel)
27 | - [Bug] Fix openxml version
28 |
29 | ### 0.9.0
30 | - [New] Support async (@isdaniel)
31 | - [New] Support new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719)
32 | - [New] Add support to conditional check for undefined tags #75 (via @bprucha)
33 | - [New] Fix when a Split Tag Text element Inner Text start with " {" instead of "{" (via @hieplenet)
34 | - [New] Extension: floating image, you can configure the image to float on the text (via @dessli)
35 | - [New] Extension: Table supports Obj.objA.List.Prop1 rendering (via wangx036)
36 | - [New] Extension: Common types support multi-level attribute rendering, such as {{Obj.A.B.C}} (via wangx036)
37 | - [New] @foreach (via wangx036)
38 | - [Bug] Fixed the problem that fonts with color, underline and other styles are normal in office, but not visible after opening with WPS (via @haozekang)
39 | - [Bug] Remove all elements between @if~@endif, not paragraph (via wangx036)
40 | - [Bug] Fix build, Fix tests, Update openxml (via @masterworgen)
41 |
42 | ### 0.8.0
43 |
44 | - [New] Support new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719)
45 |
46 | - [New] Support if statement inside foreach statement inside templates. Please refer to samples. (via @eynarhaji)
47 | - [New] Change tags for if statements for single paragraph if statement {{if and endif}} inside templates. Please refer to samples. (via @eynarhaji)
48 | - [Bug] The table should be inserted at the template tag position instead of the last row #47 (via @itldg)
49 |
50 | ### 0.7.0
51 | - [New] Add support to List inside List via `IEnumerable` and `{{foreach`/`endforeach}}` tags (via @eynarhaji)
52 | - [New] Add support to @if statements inside templates (via @eynarhaji)
53 | - [New] Support multiple color word by word (via @andy505050)
54 |
55 | ### 0.6.1
56 | - [Bug] Fixed system does not support `IEnumerable` (#39 via @isdaniel)
57 |
58 | ### 0.6.0
59 | - [New] Support hyperLink (#33 via @isdaniel)
60 | - [New] Support custom font color and highlight color (#35 via impPDX)
61 | - [New] Support 2 level object parameter (#32 via @ping9719 , @shps951023)
62 | - [Bug] Fix Multiple tags format error (#37 via @shps951023)
63 |
64 | ### 0.5.0
65 | - [New] support object & dynamic parameter (#19 via @isdaniel )
66 |
67 |
68 | ### 0.4.0
69 | - [New] support HeaderParts, FooterParts template
70 | - [Bug] fixed multiple table generate problem #18
71 |
72 | ### 0.3.0
73 | - [New] Support table generate #13
74 | - [New] datetime format -> yyyy-MM-dd HH:mm:ss
75 | - [Bug] fixed spliting template string like `{ {Tag } }` problem #17
76 |
77 |
78 | ### 0.2.1
79 |
80 | - [Bug] fixed mutiple tag System.InvalidOperationException: 'The parent of this element is null.' #13
81 |
82 |
83 |
84 | ### 0.2.0
85 |
86 | - [Feature] support array list string to generate multiple row #11
87 | - [Feature] support image #10 #3
88 | - [Feature] image support to custom width and height #8
89 | - [Feature] support multiple breakline
90 | - [Optimize] Remove xmlns declaration #7
91 |
92 | ### 0.1.1
93 |
94 | - [Bug] Fix Fuzzy Regex replace similar key
95 |
96 | ### 0.1.0
97 | - [Feature] basic template export
98 |
99 | ### 0.0.0
100 | - Init
--------------------------------------------------------------------------------
/release-note/README.zh-CN.md:
--------------------------------------------------------------------------------
1 | ## Release Notes
2 |
3 |
9 |
10 |
11 | ---
12 |
13 |
16 |
17 | ---
18 |
19 |
20 | Your
Star and
Donate can make MiniWord better
21 |
22 |
23 | ---
24 |
25 |
26 |
27 | ### 0.9.1-0.9.2
28 | - [Bug] Fix async (via @isdaniel)
29 | - [Bug] Fix openxml version
30 |
31 | ### 0.9.0
32 | - [New] Support async (@isdaniel)
33 | - [New] Support new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719)
34 | - [New] Add support to conditional check for undefined tags #75 (via @bprucha)
35 | - [New] Fix when a Split Tag Text element Inner Text start with " {" instead of "{" (via @hieplenet)
36 | - [New] Extension: floating image, you can configure the image to float on the text (via @dessli)
37 | - [New] Extension: Table supports Obj.objA.List.Prop1 rendering (via wangx036)
38 | - [New] Extension: Common types support multi-level attribute rendering, such as {{Obj.A.B.C}} (via wangx036)
39 | - [New] @foreach (via wangx036)
40 | - [Bug] Fixed the problem that fonts with color, underline and other styles are normal in office, but not visible after opening with WPS (via @haozekang)
41 | - [Bug] Remove all elements between @if~@endif, not paragraph (via wangx036)
42 | - [Bug] Fix build, Fix tests, Update openxml (via @masterworgen)
43 |
44 | ### 0.8.0
45 |
46 | - [New] 支持 new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719)
47 | - [New] 支持 to if statement inside foreach statement inside templates. Please refer to samples. (via @eynarhaji)
48 | - [New] 变更 tags for if statements for single paragraph if statement {{if and endif}} inside templates. Please refer to samples. (via @eynarhaji)
49 | - [Bug] The table should be inserted at the template tag position instead of the last row #47 (via @itldg)
50 |
51 | ### 0.7.0
52 | - [New] 支持 List inside List via `IEnumerable` and `{{foreach`/`endforeach}}` tags (via @eynarhaji)
53 | - [New] 支持 @if statements inside templates (via @eynarhaji)
54 | - [New] 支持 multiple color word by word (via @andy505050)
55 |
56 | ### 0.6.1
57 | - [Bug] 修正系统不支持 `IEnumerable` (#39 via @isdaniel)
58 |
59 | ### 0.6.0
60 | - [New] 支持 hyperLink (#33 via @isdaniel)
61 | - [New] 支持 custom font color and highlight color (#35 via impPDX)
62 | - [New] 支持 2 level object parameter (#32 via @ping9719 , @shps951023)
63 | - [Bug] 修正 Multiple tags format error (#37 via @shps951023)
64 |
65 | ### 0.5.0
66 |
67 | - [New] 支持 object & dynamic parameter (#19 via @isdaniel )
68 |
69 | ### 0.4.0
70 | - [New] 支持HeaderParts, FooterParts template
71 | - [Bug] 修正multiple table generate problem #18
72 |
73 | ### 0.3.0
74 | - [New] 支持 table 标签 #13
75 | - [New] datetime format -> yyyy-MM-dd HH:mm:ss
76 | - [Bug] fixed spliting template string like `{ {Tag } }` problem #17
77 |
78 | ### 0.2.1
79 |
80 | - [Bug] fixed mutiple tag System.InvalidOperationException: 'The parent of this element is null.' #13
81 |
82 | ### 0.2.0
83 |
84 | - [Feature] 支持 array list string 生成多行 #11
85 | - [Feature] 支持图片 #10 #3
86 | - [Feature] 图片支持自定义 width 和 height #8
87 | - [Feature] 支持多 breakline
88 | - [Optimize] 删除 xmlns declaration #7
89 |
90 | ### 0.1.1
91 | - [Bug] 修正 Fuzzy Regex replace similar key
92 |
93 |
94 | ### 0.1.0
95 | - 基本 template 导出
96 |
97 | ### 0.0.0
98 | - Init
--------------------------------------------------------------------------------
/release-note/README.zh-Hant.md:
--------------------------------------------------------------------------------
1 | ## Release Notes
2 |
3 |
9 |
10 |
11 | ---
12 |
13 |
16 |
17 | ---
18 |
19 |
20 | Your
Star and
Donate can make MiniWord better
21 |
22 |
23 | ---
24 |
25 | ### 0.9.1-0.9.2
26 | - [Bug] Fix async (via @isdaniel)
27 | - [Bug] Fix openxml version
28 |
29 | ### 0.9.0
30 | - [New] Support async (@isdaniel)
31 | - [New] Support new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719)
32 | - [New] Add support to conditional check for undefined tags #75 (via @bprucha)
33 | - [New] Fix when a Split Tag Text element Inner Text start with " {" instead of "{" (via @hieplenet)
34 | - [New] Extension: floating image, you can configure the image to float on the text (via @dessli)
35 | - [New] Extension: Table supports Obj.objA.List.Prop1 rendering (via wangx036)
36 | - [New] Extension: Common types support multi-level attribute rendering, such as {{Obj.A.B.C}} (via wangx036)
37 | - [New] @foreach (via wangx036)
38 | - [Bug] Fixed the problem that fonts with color, underline and other styles are normal in office, but not visible after opening with WPS (via @haozekang)
39 | - [Bug] Remove all elements between @if~@endif, not paragraph (via wangx036)
40 | - [Bug] Fix build, Fix tests, Update openxml (via @masterworgen)
41 |
42 |
43 | ### 0.8.0
44 |
45 | - [New] 支持 new OpenXml to solve the problem of line wrapping to multiple lines #68 (via @ping9719)
46 | - [New] 支持 to if statement inside foreach statement inside templates. Please refer to samples. (via @eynarhaji)
47 | - [New] 变更 tags for if statements for single paragraph if statement {{if and endif}} inside templates. Please refer to samples. (via @eynarhaji)
48 | - [Bug] The table should be inserted at the template tag position instead of the last row #47 (via @itldg)
49 |
50 | ### 0.7.0
51 | - [New] 支持 List inside List via `IEnumerable` and `{{foreach`/`endforeach}}` tags (via @eynarhaji)
52 | - [New] 支持 @if statements inside templates (via @eynarhaji)
53 | - [New] 支持 multiple color word by word (via @andy505050)
54 |
55 | ### 0.6.1
56 | - [Bug] 修正系統不支持 `IEnumerable` (#39 via @isdaniel)
57 |
58 | ### 0.6.0
59 | - [New] 支持 hyperLink (#33 via @isdaniel)
60 | - [New] 支持 custom font color and highlight color (#35 via impPDX)
61 | - [New] 支持 2 level object parameter (#32 via @ping9719 , @shps951023)
62 | - [Bug] 修正 Multiple tags format error (#37 via @shps951023)
63 |
64 |
65 | ### 0.5.0
66 |
67 | - [New] 支持 object & dynamic parameter (#19 via @isdaniel )
68 |
69 | ### 0.4.0
70 |
71 | - [New] 支持HeaderParts, FooterParts template
72 | - [Bug] 修正multiple table generate problem #18
73 |
74 | ### 0.3.0
75 |
76 | - [New] 支持 table 标签 #13
77 |
78 | - [New] datetime format -> yyyy-MM-dd HH:mm:ss
79 | - [Bug] fixed spliting template string like `{ {Tag } }` problem #17
80 |
81 | ### 0.2.1
82 |
83 | - [Bug] fixed mutiple tag System.InvalidOperationException: 'The parent of this element is null.' #13
84 |
85 | ### 0.2.0
86 | - [Feature] 支持 array list string 生成多行 #11
87 | - [Feature] 支持圖片 #10 #3
88 | - [Feature] 圖片支持自定義 width 和 height #8
89 | - [Feature] 支持多 breakline
90 | - [Optimize] 刪除 xmlns declaration #7
91 |
92 | ### 0.1.1
93 | - [Bug] 修正 Fuzzy Regex replace similar key
94 |
95 |
96 | ### 0.1.0
97 | - 基本 template 導出
98 |
99 | ### 0.0.0
100 | - Init
--------------------------------------------------------------------------------
/samples/docx/DemoExpenseMeeting01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/DemoExpenseMeeting01.png
--------------------------------------------------------------------------------
/samples/docx/DemoExpenseMeeting02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/DemoExpenseMeeting02.png
--------------------------------------------------------------------------------
/samples/docx/DemoLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/DemoLogo.png
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestBasicFill.docx
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/[Content_Types].xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/_rels/.rels:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/docProps/app.xml:
--------------------------------------------------------------------------------
1 |
2 | Normal.dotm 799 1 22 129 Microsoft Office Word 0 1 1 false false 150 false false 16.0000
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/docProps/core.xml:
--------------------------------------------------------------------------------
1 |
2 | Lin WeiHan Lin WeiHan 3 2022-09-11T15:14:00Z 2022-09-12T08:45:00Z
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/word/_rels/document.xml.rels:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/word/document.xml:
--------------------------------------------------------------------------------
1 |
2 | {{ Company_Name }} {{APP}} Account Data Name CreateDate VIP Points Remark {{Name}} {{ CreateDate }} {{VIP}} {{Points}} {{Name}} has {{Points}} points
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/word/fontTable.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/word/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/word/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/word/theme/theme1.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicFill/word/webSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/docx/TestBasicImage.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestBasicImage.docx
--------------------------------------------------------------------------------
/samples/docx/TestBasicImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestBasicImage.png
--------------------------------------------------------------------------------
/samples/docx/TestDemo01.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestDemo01.docx
--------------------------------------------------------------------------------
/samples/docx/TestDemo02.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestDemo02.docx
--------------------------------------------------------------------------------
/samples/docx/TestDemo03.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestDemo03.docx
--------------------------------------------------------------------------------
/samples/docx/TestDemo04.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestDemo04.docx
--------------------------------------------------------------------------------
/samples/docx/TestExpenseDemo.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestExpenseDemo.docx
--------------------------------------------------------------------------------
/samples/docx/TestForeachInTablesDemo.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestForeachInTablesDemo.docx
--------------------------------------------------------------------------------
/samples/docx/TestForeachInTablesWithIfStatementDemo.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestForeachInTablesWithIfStatementDemo.docx
--------------------------------------------------------------------------------
/samples/docx/TestIfStatement.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestIfStatement.docx
--------------------------------------------------------------------------------
/samples/docx/TestIssue11.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestIssue11.docx
--------------------------------------------------------------------------------
/samples/docx/TestIssue17.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestIssue17.docx
--------------------------------------------------------------------------------
/samples/docx/TestIssue18.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestIssue18.docx
--------------------------------------------------------------------------------
/samples/docx/TestIssue37.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestIssue37.docx
--------------------------------------------------------------------------------
/samples/docx/TestIssue43.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestIssue43.docx
--------------------------------------------------------------------------------
/samples/docx/TestIssue47.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/TestIssue47.docx
--------------------------------------------------------------------------------
/samples/docx/demo01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/samples/docx/demo01.png
--------------------------------------------------------------------------------
/src/MiniWord/Common/Enums/Extension.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware.Common.Enums
2 | {
3 | public enum Extension
4 | {
5 | Bmp,
6 | Emf,
7 | Icon,
8 | Jpeg,
9 | Pcx,
10 | Png,
11 | Svg,
12 | Tiff,
13 | Wmf
14 | }
15 | }
--------------------------------------------------------------------------------
/src/MiniWord/Extensions/ObjectExtension.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware.Extensions
2 | {
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Dynamic;
8 |
9 | internal static class ObjectExtension
10 | {
11 | internal static Dictionary ToDictionary(this object value)
12 | {
13 | if (value == null)
14 | return new Dictionary();
15 | else if (value is Dictionary dicStr)
16 | return dicStr;
17 | else if (value is ExpandoObject)
18 | return new Dictionary(value as ExpandoObject);
19 |
20 | if (IsStrongTypeEnumerable(value))
21 | throw new Exception("The parameter cannot be a collection type");
22 | else
23 | {
24 | Dictionary reuslt = new Dictionary();
25 | PropertyDescriptorCollection props = TypeDescriptor.GetProperties(value);
26 | foreach (PropertyDescriptor prop in props)
27 | {
28 | object val1 = prop.GetValue(value);
29 |
30 | if (IsStrongTypeEnumerable(val1))
31 | {
32 | var isValueOrStringType = false;
33 | List> sx = new List>();
34 | foreach (object val1item in (IEnumerable)val1)
35 | {
36 | if (val1item == null)
37 | {
38 | sx.Add(new Dictionary());
39 | continue;
40 | }
41 | // not custom type
42 | if (val1item is string || val1item is DateTime || value.GetType().IsValueType)
43 | {
44 | isValueOrStringType = true;
45 | reuslt.Add(prop.Name, val1);
46 | break;
47 | }
48 | if (val1item is Dictionary dicStr)
49 | {
50 | sx.Add(dicStr);
51 | continue;
52 | }
53 | else if (val1item is ExpandoObject)
54 | {
55 | sx.Add(new Dictionary(value as ExpandoObject));
56 | continue;
57 | }
58 |
59 | PropertyDescriptorCollection props2 = TypeDescriptor.GetProperties(val1item);
60 | Dictionary reuslt2 = new Dictionary();
61 | foreach (PropertyDescriptor prop2 in props2)
62 | {
63 | object val2 = prop2.GetValue(val1item);
64 | reuslt2.Add(prop2.Name, val2);
65 | }
66 | sx.Add(reuslt2);
67 | }
68 | if (!isValueOrStringType)
69 | reuslt.Add(prop.Name, sx);
70 | }
71 | else
72 | {
73 | reuslt.Add(prop.Name, val1);
74 | }
75 | }
76 | return reuslt;
77 | }
78 | }
79 | internal static bool IsStrongTypeEnumerable(this object obj)
80 | {
81 | return obj is IEnumerable && !(obj is string) && !(obj is char[]) && !(obj is string[]) && !(obj is MiniWordColorText[]);
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/MiniWord/Extensions/OpenXmlExtension.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware.Extensions
2 | {
3 | using DocumentFormat.OpenXml.Wordprocessing;
4 | using System;
5 | using System.Collections;
6 | using System.Collections.Generic;
7 | using System.ComponentModel;
8 | using System.Dynamic;
9 | using System.Linq;
10 | using System.Text;
11 |
12 | internal static class OpenXmlExtension
13 | {
14 | ///
15 | /// 高级搜索:得到段落里面的连续字符串
16 | ///
17 | /// 段落
18 | /// Item1:连续文本;Item2:块;Item3:块文本
19 | internal static List, List>> GetContinuousString(this Paragraph paragraph)
20 | {
21 | List, List>> tuples = new List, List>>();
22 | if (paragraph == null)
23 | return tuples;
24 |
25 | var sb = new StringBuilder();
26 | var runs = new List();
27 | var texts = new List();
28 |
29 | //段落:所有子级
30 | foreach (var pChildElement in paragraph.ChildElements)
31 | {
32 | //块
33 | if (pChildElement is Run run)
34 | {
35 | //文本块
36 | if (run.IsText())
37 | {
38 | var text = run.GetFirstChild();
39 | runs.Add(run);
40 | texts.Add(text);
41 | sb.Append(text.Text);
42 | }
43 | else
44 | {
45 | if (runs.Any())
46 | tuples.Add(new Tuple, List>(sb.ToString(), runs, texts));
47 |
48 | sb = new StringBuilder();
49 | runs = new List();
50 | texts = new List();
51 | }
52 | }
53 | //公式,书签...
54 | else
55 | {
56 | //跳过的类型
57 | if (pChildElement is BookmarkStart || pChildElement is BookmarkEnd)
58 | {
59 |
60 | }
61 | else
62 | {
63 | if (runs.Any())
64 | tuples.Add(new Tuple, List>(sb.ToString(), runs, texts));
65 |
66 | sb = new StringBuilder();
67 | runs = new List();
68 | texts = new List();
69 | }
70 | }
71 | }
72 |
73 | if (runs.Any())
74 | tuples.Add(new Tuple, List>(sb.ToString(), runs, texts));
75 |
76 | sb = null;
77 | runs = null;
78 | texts = null;
79 |
80 | return tuples;
81 | }
82 |
83 | ///
84 | /// 整理字符串到连续字符串块中
85 | ///
86 | /// 连续字符串块
87 | /// 待整理字符串
88 | internal static void TrimStringToInContinuousString(this IEnumerable texts, string text)
89 | {
90 | /*
91 | //假如块为:[A][BC][DE][FG][H]
92 | //假如替换:[AB][E][GH]
93 | //优化块为:[AB][C][DE][FGH][]
94 | */
95 |
96 | var allTxtx = string.Concat(texts.SelectMany(o => o.Text));
97 | var indexState = allTxtx.IndexOf(text);
98 | if (indexState == -1)
99 | return;
100 |
101 | int indexEnd = indexState + text.Length - 1;
102 | List> yl = new List>(allTxtx.Length);
103 | int iRun = 0;
104 | int iIndex = 0;
105 | int iRunOf = -1;
106 | foreach (var item in texts)
107 | {
108 | foreach (var item2 in item.Text)
109 | {
110 | if (indexState <= iIndex && iIndex <= indexEnd)
111 | {
112 | if (iRunOf == -1)
113 | iRunOf = iRun;
114 |
115 | yl.Add(new Tuple(iRunOf, item2));
116 | }
117 | else
118 | {
119 | yl.Add(new Tuple(iRun, item2));
120 | }
121 |
122 | iIndex++;
123 | }
124 | iRun++;
125 | }
126 |
127 | int i = 0;
128 | foreach (var item in texts)
129 | {
130 | item.Text = string.Concat(yl.Where(o => o.Item1 == i).Select(o => o.Item2));
131 | i++;
132 | }
133 |
134 | }
135 |
136 |
137 | internal static bool IsText(this Run run)
138 | {
139 | return run.Elements().All(o => o is Text || o is RunProperties);
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/src/MiniWord/Extensions/StringExtension.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware.Extensions
2 | {
3 | public static class StringExtension
4 | {
5 | public static string FirstCharacterToLower(this string str)
6 | {
7 | return string.IsNullOrWhiteSpace(str) ? str : char.ToLowerInvariant(str[0]) + str.Substring(1);
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/MiniWord/MiniWord.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware
2 | {
3 | using DocumentFormat.OpenXml.Office2013.Excel;
4 | using MiniSoftware.Extensions;
5 | using MiniSoftware.Utility;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.ComponentModel;
9 | using System.Dynamic;
10 | using System.IO;
11 | using System.Linq.Expressions;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | public static partial class MiniWord
16 | {
17 | public static void SaveAsByTemplate(string path, string templatePath, object value)
18 | {
19 | using (var stream = File.Create(path))
20 | SaveAsByTemplate(stream, templatePath, value);
21 | }
22 |
23 | public static void SaveAsByTemplate(string path, byte[] templateBytes, object value)
24 | {
25 | using (var stream = File.Create(path))
26 | SaveAsByTemplate(stream, templateBytes, value);
27 | }
28 |
29 | public static void SaveAsByTemplate(this Stream stream, string templatePath, object value)
30 | {
31 | SaveAsByTemplateImpl(stream, GetBytes(templatePath), value.ToDictionary());
32 | }
33 |
34 | public static void SaveAsByTemplate(this Stream stream, byte[] templateBytes, object value)
35 | {
36 | SaveAsByTemplateImpl(stream, templateBytes, value.ToDictionary());
37 | }
38 |
39 | public static async Task SaveAsByTemplateAsync(this Stream stream, byte[] templateBytes, object value,CancellationToken token = default(CancellationToken))
40 | {
41 | await SaveAsByTemplateImplAsync(stream, templateBytes, value.ToDictionary(),token).ConfigureAwait(false);
42 | }
43 |
44 | public static async Task SaveAsByTemplateAsync(this Stream stream, string templatePath, object value,CancellationToken token = default(CancellationToken))
45 | {
46 | await SaveAsByTemplateImplAsync(stream, await GetByteAsync(templatePath), value.ToDictionary(),token).ConfigureAwait(false);
47 | }
48 |
49 | public static async Task SaveAsByTemplateAsync(string path, string templatePath, object value,CancellationToken token = default(CancellationToken))
50 | {
51 | using (var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, true))
52 | await SaveAsByTemplateImplAsync(stream, await GetByteAsync(templatePath), value.ToDictionary(),token);
53 | }
54 |
55 | public static async Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value,CancellationToken token = default(CancellationToken))
56 | {
57 | using (var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, true))
58 | await SaveAsByTemplateImplAsync(stream, templateBytes, value.ToDictionary(),token);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/src/MiniWord/MiniWord.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net45;netstandard2.0;
4 | 0.9.1
5 |
6 |
7 | MiniWord
8 | MiniWord
9 | MiniWord
10 | word;template;micro-helper;mini;openxml;helper;
11 | .NET Word(docx) exporting template engine without COM+ and interop (support Linux and Mac)
12 |
13 | mini-software, Wei Lin, eynarhaji
14 | MiniWord
15 | mini-software, Wei Lin, 2022 onwards
16 | en
17 | https://raw.githubusercontent.com/mini-software/MiniWord/main/LICENSE
18 | MiniSoftware
19 | Apache-2.0
20 | https://github.com/mini-software/MiniWord
21 | https://github.com/mini-software/MiniWord
22 | icon.png
23 | Please Check [Release Notes](https://github.com/mini-software/MiniWord/tree/main/release-note)
24 | Github
25 |
26 |
27 |
28 | PreserveNewest
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/MiniWord/MiniWordColorText.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MiniSoftware
8 | {
9 | public class MiniWordColorText
10 | {
11 | public string FontColor { get; set; }
12 | public string Text { get; set; }
13 | public string HighlightColor { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/MiniWord/MiniWordForeach.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MiniSoftware
8 | {
9 | public class MiniWordForeach
10 | {
11 | public Dictionary Value { get; set; }
12 | public string Separator { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/MiniWord/MiniWordHyperLink.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware
2 | {
3 | using DocumentFormat.OpenXml.Wordprocessing;
4 | using MiniSoftware.Utility;
5 |
6 | public class MiniWordHyperLink
7 | {
8 | public string Url { get; set; }
9 |
10 | public string Text { get; set; }
11 |
12 | public UnderlineValues UnderLineValue { get; set; } = UnderlineValues.Single;
13 |
14 | public TargetFrameType TargetFrame { get; set; } = TargetFrameType.Blank;
15 |
16 | internal string GetTargetFrame()
17 | {
18 |
19 | switch (TargetFrame)
20 | {
21 | case TargetFrameType.Blank:
22 | return "_blank";
23 | case TargetFrameType.Top:
24 | return "_top";
25 | case TargetFrameType.Self:
26 | return "_self";
27 | case TargetFrameType.Parent:
28 | return "_parent";
29 | }
30 |
31 | return "_blank";
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/MiniWord/MiniWordPicture.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using DocumentFormat.OpenXml;
3 | using DocumentFormat.OpenXml.Packaging;
4 | using MiniSoftware.Common.Enums;
5 | using MiniSoftware.Extensions;
6 |
7 | namespace MiniSoftware
8 | {
9 | public enum MiniWordPictureWrappingType
10 | {
11 | Inline,
12 | Anchor
13 | }
14 |
15 | public class MiniWordPicture
16 | {
17 | public MiniWordPicture(string path, long width = 400, long height = 400)
18 | {
19 | Path = path;
20 | Width = width;
21 | Height= height;
22 | }
23 |
24 | public MiniWordPicture(byte[] bytes, Extension extension, long width = 400, long height = 400)
25 | {
26 | Bytes = bytes;
27 | Extension = extension.ToString().FirstCharacterToLower();
28 |
29 | Width = width;
30 | Height = height;
31 | }
32 |
33 | private string _extension;
34 |
35 | public MiniWordPictureWrappingType WrappingType { get; set; } = MiniWordPictureWrappingType.Inline;
36 |
37 | public bool BehindDoc { get; set; } = false;
38 | public bool AllowOverlap { get; set; } = false;
39 | public long HorizontalPositionOffset { get; set; } = 0;
40 | public long VerticalPositionOffset { get; set; } = 0;
41 |
42 | public string Path { get; set; }
43 |
44 | public string Extension
45 | {
46 | get
47 | {
48 | if (!string.IsNullOrWhiteSpace(_extension))
49 | return _extension;
50 |
51 | if (Path != null)
52 | return System.IO.Path.GetExtension(Path).ToUpperInvariant().Replace(".", "");
53 |
54 | return string.Empty;
55 | }
56 | set => _extension = value;
57 | }
58 |
59 | internal PartTypeInfo GetImagePartType
60 | {
61 | get
62 | {
63 | switch (Extension.ToLower())
64 | {
65 | case "bmp": return ImagePartType.Bmp;
66 | case "emf": return ImagePartType.Emf;
67 | case "ico": return ImagePartType.Icon;
68 | case "jpg":
69 | case "jpeg":
70 | return ImagePartType.Jpeg;
71 | case "pcx": return ImagePartType.Pcx;
72 | case "png": return ImagePartType.Png;
73 | case "svg": return ImagePartType.Svg;
74 | case "tiff": return ImagePartType.Tiff;
75 | case "wmf": return ImagePartType.Wmf;
76 | default:
77 | throw new NotSupportedException($"{_extension} is not supported");
78 | }
79 | }
80 | }
81 |
82 | public byte[] Bytes { get; set; }
83 |
84 | ///
85 | /// Unit is Pixel
86 | ///
87 | public Int64Value Width { get; set; }
88 |
89 | internal Int64Value Cx => Width * 9525;
90 |
91 | ///
92 | /// Unit is Pixel
93 | ///
94 | public Int64Value Height { get; set; }
95 |
96 | //format resource from http://openxmltrix.blogspot.com/2011/04/updating-images-in-image-placeholde-and.html
97 | internal Int64Value Cy => Height * 9525;
98 | }
99 | }
--------------------------------------------------------------------------------
/src/MiniWord/Utility/Helpers.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware.Utility
2 | {
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Dynamic;
6 | using System.IO;
7 | using System.Threading.Tasks;
8 |
9 | internal static partial class Helpers
10 | {
11 | public static FileStream OpenSharedRead(string path) => File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/MiniWord/Utility/TargetFrameType.cs:
--------------------------------------------------------------------------------
1 | namespace MiniSoftware.Utility
2 | {
3 | public enum TargetFrameType
4 | {
5 | Blank,
6 | Self,
7 | Parent,
8 | Top
9 | }
10 | }
--------------------------------------------------------------------------------
/src/MiniWord/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/src/MiniWord/icon.png
--------------------------------------------------------------------------------
/tests/AspNetCoreDemo/AspNetCoreDemo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32328.378
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreDemo", "AspNetCoreDemo\AspNetCoreDemo.csproj", "{81AA0AC7-2A3D-4F12-8CFA-811DEA75FBEF}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniWord", "..\..\src\MiniWord\MiniWord.csproj", "{B64788A9-F3C6-46D0-A6E4-53D94538C264}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {81AA0AC7-2A3D-4F12-8CFA-811DEA75FBEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {81AA0AC7-2A3D-4F12-8CFA-811DEA75FBEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {81AA0AC7-2A3D-4F12-8CFA-811DEA75FBEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {81AA0AC7-2A3D-4F12-8CFA-811DEA75FBEF}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {B64788A9-F3C6-46D0-A6E4-53D94538C264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {B64788A9-F3C6-46D0-A6E4-53D94538C264}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {B64788A9-F3C6-46D0-A6E4-53D94538C264}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {B64788A9-F3C6-46D0-A6E4-53D94538C264}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {A843B90B-631D-4D7E-9646-6797EB549F68}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/tests/AspNetCoreDemo/AspNetCoreDemo/AspNetCoreDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | PreserveNewest
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tests/AspNetCoreDemo/AspNetCoreDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Hosting;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.IO;
9 | using System.Net;
10 | using MiniSoftware;
11 |
12 | public class Program
13 | {
14 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
15 |
16 | public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup());
17 | }
18 |
19 | public class Startup
20 | {
21 | public void ConfigureServices(IServiceCollection services) => services.AddMvc();
22 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
23 | {
24 | app.UseStaticFiles();
25 | app.UseRouting();
26 | app.UseEndpoints(endpoints =>
27 | {
28 | endpoints.MapControllerRoute(
29 | name: "default",
30 | pattern: "{controller=api}/{action=Index}/{id?}");
31 | });
32 | }
33 | }
34 |
35 | public class ApiController : Controller
36 | {
37 | public IActionResult Index()
38 | {
39 | return new ContentResult
40 | {
41 | ContentType = "text/html",
42 | StatusCode = (int)HttpStatusCode.OK,
43 | Content = @"
44 | DownloadWordFromTemplatePath
45 | DownloadWordFromTemplateBytes
46 | DownloadWordFromTemplatePathByObjectType
47 | DownloadWordFromTemplatePathWithHyperLink
48 | "
49 | };
50 | }
51 |
52 | static Dictionary defaultValue = new Dictionary()
53 | {
54 | ["title"] = "FooCompany",
55 | ["managers"] = new List> {
56 | new Dictionary{{"name","Jack"},{ "department", "HR" } },
57 | new Dictionary {{ "name", "Loan"},{ "department", "IT" } }
58 | },
59 | ["employees"] = new List> {
60 | new Dictionary{{ "name", "Wade" },{ "department", "HR" } },
61 | new Dictionary {{ "name", "Felix" },{ "department", "HR" } },
62 | new Dictionary{{ "name", "Eric" },{ "department", "IT" } },
63 | new Dictionary {{ "name", "Keaton" },{ "department", "IT" } }
64 | }
65 | };
66 |
67 | static object objectValue = new
68 | {
69 | title = "FooCompany",
70 | managers = new List> {
71 | new Dictionary{{"name","Jack"},{ "department", "HR" } },
72 | new Dictionary {{ "name", "Loan"},{ "department", "IT" } }
73 | },
74 | employees = new List> {
75 | new Dictionary{{ "name", "Wade" },{ "department", "HR" } },
76 | new Dictionary {{ "name", "Felix" },{ "department", "HR" } },
77 | new Dictionary{{ "name", "Eric" },{ "department", "IT" } },
78 | new Dictionary {{ "name", "Keaton" },{ "department", "IT" } }
79 | }
80 | };
81 |
82 | static object hyperLinktValue = new
83 | {
84 | title = new MiniWorHyperLink() {
85 | TargetFrame = TargetFrameType.Self,
86 | Text = "This is a HyperLink!!",
87 | Url = "https://google.com"
88 | },
89 | managers = new List> {
90 | new Dictionary{{"name","Jack"},{ "department", "HR" } },
91 | new Dictionary {{ "name", "Loan"},{ "department", "IT" } }
92 | },
93 | employees = new List> {
94 | new Dictionary{{ "name", "Wade" },{ "department", "HR" } },
95 | new Dictionary {{ "name", "Felix" },{ "department", "HR" } },
96 | new Dictionary{{ "name", "Eric" },{ "department", "IT" } },
97 | new Dictionary {{ "name", "Keaton" },{ "department", "IT" } }
98 | }
99 | };
100 |
101 | public IActionResult DownloadWordFromTemplatePath()
102 | {
103 | string templatePath = "TestTemplateComplex.docx";
104 |
105 | Dictionary value = defaultValue;
106 |
107 | MemoryStream memoryStream = new MemoryStream();
108 | MiniWord.SaveAsByTemplate(memoryStream, templatePath, value);
109 | memoryStream.Seek(0, SeekOrigin.Begin);
110 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
111 | {
112 | FileDownloadName = "demo.docx"
113 | };
114 | }
115 |
116 | public IActionResult DownloadWordFromTemplatePathByObjectType()
117 | {
118 | string templatePath = "TestTemplateComplex.docx";
119 |
120 | MemoryStream memoryStream = new MemoryStream();
121 | MiniWord.SaveAsByTemplate(memoryStream, templatePath, objectValue);
122 | memoryStream.Seek(0, SeekOrigin.Begin);
123 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
124 | {
125 | FileDownloadName = "demo.docx"
126 | };
127 | }
128 |
129 | public IActionResult DownloadWordFromTemplatePathWithHyperLink()
130 | {
131 | string templatePath = "TestTemplateComplex.docx";
132 |
133 | MemoryStream memoryStream = new MemoryStream();
134 | MiniWord.SaveAsByTemplate(memoryStream, templatePath, hyperLinktValue);
135 | memoryStream.Seek(0, SeekOrigin.Begin);
136 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
137 | {
138 | FileDownloadName = "demo.docx"
139 | };
140 | }
141 |
142 | private static Dictionary TemplateBytesCache = new Dictionary();
143 |
144 | static ApiController()
145 | {
146 | string templatePath = "TestTemplateComplex.docx";
147 | byte[] bytes = System.IO.File.ReadAllBytes(templatePath);
148 | TemplateBytesCache.Add(templatePath, bytes);
149 | }
150 |
151 | public IActionResult DownloadWordFromTemplateBytes()
152 | {
153 | byte[] bytes = TemplateBytesCache["TestTemplateComplex.docx"];
154 |
155 | Dictionary value = defaultValue;
156 |
157 | MemoryStream memoryStream = new MemoryStream();
158 | MiniWord.SaveAsByTemplate(memoryStream, bytes, value);
159 | memoryStream.Seek(0, SeekOrigin.Begin);
160 | return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
161 | {
162 | FileDownloadName = "demo.docx"
163 | };
164 | }
165 |
166 | }
--------------------------------------------------------------------------------
/tests/AspNetCoreDemo/AspNetCoreDemo/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:29745",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "AspNetCoreDemo": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "applicationUrl": "http://localhost:5000",
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/AspNetCoreDemo/AspNetCoreDemo/TestTemplateComplex.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mini-software/MiniWord/0f47ffba9b19ec488f1c16092a1ea073871714e4/tests/AspNetCoreDemo/AspNetCoreDemo/TestTemplateComplex.docx
--------------------------------------------------------------------------------
/tests/AspNetCoreDemo/AspNetCoreDemo/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/AspNetCoreDemo/AspNetCoreDemo/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/tests/MiniWordTests/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Compression;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Xml;
6 | using System.Xml.Linq;
7 |
8 | namespace MiniWordTests
9 | {
10 | internal static class Helpers
11 | {
12 | internal static string GetZipFileContent(string zipPath, string filePath)
13 | {
14 | var ns = new XmlNamespaceManager(new NameTable());
15 | ns.AddNamespace("x", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
16 | using (var stream = File.OpenRead(zipPath))
17 | using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read, false, Encoding.UTF8))
18 | {
19 | var entry = archive.Entries.Single(w => w.FullName == filePath);
20 | using (var sheetStream = entry.Open())
21 | {
22 | var doc = XDocument.Load(sheetStream);
23 | return doc.ToString();
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/MiniWordTests/MiniWordTestAsync.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using DocumentFormat.OpenXml.Office2010.ExcelAc;
5 | using MiniSoftware;
6 | using Xunit;
7 |
8 | namespace MiniWordTests
9 | {
10 | public class MiniWordTestAsync
11 | {
12 | [Fact]
13 | public async void TestForeachLoopInTablesAsync()
14 | {
15 | var path = PathHelper.GetTempFilePath();
16 | var templatePath = PathHelper.GetFile("TestForeachInTablesDemo.docx");
17 | var value = new Dictionary()
18 | {
19 | ["TripHs"] = new List>
20 | {
21 | new Dictionary
22 | {
23 | { "sDate", DateTime.Parse("2022-09-08 08:30:00") },
24 | { "eDate", DateTime.Parse("2022-09-08 15:00:00") },
25 | { "How", "Discussion requirement part1" },
26 | {
27 | "Details", new List()
28 | {
29 | new MiniWordForeach()
30 | {
31 | Value = new Dictionary()
32 | {
33 | {"Text", "Air"},
34 | {"Value", "Airplane"}
35 | },
36 | Separator = " | "
37 | },
38 | new MiniWordForeach()
39 | {
40 | Value = new Dictionary()
41 | {
42 | {"Text", "Parking"},
43 | {"Value", "Car"}
44 | },
45 | Separator = " / "
46 | },
47 | new MiniWordForeach()
48 | {
49 | Value = new Dictionary()
50 | {
51 | {"Text", "Hotel"},
52 | {"Value", "Room"}
53 | },
54 | Separator = ", "
55 | },
56 | new MiniWordForeach()
57 | {
58 | Value = new Dictionary()
59 | {
60 | {"Text", "Food"},
61 | {"Value", "Plate"}
62 | },
63 | Separator = ""
64 | }
65 | }
66 | }
67 | },
68 | new Dictionary
69 | {
70 | { "sDate", DateTime.Parse("2022-09-09 08:30:00") },
71 | { "eDate", DateTime.Parse("2022-09-09 17:00:00") },
72 | { "How", "Discussion requirement part2 and development" },
73 | {
74 | "Details", new List()
75 | {
76 | new MiniWordForeach()
77 | {
78 | Value = new Dictionary()
79 | {
80 | {"Text", "Air"},
81 | {"Value", "Airplane"}
82 | },
83 | Separator = " | "
84 | },
85 | new MiniWordForeach()
86 | {
87 | Value = new Dictionary()
88 | {
89 | {"Text", "Parking"},
90 | {"Value", "Car"}
91 | },
92 | Separator = " / "
93 | },
94 | new MiniWordForeach()
95 | {
96 | Value = new Dictionary()
97 | {
98 | {"Text", "Hotel"},
99 | {"Value", "Room"}
100 | },
101 | Separator = ", "
102 | },
103 | new MiniWordForeach()
104 | {
105 | Value = new Dictionary()
106 | {
107 | {"Text", "Food"},
108 | {"Value", "Plate"}
109 | },
110 | Separator = ""
111 | }
112 | }
113 | }
114 | }
115 | }
116 | };
117 | await MiniWord.SaveAsByTemplateAsync(path, templatePath, value);
118 | //System.Diagnostics.Process.Start("explorer.exe", path);
119 | var xml = Helpers.GetZipFileContent(path, "word/document.xml");
120 | Assert.Contains(@"Discussion requirement part2 and development", xml);
121 | Assert.Contains(@"Discussion requirement part1", xml);
122 | Assert.Contains(
123 | "Air way to the Airplane | Parking way to the Car / Hotel way to the Room, Food way to the Plate", xml);
124 | }
125 |
126 | [Fact]
127 | public async void MiniWordIfStatement_FirstIfAsync()
128 | {
129 | var path = PathHelper.GetTempFilePath();
130 | var templatePath = PathHelper.GetFile("TestIfStatement.docx");
131 | var value = new Dictionary()
132 | {
133 | ["Name"] = new List(){
134 | new MiniWordHyperLink(){
135 | Url = "https://google.com",
136 | Text = "測試連結22!!"
137 | },
138 | new MiniWordHyperLink(){
139 | Url = "https://google1.com",
140 | Text = "測試連結11!!"
141 | }
142 | },
143 | ["Company_Name"] = "MiniSofteware",
144 | ["CreateDate"] = new DateTime(2021, 01, 01),
145 | ["VIP"] = true,
146 | ["Points"] = 123,
147 | ["APP"] = "Demo APP",
148 | };
149 | await MiniWord.SaveAsByTemplateAsync(path, templatePath, value);
150 | //Console.WriteLine(path);
151 | var docXml = Helpers.GetZipFileContent(path, "word/document.xml");
152 | Assert.Contains("First if chosen: MiniSofteware", docXml);
153 | Assert.DoesNotContain("Second if chosen: MaxiSoftware", docXml);
154 | Assert.Contains("Points are greater than 100", docXml);
155 | Assert.Contains("CreateDate is not less than 2021", docXml);
156 | Assert.DoesNotContain("CreateDate is not greater than 2021", docXml);
157 | }
158 |
159 | [Fact]
160 | public async void TestForeachLoopInTablesWithIfStatementAsync()
161 | {
162 | var path = PathHelper.GetTempFilePath();
163 | var templatePath = PathHelper.GetFile("TestForeachInTablesWithIfStatementDemo.docx");
164 | var value = new Dictionary()
165 | {
166 | ["TripHs"] = new List>
167 | {
168 | new Dictionary
169 | {
170 | { "sDate", DateTime.Parse("2022-09-08 08:30:00") },
171 | { "eDate", DateTime.Parse("2022-09-08 15:00:00") },
172 | { "How", "Discussion requirement part1" },
173 | {
174 | "Details", new List()
175 | {
176 | new MiniWordForeach()
177 | {
178 | Value = new Dictionary()
179 | {
180 | {"Text", "Air"},
181 | {"Value", "Airplane"}
182 | },
183 | Separator = " | "
184 | },
185 | new MiniWordForeach()
186 | {
187 | Value = new Dictionary()
188 | {
189 | {"Text", "Parking"},
190 | {"Value", "Car"}
191 | },
192 | Separator = " / "
193 | },
194 | new MiniWordForeach()
195 | {
196 | Value = new Dictionary()
197 | {
198 | {"Text", "Hotel"},
199 | {"Value", "Room"}
200 | },
201 | Separator = ", "
202 | },
203 | new MiniWordForeach()
204 | {
205 | Value = new Dictionary()
206 | {
207 | {"Text", "Food"},
208 | {"Value", "Plate"}
209 | },
210 | Separator = ""
211 | }
212 | }
213 | }
214 | },
215 | new Dictionary
216 | {
217 | { "sDate", DateTime.Parse("2022-09-09 08:30:00") },
218 | { "eDate", DateTime.Parse("2022-09-09 17:00:00") },
219 | { "How", "Discussion requirement part2 and development" },
220 | {
221 | "Details", new List()
222 | {
223 | new MiniWordForeach()
224 | {
225 | Value = new Dictionary()
226 | {
227 | {"Text", "Air"},
228 | {"Value", "Airplane"}
229 | },
230 | Separator = " | "
231 | },
232 | new MiniWordForeach()
233 | {
234 | Value = new Dictionary()
235 | {
236 | {"Text", "Parking"},
237 | {"Value", "Car"}
238 | },
239 | Separator = " / "
240 | },
241 | new MiniWordForeach()
242 | {
243 | Value = new Dictionary()
244 | {
245 | {"Text", "Hotel"},
246 | {"Value", "Room"}
247 | },
248 | Separator = ", "
249 | },
250 | new MiniWordForeach()
251 | {
252 | Value = new Dictionary()
253 | {
254 | {"Text", "Food"},
255 | {"Value", "Plate"}
256 | },
257 | Separator = ""
258 | }
259 | }
260 | }
261 | }
262 | }
263 | };
264 | await MiniWord.SaveAsByTemplateAsync(path, templatePath, value);
265 | //System.Diagnostics.Process.Start("explorer.exe", path);
266 | var xml = Helpers.GetZipFileContent(path, "word/document.xml");
267 | Assert.Contains(@"Discussion requirement part2 and development", xml);
268 | Assert.Contains(@"Discussion requirement part1", xml);
269 | Assert.Contains("Air way to the Airplane | Hotel way to the Room", xml);
270 | }
271 | }
272 | }
--------------------------------------------------------------------------------
/tests/MiniWordTests/MiniWordTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using DocumentFormat.OpenXml.Office2010.ExcelAc;
4 | using MiniSoftware;
5 | using Xunit;
6 |
7 | namespace MiniWordTests
8 | {
9 | public class MiniWordTests
10 | {
11 | [Fact]
12 | public void TestForeachLoopInTables()
13 | {
14 | var path = PathHelper.GetTempFilePath();
15 | var templatePath = PathHelper.GetFile("TestForeachInTablesDemo.docx");
16 | var value = new Dictionary()
17 | {
18 | ["TripHs"] = new List>
19 | {
20 | new Dictionary
21 | {
22 | { "sDate", DateTime.Parse("2022-09-08 08:30:00") },
23 | { "eDate", DateTime.Parse("2022-09-08 15:00:00") },
24 | { "How", "Discussion requirement part1" },
25 | {
26 | "Details", new List()
27 | {
28 | new MiniWordForeach()
29 | {
30 | Value = new Dictionary()
31 | {
32 | {"Text", "Air"},
33 | {"Value", "Airplane"}
34 | },
35 | Separator = " | "
36 | },
37 | new MiniWordForeach()
38 | {
39 | Value = new Dictionary()
40 | {
41 | {"Text", "Parking"},
42 | {"Value", "Car"}
43 | },
44 | Separator = " / "
45 | },
46 | new MiniWordForeach()
47 | {
48 | Value = new Dictionary()
49 | {
50 | {"Text", "Hotel"},
51 | {"Value", "Room"}
52 | },
53 | Separator = ", "
54 | },
55 | new MiniWordForeach()
56 | {
57 | Value = new Dictionary()
58 | {
59 | {"Text", "Food"},
60 | {"Value", "Plate"}
61 | },
62 | Separator = ""
63 | }
64 | }
65 | }
66 | },
67 | new Dictionary
68 | {
69 | { "sDate", DateTime.Parse("2022-09-09 08:30:00") },
70 | { "eDate", DateTime.Parse("2022-09-09 17:00:00") },
71 | { "How", "Discussion requirement part2 and development" },
72 | {
73 | "Details", new List()
74 | {
75 | new MiniWordForeach()
76 | {
77 | Value = new Dictionary()
78 | {
79 | {"Text", "Air"},
80 | {"Value", "Airplane"}
81 | },
82 | Separator = " | "
83 | },
84 | new MiniWordForeach()
85 | {
86 | Value = new Dictionary()
87 | {
88 | {"Text", "Parking"},
89 | {"Value", "Car"}
90 | },
91 | Separator = " / "
92 | },
93 | new MiniWordForeach()
94 | {
95 | Value = new Dictionary()
96 | {
97 | {"Text", "Hotel"},
98 | {"Value", "Room"}
99 | },
100 | Separator = ", "
101 | },
102 | new MiniWordForeach()
103 | {
104 | Value = new Dictionary()
105 | {
106 | {"Text", "Food"},
107 | {"Value", "Plate"}
108 | },
109 | Separator = ""
110 | }
111 | }
112 | }
113 | }
114 | }
115 | };
116 | MiniWord.SaveAsByTemplate(path, templatePath, value);
117 | //System.Diagnostics.Process.Start("explorer.exe", path);
118 | var xml = Helpers.GetZipFileContent(path, "word/document.xml");
119 | Assert.Contains(@"Discussion requirement part2 and development", xml);
120 | Assert.Contains(@"Discussion requirement part1", xml);
121 | Assert.Contains(
122 | "Air way to the Airplane | Parking way to the Car / Hotel way to the Room, Food way to the Plate", xml);
123 | }
124 |
125 | [Fact]
126 | public void MiniWordIfStatement_FirstIf()
127 | {
128 | var path = PathHelper.GetTempFilePath();
129 | var templatePath = PathHelper.GetFile("TestIfStatement.docx");
130 | var value = new Dictionary()
131 | {
132 | ["Name"] = new List(){
133 | new MiniWordHyperLink(){
134 | Url = "https://google.com",
135 | Text = "測試連結22!!"
136 | },
137 | new MiniWordHyperLink(){
138 | Url = "https://google1.com",
139 | Text = "測試連結11!!"
140 | }
141 | },
142 | ["Company_Name"] = "MiniSofteware",
143 | ["CreateDate"] = new DateTime(2021, 01, 01),
144 | ["VIP"] = true,
145 | ["Points"] = 123,
146 | ["APP"] = "Demo APP",
147 | };
148 | MiniWord.SaveAsByTemplate(path, templatePath, value);
149 | //Console.WriteLine(path);
150 | var docXml = Helpers.GetZipFileContent(path, "word/document.xml");
151 | Assert.Contains("First if chosen: MiniSofteware", docXml);
152 | Assert.DoesNotContain("Second if chosen: MaxiSoftware", docXml);
153 | Assert.Contains("Points are greater than 100", docXml);
154 | Assert.Contains("CreateDate is not less than 2021", docXml);
155 | Assert.DoesNotContain("CreateDate is not greater than 2021", docXml);
156 | }
157 |
158 | [Fact]
159 | public void TestForeachLoopInTablesWithIfStatement()
160 | {
161 | var path = PathHelper.GetTempFilePath();
162 | var templatePath = PathHelper.GetFile("TestForeachInTablesWithIfStatementDemo.docx");
163 | var value = new Dictionary()
164 | {
165 | ["TripHs"] = new List>
166 | {
167 | new Dictionary
168 | {
169 | { "sDate", DateTime.Parse("2022-09-08 08:30:00") },
170 | { "eDate", DateTime.Parse("2022-09-08 15:00:00") },
171 | { "How", "Discussion requirement part1" },
172 | {
173 | "Details", new List