├── .editorconfig ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── DocXToPdfConverter ├── DocXToPdfConverter.csproj ├── DocXToPdfConverter.sln ├── DocXToPdfHandlers │ ├── ConvertWithLibreOffice.cs │ ├── DocXHandler.cs │ ├── HtmlHandler.cs │ ├── ImageHandler.cs │ └── StreamHandler.cs ├── Documentation │ └── Readme.md ├── OpenXmlPowerTools │ ├── ChartUpdater.cs │ ├── ColorParser.cs │ ├── Comparer │ │ ├── ComparisonUnit.cs │ │ ├── ComparisonUnitAtom.cs │ │ ├── ComparisonUnitGroup.cs │ │ ├── ComparisonUnitGroupType.cs │ │ ├── ComparisonUnitWord.cs │ │ ├── CorrelatedSequence.cs │ │ ├── CorrelationStatus.cs │ │ ├── PartSHA1HashAnnotation.cs │ │ ├── WithHierarchicalGroupingKey.cs │ │ ├── WmlComparer.Internal.Methods.ComparisonUnits.cs │ │ ├── WmlComparer.Private.Fields.cs │ │ ├── WmlComparer.Private.Methods.Hashing.cs │ │ ├── WmlComparer.Private.Methods.Lcs.cs │ │ ├── WmlComparer.Private.Methods.PreProcessMarkup.cs │ │ ├── WmlComparer.Private.Methods.ProduceDocument.cs │ │ ├── WmlComparer.Private.Methods.Util.cs │ │ ├── WmlComparer.Private.NestedTypes.cs │ │ ├── WmlComparer.Public.Methods.Compare.cs │ │ ├── WmlComparer.Public.Methods.Consolidate.cs │ │ ├── WmlComparer.Public.Methods.GetRevisions.cs │ │ ├── WmlComparer.Public.NestedTypes.cs │ │ ├── WmlComparerConsolidateSettings.cs │ │ ├── WmlComparerExtensions.cs │ │ ├── WmlComparerSettings.cs │ │ ├── WmlComparerUtil.cs │ │ └── WmlRevisedDocumentInfo.cs │ ├── DocumentAssembler.cs │ ├── DocumentBuilder.cs │ ├── ExcelFormula.cs │ ├── FieldRetriever.cs │ ├── FormattingAssembler.cs │ ├── GetListItemText_Default.cs │ ├── GetListItemText_fr_FR.cs │ ├── GetListItemText_ru_RU.cs │ ├── GetListItemText_sv_SE.cs │ ├── GetListItemText_tr_TR.cs │ ├── GetListItemText_zh_CN.cs │ ├── HtmlToWmlConverter.cs │ ├── HtmlToWmlConverterCore.cs │ ├── HtmlToWmlCssApplier.cs │ ├── HtmlToWmlCssParser.cs │ ├── ListItemRetriever.cs │ ├── MarkupSimplifier.cs │ ├── MetricsGetter.cs │ ├── OpenXmlRegex.cs │ ├── OxPtHelpers.cs │ ├── PegBase.cs │ ├── PowerToolsBlock.cs │ ├── PowerToolsBlockExtensions.cs │ ├── PresentationBuilder.cs │ ├── Previous │ │ └── WmlComparer.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── PtOpenXmlDocument.cs │ ├── PtOpenXmlUtil.cs │ ├── PtUtil.cs │ ├── ReferenceAdder.cs │ ├── RevisionAccepter.cs │ ├── RevisionProcessor.cs │ ├── SSFormula.cs │ ├── ScalarTypes.cs │ ├── SmlCellFormatter.cs │ ├── SmlDataRetriever.cs │ ├── SmlToHtmlConverter.cs │ ├── SpreadsheetDocumentManager.cs │ ├── SpreadsheetWriter.cs │ ├── StronglyTypedBlock.cs │ ├── TestUtil.cs │ ├── TextReplacer.cs │ ├── UnicodeMapper.cs │ ├── WmlDocument.cs │ ├── WmlToHtmlConverter.cs │ ├── WmlToXml.cs │ ├── WorksheetAccessor.cs │ └── XlsxTables.cs ├── Placeholders.cs └── ReportGenerator.cs ├── ExampleApplication ├── ExampleApplication.csproj ├── ProductImage.jpg ├── Program.cs ├── QRCode.PNG ├── Test-HTML-page.html ├── Test-Template.docx └── smartinmedia.jpg ├── LICENSE └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\dev\GitHub\gofal\net-core-docx-html-to-pdf-converter\DocXToPdfConverter codebase based on best match to current usage at 15.09.2020 2 | # You can modify the rules from these initially generated values to suit your own policies 3 | # You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference 4 | [*.cs] 5 | 6 | 7 | #Core editorconfig formatting - indentation 8 | 9 | #use soft tabs (spaces) for indentation 10 | indent_style = space 11 | indent_size = 4 12 | tab_width = 4 13 | 14 | #Formatting - indentation options 15 | 16 | #indent switch case contents. 17 | csharp_indent_case_contents = true 18 | #indent switch labels 19 | csharp_indent_switch_labels = true 20 | 21 | #Formatting - new line options 22 | 23 | #place catch statements on a new line 24 | csharp_new_line_before_catch = true 25 | #place else statements on a new line 26 | csharp_new_line_before_else = true 27 | #require members of anonymous types to be on separate lines 28 | csharp_new_line_before_members_in_anonymous_types = true 29 | #require members of object intializers to be on separate lines 30 | csharp_new_line_before_members_in_object_initializers = true 31 | #require braces to be on a new line for methods, lambdas, anonymous_types, properties, object_collection_array_initializers, accessors, types, and control_blocks (also known as "Allman" style) 32 | csharp_new_line_before_open_brace = methods, lambdas, anonymous_types, properties, object_collection_array_initializers, accessors, types, control_blocks 33 | 34 | #Formatting - organize using options 35 | 36 | #sort System.* using directives alphabetically, and place them before other usings 37 | dotnet_sort_system_directives_first = true 38 | 39 | #Formatting - spacing options 40 | 41 | #require NO space between a cast and the value 42 | csharp_space_after_cast = false 43 | #require a space before the colon for bases or interfaces in a type declaration 44 | csharp_space_after_colon_in_inheritance_clause = true 45 | #require a space after a keyword in a control flow statement such as a for loop 46 | csharp_space_after_keywords_in_control_flow_statements = true 47 | #require a space before the colon for bases or interfaces in a type declaration 48 | csharp_space_before_colon_in_inheritance_clause = true 49 | #remove space within empty argument list parentheses 50 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 51 | #remove space between method call name and opening parenthesis 52 | csharp_space_between_method_call_name_and_opening_parenthesis = false 53 | #do not place space characters after the opening parenthesis and before the closing parenthesis of a method call 54 | csharp_space_between_method_call_parameter_list_parentheses = false 55 | #remove space within empty parameter list parentheses for a method declaration 56 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 57 | #place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. 58 | csharp_space_between_method_declaration_parameter_list_parentheses = false 59 | 60 | #Formatting - wrapping options 61 | 62 | #leave code block on single line 63 | csharp_preserve_single_line_blocks = true 64 | #leave statements and member declarations on the same line 65 | csharp_preserve_single_line_statements = true 66 | 67 | #Style - Code block preferences 68 | 69 | #prefer no curly braces if allowed 70 | csharp_prefer_braces = false:suggestion 71 | 72 | #Style - expression bodied member options 73 | 74 | #prefer block bodies for constructors 75 | csharp_style_expression_bodied_constructors = false:suggestion 76 | #prefer block bodies for methods 77 | csharp_style_expression_bodied_methods = false:suggestion 78 | 79 | #Style - expression level options 80 | 81 | #prefer out variables to be declared before the method call 82 | csharp_style_inlined_variable_declaration = false:suggestion 83 | #prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them 84 | dotnet_style_predefined_type_for_member_access = true:suggestion 85 | 86 | #Style - Expression-level preferences 87 | 88 | #prefer objects to be initialized using object initializers when possible 89 | dotnet_style_object_initializer = true:suggestion 90 | #prefer inferred anonymous type member names 91 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion 92 | 93 | #Style - implicit and explicit types 94 | 95 | #prefer explicit type over var in all cases, unless overridden by another code style rule 96 | csharp_style_var_elsewhere = false:silent 97 | #prefer explicit type over var to declare variables with built-in system types such as int 98 | csharp_style_var_for_built_in_types = false:silent 99 | #prefer var when the type is already mentioned on the right-hand side of a declaration expression 100 | csharp_style_var_when_type_is_apparent = true:silent 101 | 102 | #Style - language keyword and framework type options 103 | 104 | #prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them 105 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 106 | 107 | #Style - modifier options 108 | 109 | #prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. 110 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion 111 | 112 | #Style - Modifier preferences 113 | 114 | #when this rule is set to a list of modifiers, prefer the specified ordering. 115 | csharp_preferred_modifier_order = public,private,internal,static,readonly,override:suggestion 116 | 117 | #Style - Pattern matching 118 | 119 | #prefer pattern matching instead of is expression with type casts 120 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 121 | 122 | #Style - qualification options 123 | 124 | #prefer fields not to be prefaced with this. or Me. in Visual Basic 125 | dotnet_style_qualification_for_field = false:suggestion 126 | #prefer methods not to be prefaced with this. or Me. in Visual Basic 127 | dotnet_style_qualification_for_method = false:suggestion 128 | #prefer properties not to be prefaced with this. or Me. in Visual Basic 129 | dotnet_style_qualification_for_property = false:suggestion 130 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Coverlet is a free, cross platform Code Coverage Tool 141 | coverage*[.json, .xml, .info] 142 | 143 | # Visual Studio code coverage results 144 | *.coverage 145 | *.coveragexml 146 | 147 | # NCrunch 148 | _NCrunch_* 149 | .*crunch*.local.xml 150 | nCrunchTemp_* 151 | 152 | # MightyMoose 153 | *.mm.* 154 | AutoTest.Net/ 155 | 156 | # Web workbench (sass) 157 | .sass-cache/ 158 | 159 | # Installshield output folder 160 | [Ee]xpress/ 161 | 162 | # DocProject is a documentation generator add-in 163 | DocProject/buildhelp/ 164 | DocProject/Help/*.HxT 165 | DocProject/Help/*.HxC 166 | DocProject/Help/*.hhc 167 | DocProject/Help/*.hhk 168 | DocProject/Help/*.hhp 169 | DocProject/Help/Html2 170 | DocProject/Help/html 171 | 172 | # Click-Once directory 173 | publish/ 174 | 175 | # Publish Web Output 176 | *.[Pp]ublish.xml 177 | *.azurePubxml 178 | # Note: Comment the next line if you want to checkin your web deploy settings, 179 | # but database connection strings (with potential passwords) will be unencrypted 180 | *.pubxml 181 | *.publishproj 182 | 183 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 184 | # checkin your Azure Web App publish settings, but sensitive information contained 185 | # in these scripts will be unencrypted 186 | PublishScripts/ 187 | 188 | # NuGet Packages 189 | *.nupkg 190 | # NuGet Symbol Packages 191 | *.snupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/[Pp]ackages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/[Pp]ackages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/[Pp]ackages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | *.appxbundle 217 | *.appxupload 218 | 219 | # Visual Studio cache files 220 | # files ending in .cache can be ignored 221 | *.[Cc]ache 222 | # but keep track of directories ending in .cache 223 | !?*.[Cc]ache/ 224 | 225 | # Others 226 | ClientBin/ 227 | ~$* 228 | *~ 229 | *.dbmdl 230 | *.dbproj.schemaview 231 | *.jfm 232 | *.pfx 233 | *.publishsettings 234 | orleans.codegen.cs 235 | 236 | # Including strong name files can present a security risk 237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 238 | #*.snk 239 | 240 | # Since there are multiple workflows, uncomment next line to ignore bower_components 241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 242 | #bower_components/ 243 | 244 | # RIA/Silverlight projects 245 | Generated_Code/ 246 | 247 | # Backup & report files from converting an old project file 248 | # to a newer Visual Studio version. Backup files are not needed, 249 | # because we have git ;-) 250 | _UpgradeReport_Files/ 251 | Backup*/ 252 | UpgradeLog*.XML 253 | UpgradeLog*.htm 254 | ServiceFabricBackup/ 255 | *.rptproj.bak 256 | 257 | # SQL Server files 258 | *.mdf 259 | *.ldf 260 | *.ndf 261 | 262 | # Business Intelligence projects 263 | *.rdl.data 264 | *.bim.layout 265 | *.bim_*.settings 266 | *.rptproj.rsuser 267 | *- [Bb]ackup.rdl 268 | *- [Bb]ackup ([0-9]).rdl 269 | *- [Bb]ackup ([0-9][0-9]).rdl 270 | 271 | # Microsoft Fakes 272 | FakesAssemblies/ 273 | 274 | # GhostDoc plugin setting file 275 | *.GhostDoc.xml 276 | 277 | # Node.js Tools for Visual Studio 278 | .ntvs_analysis.dat 279 | node_modules/ 280 | 281 | # Visual Studio 6 build log 282 | *.plg 283 | 284 | # Visual Studio 6 workspace options file 285 | *.opt 286 | 287 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 288 | *.vbw 289 | 290 | # Visual Studio LightSwitch build output 291 | **/*.HTMLClient/GeneratedArtifacts 292 | **/*.DesktopClient/GeneratedArtifacts 293 | **/*.DesktopClient/ModelManifest.xml 294 | **/*.Server/GeneratedArtifacts 295 | **/*.Server/ModelManifest.xml 296 | _Pvt_Extensions 297 | 298 | # Paket dependency manager 299 | .paket/paket.exe 300 | paket-files/ 301 | 302 | # FAKE - F# Make 303 | .fake/ 304 | 305 | # CodeRush personal settings 306 | .cr/personal 307 | 308 | # Python Tools for Visual Studio (PTVS) 309 | __pycache__/ 310 | *.pyc 311 | 312 | # Cake - Uncomment if you are using it 313 | # tools/** 314 | # !tools/packages.config 315 | 316 | # Tabs Studio 317 | *.tss 318 | 319 | # Telerik's JustMock configuration file 320 | *.jmconfig 321 | 322 | # BizTalk build output 323 | *.btp.cs 324 | *.btm.cs 325 | *.odx.cs 326 | *.xsd.cs 327 | 328 | # OpenCover UI analysis results 329 | OpenCover/ 330 | 331 | # Azure Stream Analytics local run output 332 | ASALocalRun/ 333 | 334 | # MSBuild Binary and Structured Log 335 | *.binlog 336 | 337 | # NVidia Nsight GPU debugger configuration file 338 | *.nvuser 339 | 340 | # MFractors (Xamarin productivity tool) working folder 341 | .mfractor/ 342 | 343 | # Local History for Visual Studio 344 | .localhistory/ 345 | 346 | # BeatPulse healthcheck temp database 347 | healthchecksdb 348 | 349 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 350 | MigrationBackup/ 351 | 352 | # Ionide (cross platform F# VS Code tools) working folder 353 | .ionide/ 354 | -------------------------------------------------------------------------------- /DocXToPdfConverter/DocXToPdfConverter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library 5 | netcoreapp2.1 6 | 7 | 8 | true 9 | MIT 10 | 1.0.4 11 | Dr. Martin Weihrauch 12 | Smart In Media 13 | (c) Smart In Media 14 | Report-From-DocX-HTML-To-PDF-Converter - Create custom reports based on Word docx or HTML documents and convert to PDF with .NET CORE 15 | https://github.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter 16 | https://github.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter 17 | Docx, PDF, HTML, converter 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DocXToPdfConverter/DocXToPdfConverter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29102.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocXToPdfConverter", "DocXToPdfConverter.csproj", "{73ECDD18-0B0A-480E-A5C9-5B8A865F1E0B}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleApplication", "..\ExampleApplication\ExampleApplication.csproj", "{2936F6E2-F433-4BF3-A1B3-958754AE99D7}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2C65CCD7-FA18-4422-8AEC-83C1F068BD95}" 11 | ProjectSection(SolutionItems) = preProject 12 | ..\.editorconfig = ..\.editorconfig 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {73ECDD18-0B0A-480E-A5C9-5B8A865F1E0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {73ECDD18-0B0A-480E-A5C9-5B8A865F1E0B}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {73ECDD18-0B0A-480E-A5C9-5B8A865F1E0B}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {73ECDD18-0B0A-480E-A5C9-5B8A865F1E0B}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {2936F6E2-F433-4BF3-A1B3-958754AE99D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {2936F6E2-F433-4BF3-A1B3-958754AE99D7}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {2936F6E2-F433-4BF3-A1B3-958754AE99D7}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {2936F6E2-F433-4BF3-A1B3-958754AE99D7}.Release|Any CPU.Build.0 = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {00675420-7B2A-4660-88AE-CB80053E240A} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /DocXToPdfConverter/DocXToPdfHandlers/ConvertWithLibreOffice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Reflection; 6 | using System.Threading; 7 | 8 | namespace DocXToPdfConverter.DocXToPdfHandlers 9 | { 10 | //THIS ALL COMES FROM: https://github.com/Reflexe/doc_to_pdf 11 | 12 | //And very helpful: https://stackoverflow.com/questions/30349542/command-libreoffice-headless-convert-to-pdf-test-docx-outdir-pdf-is-not 13 | 14 | 15 | public class LibreOfficeFailedException : Exception 16 | { 17 | public LibreOfficeFailedException(int exitCode) 18 | : base(string.Format("LibreOffice has failed with " + exitCode)) 19 | { } 20 | } 21 | 22 | public static class LibreOfficeWrapper 23 | { 24 | 25 | private static string GetLibreOfficePath() 26 | { 27 | switch (Environment.OSVersion.Platform) 28 | { 29 | case PlatformID.Unix: 30 | return "/usr/bin/soffice"; 31 | case PlatformID.Win32NT: 32 | string binaryDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 33 | return binaryDirectory + "\\Windows\\program\\soffice.exe"; 34 | default: 35 | throw new PlatformNotSupportedException("Your OS is not supported"); 36 | } 37 | } 38 | 39 | 40 | 41 | //libreOfficePath for Windows: e. g. "C:\\program\\soffice.exe 42 | 43 | 44 | //With Portable Apps it is here: C:\PortableApps\LibreOfficePortable\App\libreoffice\program\soffice.exe 45 | 46 | public static void Convert(string inputFile, string outputFile, string libreOfficePath) 47 | { 48 | var commandArgs = new List(); 49 | string convertedFile = ""; 50 | 51 | if (string.IsNullOrEmpty(libreOfficePath)) 52 | { 53 | libreOfficePath = GetLibreOfficePath(); 54 | } 55 | 56 | //Create tmp folder 57 | var tmpFolder = Path.Combine(Path.GetDirectoryName(outputFile), "DocXHtmlToPdfConverterTmp" + Guid.NewGuid().ToString().Substring(0, 10)); 58 | if (!Directory.Exists(tmpFolder)) 59 | { 60 | Directory.CreateDirectory(tmpFolder); 61 | } 62 | 63 | commandArgs.Add("--convert-to"); 64 | 65 | if ((inputFile.EndsWith(".html") || inputFile.EndsWith(".htm")) && outputFile.EndsWith(".pdf")) 66 | { 67 | commandArgs.Add("pdf:writer_pdf_Export"); 68 | convertedFile = Path.Combine(tmpFolder, Path.GetFileNameWithoutExtension(inputFile) + ".pdf"); 69 | } 70 | else if (inputFile.EndsWith(".docx") && outputFile.EndsWith(".pdf")) 71 | { 72 | commandArgs.Add("pdf:writer_pdf_Export"); 73 | convertedFile = Path.Combine(tmpFolder, Path.GetFileNameWithoutExtension(inputFile) + ".pdf"); 74 | } 75 | else if (inputFile.EndsWith(".docx") && (outputFile.EndsWith(".html") || outputFile.EndsWith(".htm"))) 76 | { 77 | commandArgs.Add("html:HTML:EmbedImages"); 78 | convertedFile = Path.Combine(tmpFolder, Path.GetFileNameWithoutExtension(inputFile) + ".html"); 79 | } 80 | else if ((inputFile.EndsWith(".html") || inputFile.EndsWith(".htm")) && (outputFile.EndsWith(".docx"))) 81 | { 82 | commandArgs.Add("docx:\"Office Open XML Text\""); 83 | convertedFile = Path.Combine(tmpFolder, Path.GetFileNameWithoutExtension(inputFile) + ".docx"); 84 | } 85 | 86 | commandArgs.AddRange(new[] { inputFile, "--norestore", "--writer", "--headless", "--outdir", tmpFolder }); 87 | 88 | var procStartInfo = new ProcessStartInfo(libreOfficePath); 89 | foreach (var arg in commandArgs) { procStartInfo.ArgumentList.Add(arg); } 90 | procStartInfo.RedirectStandardOutput = true; 91 | procStartInfo.UseShellExecute = false; 92 | procStartInfo.CreateNoWindow = true; 93 | procStartInfo.WorkingDirectory = Environment.CurrentDirectory; 94 | 95 | var process = new Process() { StartInfo = procStartInfo }; 96 | Process[] pname = Process.GetProcessesByName("soffice"); 97 | 98 | //Supposedly, only one instance of Libre Office can be run simultaneously 99 | while (pname.Length > 0) 100 | { 101 | Thread.Sleep(5000); 102 | pname = Process.GetProcessesByName("soffice"); 103 | } 104 | 105 | process.Start(); 106 | process.WaitForExit(); 107 | 108 | // Check for failed exit code. 109 | if (process.ExitCode != 0) 110 | { 111 | throw new LibreOfficeFailedException(process.ExitCode); 112 | } 113 | else 114 | { 115 | if (File.Exists(outputFile)) File.Delete(outputFile); 116 | if (File.Exists(convertedFile)) 117 | { 118 | File.Move(convertedFile, outputFile); 119 | } 120 | ClearDirectory(tmpFolder); 121 | Directory.Delete(tmpFolder); 122 | } 123 | } 124 | 125 | 126 | public static void Print(string inputFile, string printerName, string libreOfficePath) 127 | { 128 | var commandArgs = new List(); 129 | 130 | if (string.IsNullOrEmpty(libreOfficePath)) 131 | { 132 | libreOfficePath = GetLibreOfficePath(); 133 | } 134 | 135 | commandArgs.Add("-p"); 136 | 137 | if (!string.IsNullOrEmpty(printerName)) 138 | { 139 | commandArgs.Add("-pt"); 140 | commandArgs.Add(printerName); 141 | } 142 | 143 | commandArgs.AddRange(new[] { inputFile, "--norestore", "--writer", "--headless" }); 144 | 145 | var procStartInfo = new ProcessStartInfo(libreOfficePath); 146 | foreach (var arg in commandArgs) { procStartInfo.ArgumentList.Add(arg); } 147 | procStartInfo.RedirectStandardOutput = true; 148 | procStartInfo.UseShellExecute = false; 149 | procStartInfo.CreateNoWindow = true; 150 | procStartInfo.WorkingDirectory = Environment.CurrentDirectory; 151 | 152 | var process = new Process() { StartInfo = procStartInfo }; 153 | Process[] pname = Process.GetProcessesByName("soffice"); 154 | 155 | //Supposedly, only one instance of Libre Office can be run simultaneously 156 | while (pname.Length > 0) 157 | { 158 | Thread.Sleep(5000); 159 | pname = Process.GetProcessesByName("soffice"); 160 | } 161 | 162 | process.Start(); 163 | process.WaitForExit(); 164 | 165 | // Check for failed exit code. 166 | if (process.ExitCode != 0) 167 | { 168 | throw new LibreOfficeFailedException(process.ExitCode); 169 | } 170 | } 171 | 172 | 173 | private static void ClearDirectory(string folderName) 174 | { 175 | var dir = new DirectoryInfo(folderName); 176 | 177 | foreach (FileInfo fi in dir.GetFiles()) 178 | { 179 | fi.Delete(); 180 | } 181 | 182 | foreach (DirectoryInfo di in dir.GetDirectories()) 183 | { 184 | ClearDirectory(di.FullName); 185 | di.Delete(); 186 | } 187 | } 188 | 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /DocXToPdfConverter/DocXToPdfHandlers/HtmlHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace DocXToPdfConverter.DocXToPdfHandlers 5 | { 6 | public static class HtmlHandler 7 | { 8 | 9 | public static string ReplaceAll(string html, Placeholders _rep) 10 | { 11 | 12 | if (_rep == null) 13 | { 14 | return html; 15 | } 16 | 17 | /* 18 | * Replace Texts 19 | */ 20 | foreach (var trDict in _rep.TextPlaceholders) 21 | { 22 | html = html.Replace(_rep.TextPlaceholderStartTag + trDict.Key + _rep.TextPlaceholderEndTag, 23 | trDict.Value); 24 | } 25 | 26 | /* 27 | * Replace Hyperlinks 28 | */ 29 | foreach (var hyperlink in _rep.HyperlinkPlaceholders) 30 | { 31 | html = html.Replace(_rep.HyperlinkPlaceholderStartTag + hyperlink.Key + _rep.HyperlinkPlaceholderEndTag, 32 | $"{hyperlink.Value.Text}"); 33 | } 34 | 35 | /* 36 | * Replace images 37 | */ 38 | foreach (var replace in _rep.ImagePlaceholders) 39 | { 40 | html = html.Replace(_rep.ImagePlaceholderStartTag + replace.Key + _rep.ImagePlaceholderEndTag, 41 | $""); 42 | } 43 | 44 | /* 45 | * Replace Tables 46 | */ 47 | foreach (var table in _rep.TablePlaceholders) //Take a Row/Table (one Dictionary) at a time 48 | { 49 | var tableCol0 = table.First(); //This is the first placeholder in the row, so the first columns. We'll need 50 | //just that one to find a matching table col 51 | // Find the first text element matching the search string - Then we will find the row - 52 | // where the text (placeholder) is inside a table cell --> this is the row we are searching for. 53 | var placeholder = _rep.TablePlaceholderStartTag + tableCol0.Key + _rep.TablePlaceholderEndTag; 54 | 55 | var regex = new Regex(""); 56 | var match = regex.Match(html); 57 | if (match.Success) //Now we have the correct table and the row containing the placeholders 58 | { 59 | string copiedRow = match.Value; 60 | int differenceInNoCharacters = 0; 61 | 62 | for (var newRow = 0; newRow < tableCol0.Value.Length; newRow++) //Lets create new row by new row and replace placeholders 63 | { 64 | for (var tableCol = 0; 65 | tableCol < table.Count; 66 | tableCol++) //Now cycle through the "columns" (keys) of the Dictionary and replace item by item 67 | { 68 | var colPlaceholder = table.ElementAt(tableCol); 69 | 70 | if (html.Contains(_rep.TablePlaceholderStartTag + colPlaceholder.Key + _rep.TablePlaceholderEndTag)) 71 | { 72 | var oldHtml = html; 73 | html = html.Replace( 74 | _rep.TablePlaceholderStartTag + colPlaceholder.Key + _rep.TablePlaceholderEndTag, 75 | colPlaceholder.Value[newRow]); 76 | differenceInNoCharacters += html.Length - oldHtml.Length; 77 | } 78 | } 79 | 80 | if (newRow < tableCol0.Value.Length - 1)//If we have not reached the end of the rows to insert, we 81 | //can insert the resulting row 82 | { 83 | html = html.Insert((match.Index + ((newRow + 1) * match.Length) + differenceInNoCharacters), copiedRow); 84 | } 85 | 86 | } 87 | 88 | html = html.Replace(_rep.NewLineTag, "
"); 89 | } 90 | } 91 | 92 | return html; 93 | } 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /DocXToPdfConverter/DocXToPdfHandlers/ImageHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | using System.IO; 5 | using DocumentFormat.OpenXml.Packaging; 6 | 7 | namespace DocXToPdfConverter.DocXToPdfHandlers 8 | { 9 | public static class ImageHandler 10 | { 11 | 12 | public static Image GetImage(this MemoryStream ms) 13 | { 14 | ms.Position = 0; 15 | var image = Image.FromStream(ms); 16 | ms.Position = 0; 17 | return image; 18 | } 19 | 20 | public static ImagePartType GetImagePartType(this MemoryStream stream) 21 | { 22 | stream.Position = 0; 23 | using (var image = Image.FromStream(stream)) 24 | { 25 | stream.Position = 0; 26 | 27 | 28 | if (ImageFormat.Jpeg.Equals(image.RawFormat)) 29 | { 30 | return ImagePartType.Jpeg; 31 | } 32 | else if (ImageFormat.Png.Equals(image.RawFormat)) 33 | { 34 | return ImagePartType.Png; 35 | } 36 | else if (ImageFormat.Gif.Equals(image.RawFormat)) 37 | { 38 | return ImagePartType.Gif; 39 | } 40 | else if (ImageFormat.Bmp.Equals(image.RawFormat)) 41 | { 42 | return ImagePartType.Bmp; 43 | } 44 | else if (ImageFormat.Tiff.Equals(image.RawFormat)) 45 | { 46 | return ImagePartType.Tiff; 47 | } 48 | 49 | return ImagePartType.Jpeg; 50 | } 51 | } 52 | 53 | public static string GetImageType(this MemoryStream stream) 54 | { 55 | stream.Position = 0; 56 | using (var image = Image.FromStream(stream)) 57 | { 58 | stream.Position = 0; 59 | 60 | if (ImageFormat.Jpeg.Equals(image.RawFormat)) 61 | { 62 | return "jpeg"; 63 | } 64 | else if (ImageFormat.Png.Equals(image.RawFormat)) 65 | { 66 | return "png"; 67 | } 68 | else if (ImageFormat.Gif.Equals(image.RawFormat)) 69 | { 70 | return "gif"; 71 | } 72 | else if (ImageFormat.Bmp.Equals(image.RawFormat)) 73 | { 74 | return "bmp"; 75 | } 76 | else if (ImageFormat.Tiff.Equals(image.RawFormat)) 77 | { 78 | return "tiff"; 79 | } 80 | 81 | return ""; 82 | } 83 | } 84 | 85 | public static string GetBase64(this MemoryStream stream) 86 | { 87 | byte[] imageBytes = stream.ToArray(); 88 | 89 | // Convert byte[] to Base64 String 90 | return Convert.ToBase64String(imageBytes); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /DocXToPdfConverter/DocXToPdfHandlers/StreamHandler.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace DocXToPdfConverter.DocXToPdfHandlers 4 | { 5 | public static class StreamHandler 6 | { 7 | public static MemoryStream GetFileAsMemoryStream(string filename) 8 | { 9 | MemoryStream ms = new MemoryStream(); 10 | using (FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read)) 11 | file.CopyTo(ms); 12 | ms.Position = 0; 13 | return ms; 14 | } 15 | 16 | public static void WriteMemoryStreamToDisk(MemoryStream ms, string filename) 17 | { 18 | ms.Position = 0; 19 | 20 | using (FileStream file = new FileStream(filename, FileMode.Create, System.IO.FileAccess.Write)) 21 | ms.CopyTo(file); 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DocXToPdfConverter/Documentation/Readme.md: -------------------------------------------------------------------------------- 1 | # Report-From-DocX-HTML-To-PDF-Converter - Create custom reports based on Word docx or HTML documents and convert to PDF with .NET CORE 2 | 3 | ## Please Donate! 4 | 5 | 6 | [![paypal](https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=GB67WF3E6JBZN) 7 | 8 | 9 | We are a company (Smart In Media GmbH & Co. KG, https://www.smartinmedia.com) and have to pay salaries to our developers. As we also believe in free software, we give away this under the __MIT license__ and you can do whatever you want with it. Other than with commercial libraries, you are not bound to making continuous paments, etc. So, if you saved money and time by using this library, please donate to us via Paypal! 10 | 11 | ## Background/Why? 12 | I was working on a medical project (https://www.easyradiology.net) and wanted that users receive a dynamically created PDF document via e-mail. Can't be too difficult, I thought. There'll be some library to do that. I discovered that in fact, there is PDF Sharp, an open source library. However, I found out that you have to "code" each line of the PDF yourself. What happens, if your sales department or user support wants to change just one line here or update a logo there in the document? Then, you have to go back to your code, change it, talk again to the sales dept or user support, if they are happy, etc until you can create a new release. This is super tedious. So I thought, would be great, if the sales department / user support can just create a simple Word docx or a HTML document with some placeholders and as a developer, you can just paste your dynamic content. 13 | Using Word docx - a Microsoft product - in .NET and adding stuff to it and then converting to PDF cannot be too difficult, right? Unfortunately, I was surprised, just how difficult that endeavour is if you don't want to pay huge amounts of money to commercial libraries. The cheapest started at U$300 and the most expensive were around U$5,000. So, I started deloping a solution myself. It is not perfect, but it seems to work... 14 | Again, if you like it, please donate money or code! 15 | 16 | ## What's the functionality? 17 | 18 | Report from DOCX / HTML to PDF Converter can parse the source document and introduce the dynamic content into predefined __placeholders__ It works on Windows (tested) and should work on Linux and MacOS. Then it can perform the following conversions: 19 | 20 | * DOCX to DOCX 21 | * DOCX to PDF 22 | * DOCX to HTML 23 | * HTML to HTML 24 | * HTML to DOCX 25 | * HTML to PDF 26 | 27 | ## What do you need to get started? 28 | Don't get scared away that I use LibreOffice, it is easier than you may think! 29 | 1. LibreOffice - just get the PORTABLE EDITION as you don't screw up your webserver with an installation. The portable version just runs without any installation. We need LibreOffice for converting from DOCX or from HTML to PDF and DOCX, etc 30 | 31 | 2. Nuget: 32 | * Microsoft.NetCore.App 33 | * Document.Format.OpenXml 34 | * System.Drawing.Common 35 | 36 | 3. The OpenXml PowerTools (thanks to Eric White for this great work), which I already included into the project, the whole code! 37 | 38 | ## Get started here! 39 | 40 | 1. Add dependencies to a) Document.Format.OpenXml (by Microsoft). For the OpenXml Powertools by Eric White (which are already included in the project), add System.Drawing.Common (for .NET Core). The regular System.Drawing won't work! 41 | 42 | 2. Download LibreOffice (https://www.libreoffice.org/download/portable-versions/). I recommend the portable edition as it does not install anything in your server. It is like unzipping files onto your harddrive. Note the path to "soffice.exe" (I don't know what the file is called in Linux / MacOS, probably just soffice. It is an executable to run a headless, mute version of LibreOffice for conversion processes). On my Windows machine, it is under: C:\PortableApps\LibreOfficePortable\App\libreoffice\program\soffice.exe. 43 | 44 | 3. Have your templates (Word docx or HTML) ready. In the repository of the project ExampleApplication, i added both, docx and HTML: Test-html-page.html and Test-Template.docx. When you run the sample application, the output will land in: 45 | \DocXToPdfConvter\ExampleApplication\bin\Debug\netcoreapp2.1 46 | 47 | ```csharp 48 | string executableLocation = Path.GetDirectoryName( 49 | Assembly.GetExecutingAssembly().Location); 50 | 51 | //Here are the 2 test files as input. They contain placeholders 52 | string docxLocation = Path.Combine(executableLocation, "Test-Template.docx"); 53 | string htmlLocation = Path.Combine(executableLocation, "Test-HTML-page.html"); 54 | ``` 55 | 56 | 4. In this repository, there are 2 projects: the library itself and the ExampleApplication. Have a look at program.cs on how to implement the library. Here are the steps: 57 | 58 | a) Add the path to your soffice.exe from LibreOffice, e. g.: 59 | 60 | ```csharp 61 | string locationOfLibreOfficeSoffice = 62 | @"C:\PortableApps\LibreOfficePortable\App\libreoffice\program\soffice.exe"; 63 | ``` 64 | 65 | 66 | b) Define your __placeholders__, which you want to use either in your Word document or in a HTML document. There are 3 types of placeholders: one for plain text, one for table rows and one for images. A placeholder consists of a start tag, a string for the placeholder and an end tag. For instance, in your Word document or HTML document, you can place "##ThisIsAPlaceholder## - then the start and end tags are "##" and the "ThisIsAPlaceholder" is the string. To define placeholders, you have to create an object of the Placeholders class. You can customize the placeholder start and end tags and the "NewLineTag" (only for docx documents - for HTML, you just use the standard <br/>). If you don't define them, the following standard placeholders will be used. A start and an end tag do not have to be the same, they can also differ. __Important__: Different placeholder types (text, table row and images) MUST have different start/end tags! 67 | 68 | So, here we first define the NewLineTag (only for Word documents) and the start/end tags. If you want to use these exactly, you don't have to define them, they are autocreated by the constructor. 69 | 70 | ```csharp 71 | var placeholders = new Placeholders(); 72 | placeholders.NewLineTag = "
"; 73 | placeholders.TextPlaceholderStartTag = "##"; 74 | placeholders.TextPlaceholderEndTag = "##"; 75 | placeholders.TablePlaceholderStartTag = "=="; 76 | placeholders.TablePlaceholderEndTag = "=="; 77 | placeholders.ImagePlaceholderStartTag = "++"; 78 | placeholders.ImagePlaceholderEndTag = "++"; 79 | 80 | ``` 81 | 82 | Now, let's make placeholders for texts: 83 | 84 | ```csharp 85 | placeholders.TextPlaceholders = new Dictionary 86 | { 87 | {"Name", "Mr. Miller" }, 88 | {"Street", "89 Brook St" }, 89 | {"City", "Brookline MA 02115
USA" }, 90 | {"InvoiceNo", "5" }, 91 | {"Total", "U$ 4,500" }, 92 | {"Date", "28 Jul 2019" } 93 | }; 94 | ``` 95 | 96 | A cool feature is creating placeholders for table rows. The rows of the template will be multiplied according to the number of placeholders in an array. In this example, we have 2 different table rows (actually, 2 different tables), which are automatically duplicated and filled: 97 | 98 | ```csharp 99 | placeholders.TablePlaceholders = new List> 100 | { 101 | 102 | new Dictionary() 103 | { 104 | {"Name", new string[]{ "Homer Simpson", "Mr. Burns", "Mr. Smithers" }}, 105 | {"Department", new string[]{ "Power Plant", "Administration", "Administration" }}, 106 | {"Responsibility", new string[]{ "Oversight", "CEO", "Assistant" }}, 107 | {"Telephone number", new string[]{ "888-234-2353", "888-295-8383", "888-848-2803" }} 108 | }, 109 | new Dictionary() 110 | { 111 | {"Qty", new string[]{ "2", "5", "7" }}, 112 | {"Product", new string[]{ "Software development", "Customization", "Travel expenses" }}, 113 | {"Price", new string[]{ "U$ 2,000", "U$ 1,000", "U$ 1,500" }}, 114 | } 115 | 116 | }; 117 | ``` 118 | 119 | 120 | Of course, you can also add images of many formats (jpg/jpeg, png is supported and a couple of others, too) into placeholders. Here is an example: 121 | 122 | 123 | ```csharp 124 | var productImage = 125 | StreamHandler.GetFileAsMemoryStream(Path.Combine(executableLocation, "ProductImage.jpg")); 126 | 127 | var qrImage = 128 | StreamHandler.GetFileAsMemoryStream(Path.Combine(executableLocation, "QRCode.PNG")); 129 | 130 | placeholders.ImagePlaceholders = new Dictionary 131 | { 132 | {"QRCode", qrImage }, 133 | {"ProductImage", productImage } 134 | }; 135 | ``` 136 | 137 | c) As we now have everything setup, we can now start the conversion process(es). While converting, the placeholders are filled with the values. Of course docx to docx and html to html aren't really conversions, but I also added them to the functionality of the library, because that may be useful for some people. 138 | 139 | ```csharp 140 | var test = new ReportGenerator(locationOfLibreOfficeSoffice); 141 | 142 | //Convert from HTML to HTML 143 | test.Convert(htmlLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-HTML-page-out.html"), placeholders); 144 | 145 | //Convert from HTML to PDF 146 | test.Convert(htmlLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-HTML-page-out.pdf"), placeholders); 147 | 148 | //Convert from HTML to DOCX 149 | test.Convert(htmlLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-HTML-page-out.docx"), placeholders); 150 | 151 | //Convert from DOCX to DOCX 152 | test.Convert(docxLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-Template-out.docx"), placeholders); 153 | 154 | //Convert from DOCX to HTML 155 | test.Convert(docxLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-Template-out.html"), placeholders); 156 | 157 | //Convert from DOCX to PDF 158 | test.Convert(docxLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-Template-out.pdf"), placeholders); 159 | 160 | ``` 161 | 162 | ## That's all folks, I hope it works well for you. Please don't forget to donate!!! 163 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/ColorParser.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Drawing; 5 | 6 | namespace OpenXmlPowerTools 7 | { 8 | public static class ColorParser 9 | { 10 | public static Color FromName(string name) 11 | { 12 | return Color.FromName(name); 13 | } 14 | 15 | public static bool TryFromName(string name, out Color color) 16 | { 17 | try 18 | { 19 | color = Color.FromName(name); 20 | 21 | return color.IsNamedColor; 22 | } 23 | catch 24 | { 25 | color = default(Color); 26 | 27 | return false; 28 | } 29 | } 30 | 31 | public static bool IsValidName(string name) 32 | { 33 | return TryFromName(name, out _); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/ComparisonUnit.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | public abstract class ComparisonUnit 12 | { 13 | private int? _descendantContentAtomsCount; 14 | 15 | public CorrelationStatus CorrelationStatus { get; set; } 16 | 17 | public List Contents { get; protected set; } 18 | 19 | public string SHA1Hash { get; protected set; } 20 | 21 | public int DescendantContentAtomsCount 22 | { 23 | get 24 | { 25 | if (_descendantContentAtomsCount != null) return (int) _descendantContentAtomsCount; 26 | 27 | _descendantContentAtomsCount = DescendantContentAtoms().Count(); 28 | return (int) _descendantContentAtomsCount; 29 | } 30 | } 31 | 32 | private IEnumerable Descendants() 33 | { 34 | var comparisonUnitList = new List(); 35 | DescendantsInternal(this, comparisonUnitList); 36 | return comparisonUnitList; 37 | } 38 | 39 | public IEnumerable DescendantContentAtoms() 40 | { 41 | return Descendants().OfType(); 42 | } 43 | 44 | private static void DescendantsInternal( 45 | ComparisonUnit comparisonUnit, 46 | List comparisonUnitList) 47 | { 48 | foreach (ComparisonUnit cu in comparisonUnit.Contents) 49 | { 50 | comparisonUnitList.Add(cu); 51 | if (cu.Contents != null && cu.Contents.Any()) 52 | DescendantsInternal(cu, comparisonUnitList); 53 | } 54 | } 55 | 56 | public abstract string ToString(int indent); 57 | 58 | internal static string ComparisonUnitListToString(ComparisonUnit[] cul) 59 | { 60 | var sb = new StringBuilder(); 61 | sb.Append("Dump Comparision Unit List To String" + Environment.NewLine); 62 | foreach (ComparisonUnit item in cul) sb.Append(item.ToString(2) + Environment.NewLine); 63 | 64 | return sb.ToString(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/ComparisonUnitAtom.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Xml.Linq; 8 | using DocumentFormat.OpenXml.Packaging; 9 | 10 | namespace OpenXmlPowerTools 11 | { 12 | public class ComparisonUnitAtom : ComparisonUnit 13 | { 14 | public ComparisonUnitAtom( 15 | XElement contentElement, 16 | XElement[] ancestorElements, 17 | OpenXmlPart part, 18 | WmlComparerSettings settings) 19 | { 20 | ContentElement = contentElement; 21 | AncestorElements = ancestorElements; 22 | Part = part; 23 | RevTrackElement = GetRevisionTrackingElementFromAncestors(contentElement, AncestorElements); 24 | 25 | if (RevTrackElement == null) 26 | { 27 | CorrelationStatus = CorrelationStatus.Equal; 28 | } 29 | else 30 | { 31 | if (RevTrackElement.Name == W.del) 32 | { 33 | CorrelationStatus = CorrelationStatus.Deleted; 34 | } 35 | else if (RevTrackElement.Name == W.ins) 36 | { 37 | CorrelationStatus = CorrelationStatus.Inserted; 38 | } 39 | } 40 | 41 | var sha1Hash = (string) contentElement.Attribute(PtOpenXml.SHA1Hash); 42 | if (sha1Hash != null) 43 | { 44 | SHA1Hash = sha1Hash; 45 | } 46 | else 47 | { 48 | string shaHashString = GetSha1HashStringForElement(ContentElement, settings); 49 | SHA1Hash = WmlComparerUtil.SHA1HashStringForUTF8String(shaHashString); 50 | } 51 | } 52 | 53 | // AncestorElements are kept in order from the body to the leaf, because this is the order in which we need to access in order 54 | // to reassemble the document. However, in many places in the code, it is necessary to find the nearest ancestor, i.e. cell 55 | // so it is necessary to reverse the order when looking for it, i.e. look from the leaf back to the body element. 56 | 57 | public XElement[] AncestorElements { get; } 58 | 59 | public XElement ContentElement { get; } 60 | 61 | public XElement RevTrackElement { get; } 62 | 63 | public string[] AncestorUnids { get; set; } 64 | 65 | public ComparisonUnitAtom ComparisonUnitAtomBefore { get; set; } 66 | 67 | public XElement ContentElementBefore { get; set; } 68 | 69 | public OpenXmlPart Part { get; } 70 | 71 | private static string GetSha1HashStringForElement(XElement contentElement, WmlComparerSettings settings) 72 | { 73 | string text = contentElement.Value; 74 | if (settings.CaseInsensitive) 75 | { 76 | text = text.ToUpper(settings.CultureInfo); 77 | } 78 | 79 | return contentElement.Name.LocalName + text; 80 | } 81 | 82 | private static XElement GetRevisionTrackingElementFromAncestors( 83 | XElement contentElement, 84 | IEnumerable ancestors) 85 | { 86 | return contentElement.Name == W.pPr 87 | ? contentElement.Elements(W.rPr).Elements().FirstOrDefault(e => e.Name == W.del || e.Name == W.ins) 88 | : ancestors.FirstOrDefault(a => a.Name == W.del || a.Name == W.ins); 89 | } 90 | 91 | public override string ToString() 92 | { 93 | return ToString(0); 94 | } 95 | 96 | public override string ToString(int indent) 97 | { 98 | const int xNamePad = 16; 99 | string indentString = "".PadRight(indent); 100 | 101 | var sb = new StringBuilder(); 102 | sb.Append(indentString); 103 | 104 | var correlationStatus = ""; 105 | if (CorrelationStatus != CorrelationStatus.Nil) 106 | { 107 | correlationStatus = $"[{CorrelationStatus.ToString().PadRight(8)}] "; 108 | } 109 | 110 | if (ContentElement.Name == W.t || ContentElement.Name == W.delText) 111 | { 112 | sb.AppendFormat( 113 | "Atom {0}: {1} {2} SHA1:{3} ", 114 | PadLocalName(xNamePad, this), 115 | ContentElement.Value, 116 | correlationStatus, 117 | SHA1Hash.Substring(0, 8)); 118 | 119 | AppendAncestorsDump(sb, this); 120 | } 121 | else 122 | { 123 | sb.AppendFormat( 124 | "Atom {0}: {1} SHA1:{2} ", 125 | PadLocalName(xNamePad, this), 126 | correlationStatus, 127 | SHA1Hash.Substring(0, 8)); 128 | 129 | AppendAncestorsDump(sb, this); 130 | } 131 | 132 | return sb.ToString(); 133 | } 134 | 135 | public string ToStringAncestorUnids() 136 | { 137 | return ToStringAncestorUnids(0); 138 | } 139 | 140 | private string ToStringAncestorUnids(int indent) 141 | { 142 | const int xNamePad = 16; 143 | string indentString = "".PadRight(indent); 144 | 145 | var sb = new StringBuilder(); 146 | sb.Append(indentString); 147 | 148 | var correlationStatus = ""; 149 | if (CorrelationStatus != CorrelationStatus.Nil) 150 | { 151 | correlationStatus = $"[{CorrelationStatus.ToString().PadRight(8)}] "; 152 | } 153 | 154 | if (ContentElement.Name == W.t || ContentElement.Name == W.delText) 155 | { 156 | sb.AppendFormat( 157 | "Atom {0}: {1} {2} SHA1:{3} ", 158 | PadLocalName(xNamePad, this), 159 | ContentElement.Value, 160 | correlationStatus, 161 | SHA1Hash.Substring(0, 8)); 162 | 163 | AppendAncestorsUnidsDump(sb, this); 164 | } 165 | else 166 | { 167 | sb.AppendFormat( 168 | "Atom {0}: {1} SHA1:{2} ", 169 | PadLocalName(xNamePad, this), 170 | correlationStatus, 171 | SHA1Hash.Substring(0, 8)); 172 | 173 | AppendAncestorsUnidsDump(sb, this); 174 | } 175 | 176 | return sb.ToString(); 177 | } 178 | 179 | private static string PadLocalName(int xNamePad, ComparisonUnitAtom item) 180 | { 181 | return (item.ContentElement.Name.LocalName + " ").PadRight(xNamePad, '-') + " "; 182 | } 183 | 184 | private static void AppendAncestorsDump(StringBuilder sb, ComparisonUnitAtom sr) 185 | { 186 | string s = sr 187 | .AncestorElements.Select(p => p.Name.LocalName + GetUnid(p) + "/") 188 | .StringConcatenate() 189 | .TrimEnd('/'); 190 | 191 | sb.Append("Ancestors:" + s); 192 | } 193 | 194 | private static void AppendAncestorsUnidsDump(StringBuilder sb, ComparisonUnitAtom sr) 195 | { 196 | var zipped = sr.AncestorElements.Zip(sr.AncestorUnids, (a, u) => new 197 | { 198 | AncestorElement = a, 199 | AncestorUnid = u 200 | }); 201 | 202 | string s = zipped 203 | .Select(p => p.AncestorElement.Name.LocalName + "[" + p.AncestorUnid.Substring(0, 8) + "]/") 204 | .StringConcatenate().TrimEnd('/'); 205 | 206 | sb.Append("Ancestors:" + s); 207 | } 208 | 209 | private static string GetUnid(XElement p) 210 | { 211 | var unid = (string) p.Attribute(PtOpenXml.Unid); 212 | return unid == null ? "" : "[" + unid.Substring(0, 8) + "]"; 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/ComparisonUnitGroup.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Xml.Linq; 9 | 10 | namespace OpenXmlPowerTools 11 | { 12 | internal class ComparisonUnitGroup : ComparisonUnit 13 | { 14 | public ComparisonUnitGroup( 15 | IEnumerable comparisonUnitList, 16 | ComparisonUnitGroupType groupType, 17 | int level) 18 | { 19 | Contents = comparisonUnitList.ToList(); 20 | ComparisonUnitGroupType = groupType; 21 | ComparisonUnit first = Contents.First(); 22 | ComparisonUnitAtom comparisonUnitAtom = GetFirstComparisonUnitAtomOfGroup(first); 23 | 24 | XElement[] ancestorsToLookAt = comparisonUnitAtom 25 | .AncestorElements 26 | .Where(e => e.Name == W.tbl || e.Name == W.tr || e.Name == W.tc || e.Name == W.p || e.Name == W.txbxContent) 27 | .ToArray(); 28 | 29 | XElement ancestor = ancestorsToLookAt[level]; 30 | if (ancestor == null) throw new OpenXmlPowerToolsException("Internal error: ComparisonUnitGroup"); 31 | 32 | SHA1Hash = (string) ancestor.Attribute(PtOpenXml.SHA1Hash); 33 | CorrelatedSHA1Hash = (string) ancestor.Attribute(PtOpenXml.CorrelatedSHA1Hash); 34 | StructureSHA1Hash = (string) ancestor.Attribute(PtOpenXml.StructureSHA1Hash); 35 | } 36 | 37 | public ComparisonUnitGroupType ComparisonUnitGroupType { get; } 38 | 39 | public string CorrelatedSHA1Hash { get; } 40 | 41 | public string StructureSHA1Hash { get; } 42 | 43 | private static ComparisonUnitAtom GetFirstComparisonUnitAtomOfGroup(ComparisonUnit group) 44 | { 45 | ComparisonUnit thisGroup = group; 46 | while (true) 47 | { 48 | if (thisGroup is ComparisonUnitGroup tg) 49 | { 50 | thisGroup = tg.Contents.First(); 51 | continue; 52 | } 53 | 54 | if (!(thisGroup is ComparisonUnitWord tw)) 55 | { 56 | throw new OpenXmlPowerToolsException("Internal error: GetFirstComparisonUnitAtomOfGroup"); 57 | } 58 | 59 | var ca = (ComparisonUnitAtom) tw.Contents.First(); 60 | return ca; 61 | } 62 | } 63 | 64 | public override string ToString(int indent) 65 | { 66 | var sb = new StringBuilder(); 67 | sb.Append("".PadRight(indent) + "Group Type: " + ComparisonUnitGroupType + " SHA1:" + SHA1Hash + Environment.NewLine); 68 | 69 | foreach (ComparisonUnit comparisonUnitAtom in Contents) 70 | { 71 | sb.Append(comparisonUnitAtom.ToString(indent + 2)); 72 | } 73 | 74 | return sb.ToString(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/ComparisonUnitGroupType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace OpenXmlPowerTools 5 | { 6 | internal enum ComparisonUnitGroupType 7 | { 8 | Paragraph, 9 | Table, 10 | Row, 11 | Cell, 12 | Textbox, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/ComparisonUnitWord.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Xml.Linq; 9 | 10 | namespace OpenXmlPowerTools 11 | { 12 | internal class ComparisonUnitWord : ComparisonUnit 13 | { 14 | public static readonly XName[] ElementsWithRelationshipIds = 15 | { 16 | A.blip, 17 | A.hlinkClick, 18 | A.relIds, 19 | C.chart, 20 | C.externalData, 21 | C.userShapes, 22 | DGM.relIds, 23 | O.OLEObject, 24 | VML.fill, 25 | VML.imagedata, 26 | VML.stroke, 27 | W.altChunk, 28 | W.attachedTemplate, 29 | W.control, 30 | W.dataSource, 31 | W.embedBold, 32 | W.embedBoldItalic, 33 | W.embedItalic, 34 | W.embedRegular, 35 | W.footerReference, 36 | W.headerReference, 37 | W.headerSource, 38 | W.hyperlink, 39 | W.printerSettings, 40 | W.recipientData, 41 | W.saveThroughXslt, 42 | W.sourceFileName, 43 | W.src, 44 | W.subDoc, 45 | WNE.toolbarData 46 | }; 47 | 48 | public static readonly XName[] RelationshipAttributeNames = 49 | { 50 | R.embed, 51 | R.link, 52 | R.id, 53 | R.cs, 54 | R.dm, 55 | R.lo, 56 | R.qs, 57 | R.href, 58 | R.pict 59 | }; 60 | 61 | public ComparisonUnitWord(IEnumerable comparisonUnitAtomList) 62 | { 63 | Contents = comparisonUnitAtomList.OfType().ToList(); 64 | string sha1String = Contents.Select(c => c.SHA1Hash).StringConcatenate(); 65 | SHA1Hash = WmlComparerUtil.SHA1HashStringForUTF8String(sha1String); 66 | } 67 | 68 | public override string ToString(int indent) 69 | { 70 | var sb = new StringBuilder(); 71 | sb.Append("".PadRight(indent) + "Word SHA1:" + SHA1Hash.Substring(0, 8) + Environment.NewLine); 72 | 73 | foreach (ComparisonUnit comparisonUnitAtom in Contents) 74 | { 75 | sb.Append(comparisonUnitAtom.ToString(indent + 2) + Environment.NewLine); 76 | } 77 | 78 | return sb.ToString(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/CorrelatedSequence.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Text; 6 | 7 | namespace OpenXmlPowerTools 8 | { 9 | internal class CorrelatedSequence 10 | { 11 | #if DEBUG 12 | public string SourceFile; 13 | public int SourceLine; 14 | #endif 15 | 16 | public CorrelatedSequence() 17 | { 18 | #if DEBUG 19 | SourceFile = new System.Diagnostics.StackTrace(true).GetFrame(1).GetFileName(); 20 | SourceLine = new System.Diagnostics.StackTrace(true).GetFrame(1).GetFileLineNumber(); 21 | #endif 22 | } 23 | 24 | public CorrelationStatus CorrelationStatus { get; set; } 25 | 26 | // if ComparisonUnitList1 == null and ComparisonUnitList2 contains sequence, then inserted content. 27 | // if ComparisonUnitList2 == null and ComparisonUnitList1 contains sequence, then deleted content. 28 | // if ComparisonUnitList2 contains sequence and ComparisonUnitList1 contains sequence, then either is Unknown or Equal. 29 | public ComparisonUnit[] ComparisonUnitArray1 { get; set; } 30 | 31 | public ComparisonUnit[] ComparisonUnitArray2 { get; set; } 32 | 33 | public override string ToString() 34 | { 35 | var sb = new StringBuilder(); 36 | const string indentString = " "; 37 | const string indentString4 = " "; 38 | sb.Append("CorrelatedSequence =====" + Environment.NewLine); 39 | #if DEBUG 40 | sb.Append(indentString + "Created at Line: " + SourceLine + Environment.NewLine); 41 | #endif 42 | sb.Append(indentString + "CorrelatedItem =====" + Environment.NewLine); 43 | sb.Append(indentString4 + "CorrelationStatus: " + CorrelationStatus + Environment.NewLine); 44 | if (CorrelationStatus == CorrelationStatus.Equal) 45 | { 46 | sb.Append(indentString4 + "ComparisonUnitList =====" + Environment.NewLine); 47 | foreach (ComparisonUnit item in ComparisonUnitArray2) 48 | sb.Append(item.ToString(6) + Environment.NewLine); 49 | } 50 | else 51 | { 52 | if (ComparisonUnitArray1 != null) 53 | { 54 | sb.Append(indentString4 + "ComparisonUnitList1 =====" + Environment.NewLine); 55 | foreach (ComparisonUnit item in ComparisonUnitArray1) 56 | sb.Append(item.ToString(6) + Environment.NewLine); 57 | } 58 | 59 | if (ComparisonUnitArray2 != null) 60 | { 61 | sb.Append(indentString4 + "ComparisonUnitList2 =====" + Environment.NewLine); 62 | foreach (ComparisonUnit item in ComparisonUnitArray2) 63 | sb.Append(item.ToString(6) + Environment.NewLine); 64 | } 65 | } 66 | 67 | return sb.ToString(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/CorrelationStatus.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace OpenXmlPowerTools 5 | { 6 | public enum CorrelationStatus 7 | { 8 | Nil, 9 | Normal, 10 | Unknown, 11 | Inserted, 12 | Deleted, 13 | Equal, 14 | Group, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/PartSHA1HashAnnotation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace OpenXmlPowerTools 5 | { 6 | internal class PartSHA1HashAnnotation 7 | { 8 | public string Hash; 9 | 10 | public PartSHA1HashAnnotation(string hash) 11 | { 12 | Hash = hash; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WithHierarchicalGroupingKey.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace OpenXmlPowerTools 5 | { 6 | internal class WithHierarchicalGroupingKey 7 | { 8 | public string[] HierarchicalGroupingArray { get; set; } 9 | 10 | public ComparisonUnitWord ComparisonUnitWord { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparer.Private.Fields.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml.Linq; 6 | 7 | namespace OpenXmlPowerTools 8 | { 9 | public static partial class WmlComparer 10 | { 11 | #pragma warning disable 414 12 | private static readonly bool False = false; 13 | private static readonly bool True = true; 14 | private static readonly bool SaveIntermediateFilesForDebugging = false; 15 | #pragma warning restore 414 16 | 17 | private static readonly string NewLine = Environment.NewLine; 18 | 19 | private static readonly XAttribute[] NamespaceAttributes = 20 | { 21 | new XAttribute(XNamespace.Xmlns + "wpc", WPC.wpc), 22 | new XAttribute(XNamespace.Xmlns + "mc", MC.mc), 23 | new XAttribute(XNamespace.Xmlns + "o", O.o), 24 | new XAttribute(XNamespace.Xmlns + "r", R.r), 25 | new XAttribute(XNamespace.Xmlns + "m", M.m), 26 | new XAttribute(XNamespace.Xmlns + "v", VML.vml), 27 | new XAttribute(XNamespace.Xmlns + "wp14", WP14.wp14), 28 | new XAttribute(XNamespace.Xmlns + "wp", WP.wp), 29 | new XAttribute(XNamespace.Xmlns + "w10", W10.w10), 30 | new XAttribute(XNamespace.Xmlns + "w", W.w), 31 | new XAttribute(XNamespace.Xmlns + "w14", W14.w14), 32 | new XAttribute(XNamespace.Xmlns + "wpg", WPG.wpg), 33 | new XAttribute(XNamespace.Xmlns + "wpi", WPI.wpi), 34 | new XAttribute(XNamespace.Xmlns + "wne", WNE.wne), 35 | new XAttribute(XNamespace.Xmlns + "wps", WPS.wps), 36 | new XAttribute(MC.Ignorable, "w14 wp14") 37 | }; 38 | 39 | private static readonly XName[] RevElementsWithNoText = 40 | { 41 | M.oMath, 42 | M.oMathPara, 43 | W.drawing 44 | }; 45 | 46 | private static readonly XName[] AttributesToTrimWhenCloning = 47 | { 48 | WP14.anchorId, 49 | WP14.editId, 50 | "ObjectID", 51 | "ShapeID", 52 | "id", 53 | "type" 54 | }; 55 | 56 | private static int _maxId; 57 | 58 | private static readonly XName[] WordBreakElements = 59 | { 60 | W.pPr, 61 | W.tab, 62 | W.br, 63 | W.continuationSeparator, 64 | W.cr, 65 | W.dayLong, 66 | W.dayShort, 67 | W.drawing, 68 | W.pict, 69 | W.endnoteRef, 70 | W.footnoteRef, 71 | W.monthLong, 72 | W.monthShort, 73 | W.noBreakHyphen, 74 | W._object, 75 | W.ptab, 76 | W.separator, 77 | W.sym, 78 | W.yearLong, 79 | W.yearShort, 80 | M.oMathPara, 81 | M.oMath, 82 | W.footnoteReference, 83 | W.endnoteReference 84 | }; 85 | 86 | private static readonly XName[] AllowableRunChildren = 87 | { 88 | W.br, 89 | W.drawing, 90 | W.cr, 91 | W.dayLong, 92 | W.dayShort, 93 | W.footnoteReference, 94 | W.endnoteReference, 95 | W.monthLong, 96 | W.monthShort, 97 | W.noBreakHyphen, 98 | 99 | //W._object, 100 | W.pgNum, 101 | W.ptab, 102 | W.softHyphen, 103 | W.sym, 104 | W.tab, 105 | W.yearLong, 106 | W.yearShort, 107 | M.oMathPara, 108 | M.oMath, 109 | W.fldChar, 110 | W.instrText 111 | }; 112 | 113 | private static readonly XName[] ElementsToThrowAway = 114 | { 115 | W.bookmarkStart, 116 | W.bookmarkEnd, 117 | W.commentRangeStart, 118 | W.commentRangeEnd, 119 | W.lastRenderedPageBreak, 120 | W.proofErr, 121 | W.tblPr, 122 | W.sectPr, 123 | W.permEnd, 124 | W.permStart, 125 | W.footnoteRef, 126 | W.endnoteRef, 127 | W.separator, 128 | W.continuationSeparator 129 | }; 130 | 131 | private static readonly XName[] ElementsToHaveSha1Hash = 132 | { 133 | W.p, 134 | W.tbl, 135 | W.tr, 136 | W.tc, 137 | W.drawing, 138 | W.pict, 139 | W.txbxContent 140 | }; 141 | 142 | private static readonly XName[] InvalidElements = 143 | { 144 | W.altChunk, 145 | W.customXml, 146 | W.customXmlDelRangeEnd, 147 | W.customXmlDelRangeStart, 148 | W.customXmlInsRangeEnd, 149 | W.customXmlInsRangeStart, 150 | W.customXmlMoveFromRangeEnd, 151 | W.customXmlMoveFromRangeStart, 152 | W.customXmlMoveToRangeEnd, 153 | W.customXmlMoveToRangeStart, 154 | W.moveFrom, 155 | W.moveFromRangeStart, 156 | W.moveFromRangeEnd, 157 | W.moveTo, 158 | W.moveToRangeStart, 159 | W.moveToRangeEnd, 160 | W.subDoc 161 | }; 162 | 163 | private static readonly RecursionInfo[] RecursionElements = 164 | { 165 | new RecursionInfo 166 | { 167 | ElementName = W.del, 168 | ChildElementPropertyNames = null 169 | }, 170 | new RecursionInfo 171 | { 172 | ElementName = W.ins, 173 | ChildElementPropertyNames = null 174 | }, 175 | new RecursionInfo 176 | { 177 | ElementName = W.tbl, 178 | ChildElementPropertyNames = new[] { W.tblPr, W.tblGrid, W.tblPrEx } 179 | }, 180 | new RecursionInfo 181 | { 182 | ElementName = W.tr, 183 | ChildElementPropertyNames = new[] { W.trPr, W.tblPrEx } 184 | }, 185 | new RecursionInfo 186 | { 187 | ElementName = W.tc, 188 | ChildElementPropertyNames = new[] { W.tcPr, W.tblPrEx } 189 | }, 190 | new RecursionInfo 191 | { 192 | ElementName = W.pict, 193 | ChildElementPropertyNames = new[] { VML.shapetype } 194 | }, 195 | new RecursionInfo 196 | { 197 | ElementName = VML.group, 198 | ChildElementPropertyNames = null 199 | }, 200 | new RecursionInfo 201 | { 202 | ElementName = VML.shape, 203 | ChildElementPropertyNames = null 204 | }, 205 | new RecursionInfo 206 | { 207 | ElementName = VML.rect, 208 | ChildElementPropertyNames = null 209 | }, 210 | new RecursionInfo 211 | { 212 | ElementName = VML.textbox, 213 | ChildElementPropertyNames = null 214 | }, 215 | new RecursionInfo 216 | { 217 | ElementName = O._lock, 218 | ChildElementPropertyNames = null 219 | }, 220 | new RecursionInfo 221 | { 222 | ElementName = W.txbxContent, 223 | ChildElementPropertyNames = null 224 | }, 225 | new RecursionInfo 226 | { 227 | ElementName = W10.wrap, 228 | ChildElementPropertyNames = null 229 | }, 230 | new RecursionInfo 231 | { 232 | ElementName = W.sdt, 233 | ChildElementPropertyNames = new[] { W.sdtPr, W.sdtEndPr } 234 | }, 235 | new RecursionInfo 236 | { 237 | ElementName = W.sdtContent, 238 | ChildElementPropertyNames = null 239 | }, 240 | new RecursionInfo 241 | { 242 | ElementName = W.hyperlink, 243 | ChildElementPropertyNames = null 244 | }, 245 | new RecursionInfo 246 | { 247 | ElementName = W.fldSimple, 248 | ChildElementPropertyNames = null 249 | }, 250 | new RecursionInfo 251 | { 252 | ElementName = VML.shapetype, 253 | ChildElementPropertyNames = null 254 | }, 255 | new RecursionInfo 256 | { 257 | ElementName = W.smartTag, 258 | ChildElementPropertyNames = new[] { W.smartTagPr } 259 | }, 260 | new RecursionInfo 261 | { 262 | ElementName = W.ruby, 263 | ChildElementPropertyNames = new[] { W.rubyPr } 264 | } 265 | }; 266 | 267 | private static readonly XName[] ComparisonGroupingElements = 268 | { 269 | W.p, 270 | W.tbl, 271 | W.tr, 272 | W.tc, 273 | W.txbxContent 274 | }; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparer.Private.Methods.Util.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.IO.Packaging; 8 | using System.Linq; 9 | using System.Xml.Linq; 10 | 11 | namespace OpenXmlPowerTools 12 | { 13 | public static partial class WmlComparer 14 | { 15 | private static XElement MoveRelatedPartsToDestination( 16 | PackagePart partOfDeletedContent, 17 | PackagePart partInNewDocument, 18 | XElement contentElement) 19 | { 20 | List elementsToUpdate = contentElement 21 | .Descendants() 22 | .Where(d => d.Attributes().Any(a => ComparisonUnitWord.RelationshipAttributeNames.Contains(a.Name))) 23 | .ToList(); 24 | 25 | foreach (XElement element in elementsToUpdate) 26 | { 27 | List attributesToUpdate = element 28 | .Attributes() 29 | .Where(a => ComparisonUnitWord.RelationshipAttributeNames.Contains(a.Name)) 30 | .ToList(); 31 | 32 | foreach (XAttribute att in attributesToUpdate) 33 | { 34 | var rId = (string) att; 35 | 36 | PackageRelationship relationshipForDeletedPart = partOfDeletedContent.GetRelationship(rId); 37 | 38 | Uri targetUri = PackUriHelper 39 | .ResolvePartUri( 40 | new Uri(partOfDeletedContent.Uri.ToString(), UriKind.Relative), 41 | relationshipForDeletedPart.TargetUri); 42 | 43 | PackagePart relatedPackagePart = partOfDeletedContent.Package.GetPart(targetUri); 44 | string[] uriSplit = relatedPackagePart.Uri.ToString().Split('/'); 45 | string[] last = uriSplit[uriSplit.Length - 1].Split('.'); 46 | string uriString; 47 | if (last.Length == 2) 48 | { 49 | uriString = uriSplit.SkipLast(1).Select(p => p + "/").StringConcatenate() + 50 | "P" + Guid.NewGuid().ToString().Replace("-", "") + "." + last[1]; 51 | } 52 | else 53 | { 54 | uriString = uriSplit.SkipLast(1).Select(p => p + "/").StringConcatenate() + 55 | "P" + Guid.NewGuid().ToString().Replace("-", ""); 56 | } 57 | 58 | Uri uri = relatedPackagePart.Uri.IsAbsoluteUri 59 | ? new Uri(uriString, UriKind.Absolute) 60 | : new Uri(uriString, UriKind.Relative); 61 | 62 | PackagePart newPart = partInNewDocument.Package.CreatePart(uri, relatedPackagePart.ContentType); 63 | 64 | // ReSharper disable once PossibleNullReferenceException 65 | using (Stream oldPartStream = relatedPackagePart.GetStream()) 66 | using (Stream newPartStream = newPart.GetStream()) 67 | { 68 | FileUtils.CopyStream(oldPartStream, newPartStream); 69 | } 70 | 71 | string newRid = "R" + Guid.NewGuid().ToString().Replace("-", ""); 72 | partInNewDocument.CreateRelationship(newPart.Uri, TargetMode.Internal, 73 | relationshipForDeletedPart.RelationshipType, newRid); 74 | att.Value = newRid; 75 | 76 | if (newPart.ContentType.EndsWith("xml")) 77 | { 78 | XDocument newPartXDoc; 79 | using (Stream stream = newPart.GetStream()) 80 | { 81 | newPartXDoc = XDocument.Load(stream); 82 | MoveRelatedPartsToDestination(relatedPackagePart, newPart, newPartXDoc.Root); 83 | } 84 | 85 | using (Stream stream = newPart.GetStream()) 86 | newPartXDoc.Save(stream); 87 | } 88 | } 89 | } 90 | 91 | return contentElement; 92 | } 93 | 94 | private static XAttribute GetXmlSpaceAttribute(string textOfTextElement) 95 | { 96 | if (char.IsWhiteSpace(textOfTextElement[0]) || 97 | char.IsWhiteSpace(textOfTextElement[textOfTextElement.Length - 1])) 98 | return new XAttribute(XNamespace.Xml + "space", "preserve"); 99 | 100 | return null; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparer.Private.NestedTypes.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Drawing; 5 | using System.Xml.Linq; 6 | 7 | namespace OpenXmlPowerTools 8 | { 9 | public static partial class WmlComparer 10 | { 11 | private class Atgbw 12 | { 13 | public int? Key; 14 | public ComparisonUnitAtom ComparisonUnitAtomMember; 15 | public int NextIndex; 16 | } 17 | 18 | private class ConsolidationInfo 19 | { 20 | public string Revisor; 21 | public Color Color; 22 | public XElement RevisionElement; 23 | public bool InsertBefore; 24 | public string RevisionHash; 25 | public XElement[] Footnotes; 26 | public XElement[] Endnotes; 27 | public string RevisionString; // for debugging purposes only 28 | } 29 | 30 | private class RecursionInfo 31 | { 32 | public XName ElementName; 33 | public XName[] ChildElementPropertyNames; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparer.Public.Methods.GetRevisions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Xml.Linq; 10 | using DocumentFormat.OpenXml.Packaging; 11 | 12 | namespace OpenXmlPowerTools 13 | { 14 | public static partial class WmlComparer 15 | { 16 | // the following gets a flattened list of ComparisonUnitAtoms, with status indicated in each ComparisonUnitAtom: Deleted, Inserted, or Equal 17 | 18 | // for any deleted or inserted rows, we go into the w:trPr properties, and add the appropriate w:ins or w:del element, and therefore 19 | // when generating the document, the appropriate row will be marked as deleted or inserted. 20 | 21 | public static List GetRevisions(WmlDocument source, WmlComparerSettings settings) 22 | { 23 | using (var ms = new MemoryStream()) 24 | { 25 | ms.Write(source.DocumentByteArray, 0, source.DocumentByteArray.Length); 26 | using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) 27 | { 28 | TestForInvalidContent(wDoc); 29 | RemoveExistingPowerToolsMarkup(wDoc); 30 | 31 | XElement contentParent = wDoc.MainDocumentPart.GetXDocument().Root?.Element(W.body); 32 | ComparisonUnitAtom[] atomList = 33 | CreateComparisonUnitAtomList(wDoc.MainDocumentPart, contentParent, settings).ToArray(); 34 | 35 | if (False) 36 | { 37 | var sb = new StringBuilder(); 38 | foreach (ComparisonUnitAtom item in atomList) 39 | sb.Append(item + Environment.NewLine); 40 | string sbs = sb.ToString(); 41 | TestUtil.NotePad(sbs); 42 | } 43 | 44 | List> grouped = atomList 45 | .GroupAdjacent(a => 46 | { 47 | string key = a.CorrelationStatus.ToString(); 48 | if (a.CorrelationStatus != CorrelationStatus.Equal) 49 | { 50 | var rt = new XElement(a.RevTrackElement.Name, 51 | new XAttribute(XNamespace.Xmlns + "w", 52 | "http://schemas.openxmlformats.org/wordprocessingml/2006/main"), 53 | a.RevTrackElement.Attributes().Where(a2 => a2.Name != W.id && a2.Name != PtOpenXml.Unid)); 54 | key += rt.ToString(SaveOptions.DisableFormatting); 55 | } 56 | 57 | return key; 58 | }) 59 | .ToList(); 60 | 61 | List> revisions = grouped 62 | .Where(k => k.Key != "Equal") 63 | .ToList(); 64 | 65 | if (False) 66 | { 67 | var sb = new StringBuilder(); 68 | foreach (IGrouping item in revisions) 69 | { 70 | sb.Append(item.Key + Environment.NewLine); 71 | } 72 | 73 | string sbs = sb.ToString(); 74 | TestUtil.NotePad(sbs); 75 | } 76 | 77 | List mainDocPartRevisionList = revisions 78 | .Select(rg => 79 | { 80 | var rev = new WmlComparerRevision(); 81 | if (rg.Key.StartsWith("Inserted")) 82 | { 83 | rev.RevisionType = WmlComparerRevisionType.Inserted; 84 | } 85 | else if (rg.Key.StartsWith("Deleted")) 86 | { 87 | rev.RevisionType = WmlComparerRevisionType.Deleted; 88 | } 89 | 90 | XElement revTrackElement = rg.First().RevTrackElement; 91 | rev.RevisionXElement = revTrackElement; 92 | rev.Author = (string) revTrackElement.Attribute(W.author); 93 | rev.ContentXElement = rg.First().ContentElement; 94 | rev.Date = (string) revTrackElement.Attribute(W.date); 95 | rev.PartUri = wDoc.MainDocumentPart.Uri; 96 | rev.PartContentType = wDoc.MainDocumentPart.ContentType; 97 | 98 | if (!RevElementsWithNoText.Contains(rev.ContentXElement.Name)) 99 | { 100 | rev.Text = rg 101 | .Select(rgc => rgc.ContentElement.Name == W.pPr ? NewLine : rgc.ContentElement.Value) 102 | .StringConcatenate(); 103 | } 104 | 105 | return rev; 106 | }) 107 | .ToList(); 108 | 109 | IEnumerable footnotesRevisionList = 110 | GetFootnoteEndnoteRevisionList(wDoc.MainDocumentPart.FootnotesPart, W.footnote, settings); 111 | IEnumerable endnotesRevisionList = 112 | GetFootnoteEndnoteRevisionList(wDoc.MainDocumentPart.EndnotesPart, W.endnote, settings); 113 | 114 | List finalRevisionList = mainDocPartRevisionList 115 | .Concat(footnotesRevisionList) 116 | .Concat(endnotesRevisionList) 117 | .ToList(); 118 | 119 | return finalRevisionList; 120 | } 121 | } 122 | } 123 | 124 | private static IEnumerable GetFootnoteEndnoteRevisionList( 125 | OpenXmlPart footnotesEndnotesPart, 126 | XName footnoteEndnoteElementName, 127 | WmlComparerSettings settings) 128 | { 129 | if (footnotesEndnotesPart == null) 130 | { 131 | return Enumerable.Empty(); 132 | } 133 | 134 | XDocument xDoc = footnotesEndnotesPart.GetXDocument(); 135 | IEnumerable footnotesEndnotes = 136 | xDoc.Root?.Elements(footnoteEndnoteElementName) ?? throw new OpenXmlPowerToolsException("Invalid document."); 137 | 138 | var revisionsForPart = new List(); 139 | foreach (XElement fn in footnotesEndnotes) 140 | { 141 | ComparisonUnitAtom[] atomList = CreateComparisonUnitAtomList(footnotesEndnotesPart, fn, settings).ToArray(); 142 | 143 | if (False) 144 | { 145 | var sb = new StringBuilder(); 146 | foreach (ComparisonUnitAtom item in atomList) 147 | { 148 | sb.Append(item + Environment.NewLine); 149 | } 150 | 151 | string sbs = sb.ToString(); 152 | TestUtil.NotePad(sbs); 153 | } 154 | 155 | List> grouped = atomList 156 | .GroupAdjacent(a => 157 | { 158 | string key = a.CorrelationStatus.ToString(); 159 | if (a.CorrelationStatus != CorrelationStatus.Equal) 160 | { 161 | var rt = new XElement(a.RevTrackElement.Name, 162 | new XAttribute(XNamespace.Xmlns + "w", 163 | "http://schemas.openxmlformats.org/wordprocessingml/2006/main"), 164 | a.RevTrackElement.Attributes().Where(a2 => a2.Name != W.id && a2.Name != PtOpenXml.Unid)); 165 | 166 | key += rt.ToString(SaveOptions.DisableFormatting); 167 | } 168 | 169 | return key; 170 | }) 171 | .ToList(); 172 | 173 | List> revisions = grouped 174 | .Where(k => k.Key != "Equal") 175 | .ToList(); 176 | 177 | IEnumerable thisNoteRevisionList = revisions 178 | .Select(rg => 179 | { 180 | var rev = new WmlComparerRevision(); 181 | if (rg.Key.StartsWith("Inserted")) 182 | { 183 | rev.RevisionType = WmlComparerRevisionType.Inserted; 184 | } 185 | else if (rg.Key.StartsWith("Deleted")) 186 | { 187 | rev.RevisionType = WmlComparerRevisionType.Deleted; 188 | } 189 | 190 | XElement revTrackElement = rg.First().RevTrackElement; 191 | rev.RevisionXElement = revTrackElement; 192 | rev.Author = (string) revTrackElement.Attribute(W.author); 193 | rev.ContentXElement = rg.First().ContentElement; 194 | rev.Date = (string) revTrackElement.Attribute(W.date); 195 | rev.PartUri = footnotesEndnotesPart.Uri; 196 | rev.PartContentType = footnotesEndnotesPart.ContentType; 197 | 198 | if (!RevElementsWithNoText.Contains(rev.ContentXElement.Name)) 199 | { 200 | rev.Text = rg 201 | .Select(rgc => rgc.ContentElement.Name == W.pPr ? NewLine : rgc.ContentElement.Value) 202 | .StringConcatenate(); 203 | } 204 | 205 | return rev; 206 | }); 207 | 208 | revisionsForPart.AddRange(thisNoteRevisionList); 209 | } 210 | 211 | return revisionsForPart; 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparer.Public.NestedTypes.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml.Linq; 6 | 7 | namespace OpenXmlPowerTools 8 | { 9 | public static partial class WmlComparer 10 | { 11 | public class WmlComparerRevision 12 | { 13 | public WmlComparerRevisionType RevisionType; 14 | public string Text; 15 | public string Author; 16 | public string Date; 17 | public XElement ContentXElement; 18 | public XElement RevisionXElement; 19 | public Uri PartUri; 20 | public string PartContentType; 21 | } 22 | 23 | public enum WmlComparerRevisionType 24 | { 25 | Inserted, 26 | Deleted 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparerConsolidateSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace OpenXmlPowerTools 5 | { 6 | public class WmlComparerConsolidateSettings 7 | { 8 | public bool ConsolidateWithTable = true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml.Linq; 6 | using DocumentFormat.OpenXml.Packaging; 7 | 8 | namespace OpenXmlPowerTools 9 | { 10 | public static class WmlComparerExtensions 11 | { 12 | public static XElement GetMainDocumentBody(this WordprocessingDocument wordDocument) 13 | { 14 | return wordDocument.GetMainDocumentRoot().Element(W.body) ?? throw new ArgumentException("Invalid document."); 15 | } 16 | 17 | public static XElement GetMainDocumentRoot(this WordprocessingDocument wordDocument) 18 | { 19 | return wordDocument.MainDocumentPart?.GetXElement() ?? throw new ArgumentException("Invalid document."); 20 | } 21 | 22 | public static XElement GetXElement(this OpenXmlPart part) 23 | { 24 | return part.GetXDocument()?.Root ?? throw new ArgumentException("Invalid document."); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparerSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Globalization; 6 | using System.IO; 7 | 8 | namespace OpenXmlPowerTools 9 | { 10 | public class WmlComparerSettings 11 | { 12 | public char[] WordSeparators; 13 | public string AuthorForRevisions = "Open-Xml-PowerTools"; 14 | public string DateTimeForRevisions = DateTime.Now.ToString("o"); 15 | public double DetailThreshold = 0.15; 16 | public bool CaseInsensitive = false; 17 | public CultureInfo CultureInfo = null; 18 | public Action LogCallback = null; 19 | public int StartingIdForFootnotesEndnotes = 1; 20 | 21 | public DirectoryInfo DebugTempFileDi; 22 | 23 | public WmlComparerSettings() 24 | { 25 | // note that , and . are processed explicitly to handle cases where they are in a number or word 26 | WordSeparators = new[] { ' ', '-', ')', '(', ';', ',' }; // todo need to fix this for complete list 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlComparerUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | namespace OpenXmlPowerTools 9 | { 10 | internal static class WmlComparerUtil 11 | { 12 | public static string SHA1HashStringForUTF8String(string s) 13 | { 14 | var bytes = Encoding.UTF8.GetBytes(s); 15 | var sha1 = SHA1.Create(); 16 | var hashBytes = sha1.ComputeHash(bytes); 17 | return HexStringFromBytes(hashBytes); 18 | } 19 | 20 | public static string SHA1HashStringForByteArray(byte[] bytes) 21 | { 22 | var sha1 = SHA1.Create(); 23 | var hashBytes = sha1.ComputeHash(bytes); 24 | return HexStringFromBytes(hashBytes); 25 | } 26 | 27 | public static string HexStringFromBytes(byte[] bytes) 28 | { 29 | var sb = new StringBuilder(); 30 | foreach (var b in bytes) 31 | { 32 | var hex = b.ToString("x2"); 33 | sb.Append(hex); 34 | } 35 | 36 | return sb.ToString(); 37 | } 38 | 39 | public static ComparisonUnitGroupType ComparisonUnitGroupTypeFromLocalName(string localName) 40 | { 41 | switch (localName) 42 | { 43 | case "p": 44 | return ComparisonUnitGroupType.Paragraph; 45 | case "tbl": 46 | return ComparisonUnitGroupType.Table; 47 | case "tr": 48 | return ComparisonUnitGroupType.Row; 49 | case "tc": 50 | return ComparisonUnitGroupType.Cell; 51 | case "txbxContent": 52 | return ComparisonUnitGroupType.Textbox; 53 | default: 54 | throw new ArgumentOutOfRangeException(nameof(localName), 55 | $@"Unsupported localName: '{localName}'."); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Comparer/WmlRevisedDocumentInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Drawing; 5 | 6 | namespace OpenXmlPowerTools 7 | { 8 | public class WmlRevisedDocumentInfo 9 | { 10 | public WmlDocument RevisedDocument; 11 | public string Revisor; 12 | public Color Color; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/GetListItemText_Default.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | class ListItemTextGetter_Default 12 | { 13 | private static string[] RomanOnes = 14 | { 15 | "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" 16 | }; 17 | 18 | private static string[] RomanTens = 19 | { 20 | "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" 21 | }; 22 | 23 | private static string[] RomanHundreds = 24 | { 25 | "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M" 26 | }; 27 | 28 | private static string[] RomanThousands = 29 | { 30 | "", "M", "MM", "MMM", "MMMM", "MMMMM", "MMMMMM", "MMMMMMM", "MMMMMMMM", 31 | "MMMMMMMMM", "MMMMMMMMMM" 32 | }; 33 | 34 | private static string[] OneThroughNineteen = { 35 | "one", "two", "three", "four", "five", "six", "seven", "eight", 36 | "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", 37 | "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" 38 | }; 39 | 40 | private static string[] Tens = { 41 | "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", 42 | "eighty", "ninety" 43 | }; 44 | 45 | private static string[] OrdinalOneThroughNineteen = { 46 | "first", "second", "third", "fourth", "fifth", "sixth", 47 | "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", 48 | "thirteenth", "fourteenth", "fifteenth", "sixteenth", 49 | "seventeenth", "eighteenth", "nineteenth" 50 | }; 51 | 52 | private static string[] OrdinalTenths = { 53 | "tenth", "twentieth", "thirtieth", "fortieth", "fiftieth", 54 | "sixtieth", "seventieth", "eightieth", "ninetieth" 55 | }; 56 | 57 | public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) 58 | { 59 | if (numFmt == "none") 60 | { 61 | return ""; 62 | } 63 | if (numFmt == "decimal") 64 | { 65 | return levelNumber.ToString(); 66 | } 67 | if (numFmt == "decimalZero") 68 | { 69 | if (levelNumber <= 9) 70 | return "0" + levelNumber.ToString(); 71 | else 72 | return levelNumber.ToString(); 73 | } 74 | if (numFmt == "upperRoman") 75 | { 76 | int ones = levelNumber % 10; 77 | int tens = (levelNumber % 100) / 10; 78 | int hundreds = (levelNumber % 1000) / 100; 79 | int thousands = levelNumber / 1000; 80 | return RomanThousands[thousands] + RomanHundreds[hundreds] + 81 | RomanTens[tens] + RomanOnes[ones]; 82 | } 83 | if (numFmt == "lowerRoman") 84 | { 85 | int ones = levelNumber % 10; 86 | int tens = (levelNumber % 100) / 10; 87 | int hundreds = (levelNumber % 1000) / 100; 88 | int thousands = levelNumber / 1000; 89 | return (RomanThousands[thousands] + RomanHundreds[hundreds] + 90 | RomanTens[tens] + RomanOnes[ones]).ToLower(); 91 | } 92 | if (numFmt == "upperLetter") 93 | { 94 | int levelNumber2 = levelNumber % 780; 95 | if (levelNumber2 == 0) 96 | levelNumber2 = 780; 97 | string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 98 | int c = (levelNumber2 - 1) / 26; 99 | int n = (levelNumber2 - 1) % 26; 100 | char x = a[n]; 101 | return "".PadRight(c + 1, x); 102 | } 103 | if (numFmt == "lowerLetter") 104 | { 105 | int levelNumber3 = levelNumber % 780; 106 | if (levelNumber3 == 0) 107 | levelNumber3 = 780; 108 | string a = "abcdefghijklmnopqrstuvwxyz"; 109 | int c = (levelNumber3 - 1) / 26; 110 | int n = (levelNumber3 - 1) % 26; 111 | char x = a[n]; 112 | return "".PadRight(c + 1, x); 113 | } 114 | if (numFmt == "ordinal") 115 | { 116 | string suffix; 117 | if (levelNumber % 100 == 11 || levelNumber % 100 == 12 || 118 | levelNumber % 100 == 13) 119 | suffix = "th"; 120 | else if (levelNumber % 10 == 1) 121 | suffix = "st"; 122 | else if (levelNumber % 10 == 2) 123 | suffix = "nd"; 124 | else if (levelNumber % 10 == 3) 125 | suffix = "rd"; 126 | else 127 | suffix = "th"; 128 | return levelNumber.ToString() + suffix; 129 | } 130 | if (numFmt == "cardinalText") 131 | { 132 | string result = ""; 133 | int t1 = levelNumber / 1000; 134 | int t2 = levelNumber % 1000; 135 | if (t1 >= 1) 136 | result += OneThroughNineteen[t1 - 1] + " thousand"; 137 | if (t1 >= 1 && t2 == 0) 138 | return result.Substring(0, 1).ToUpper() + 139 | result.Substring(1); 140 | if (t1 >= 1) 141 | result += " "; 142 | int h1 = (levelNumber % 1000) / 100; 143 | int h2 = levelNumber % 100; 144 | if (h1 >= 1) 145 | result += OneThroughNineteen[h1 - 1] + " hundred"; 146 | if (h1 >= 1 && h2 == 0) 147 | return result.Substring(0, 1).ToUpper() + 148 | result.Substring(1); 149 | if (h1 >= 1) 150 | result += " "; 151 | int z = levelNumber % 100; 152 | if (z <= 19) 153 | result += OneThroughNineteen[z - 1]; 154 | else 155 | { 156 | int x = z / 10; 157 | int r = z % 10; 158 | result += Tens[x - 1]; 159 | if (r >= 1) 160 | result += "-" + OneThroughNineteen[r - 1]; 161 | } 162 | return result.Substring(0, 1).ToUpper() + 163 | result.Substring(1); 164 | } 165 | if (numFmt == "ordinalText") 166 | { 167 | string result = ""; 168 | int t1 = levelNumber / 1000; 169 | int t2 = levelNumber % 1000; 170 | if (t1 >= 1 && t2 != 0) 171 | result += OneThroughNineteen[t1 - 1] + " thousand"; 172 | if (t1 >= 1 && t2 == 0) 173 | { 174 | result += OneThroughNineteen[t1 - 1] + " thousandth"; 175 | return result.Substring(0, 1).ToUpper() + 176 | result.Substring(1); 177 | } 178 | if (t1 >= 1) 179 | result += " "; 180 | int h1 = (levelNumber % 1000) / 100; 181 | int h2 = levelNumber % 100; 182 | if (h1 >= 1 && h2 != 0) 183 | result += OneThroughNineteen[h1 - 1] + " hundred"; 184 | if (h1 >= 1 && h2 == 0) 185 | { 186 | result += OneThroughNineteen[h1 - 1] + " hundredth"; 187 | return result.Substring(0, 1).ToUpper() + 188 | result.Substring(1); 189 | } 190 | if (h1 >= 1) 191 | result += " "; 192 | int z = levelNumber % 100; 193 | if (z <= 19) 194 | result += OrdinalOneThroughNineteen[z - 1]; 195 | else 196 | { 197 | int x = z / 10; 198 | int r = z % 10; 199 | if (r == 0) 200 | result += OrdinalTenths[x - 1]; 201 | else 202 | result += Tens[x - 1]; 203 | if (r >= 1) 204 | result += "-" + OrdinalOneThroughNineteen[r - 1]; 205 | } 206 | return result.Substring(0, 1).ToUpper() + 207 | result.Substring(1); 208 | } 209 | if (numFmt == "01, 02, 03, ...") 210 | { 211 | return string.Format("{0:00}", levelNumber); 212 | } 213 | if (numFmt == "001, 002, 003, ...") 214 | { 215 | return string.Format("{0:000}", levelNumber); 216 | } 217 | if (numFmt == "0001, 0002, 0003, ...") 218 | { 219 | return string.Format("{0:0000}", levelNumber); 220 | } 221 | if (numFmt == "00001, 00002, 00003, ...") 222 | { 223 | return string.Format("{0:00000}", levelNumber); 224 | } 225 | if (numFmt == "bullet") 226 | return ""; 227 | if (numFmt == "decimalEnclosedCircle") 228 | { 229 | if (levelNumber >= 1 && levelNumber <= 20) 230 | { 231 | // 9311 + levelNumber 232 | var s = new string(new[] { (char)(9311 + levelNumber) }); 233 | return s; 234 | } 235 | return levelNumber.ToString(); 236 | } 237 | return levelNumber.ToString(); 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/GetListItemText_fr_FR.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | public class ListItemTextGetter_fr_FR 12 | { 13 | private static string[] OneThroughNineteen = { 14 | "", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", 15 | "neuf", "dix", "onze", "douze", "treize", "quatorze", 16 | "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf" 17 | }; 18 | 19 | private static string[] Tens = { 20 | "", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante-dix", 21 | "quatre-vingt", "quatre-vingt-dix" 22 | }; 23 | 24 | private static string[] OrdinalOneThroughNineteen = { 25 | "", "unième", "deuxième", "troisième", "quatrième", "cinquième", "sixième", 26 | "septième", "huitième", "neuvième", "dixième", "onzième", "douzième", 27 | "treizième", "quatorzième", "quinzième", "seizième", 28 | "dix-septième", "dix-huitième", "dix-neuvième" 29 | }; 30 | 31 | private static string[] OrdinalTenths = { 32 | "", "dixième", "vingtième", "trentième", "quarantième", "cinquantième", 33 | "soixantième", "soixante-dixième", "quatre-vingtième", "quatre-vingt-dixième" 34 | }; 35 | 36 | private static string[] OrdinalTenthsPlus = { 37 | "", "", "vingt", "trente", "quarante", "cinquante", 38 | "soixante", "", "quatre-vingt", "" 39 | }; 40 | 41 | public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) 42 | { 43 | if (numFmt == "cardinalText") 44 | { 45 | string result = ""; 46 | 47 | var sLevel = (levelNumber + 10000).ToString(); 48 | int thousands = int.Parse(sLevel.Substring(1, 1)); 49 | int hundreds = int.Parse(sLevel.Substring(2, 1)); 50 | int tens = int.Parse(sLevel.Substring(3, 1)); 51 | int ones = int.Parse(sLevel.Substring(4, 1)); 52 | 53 | /* exact thousands */ 54 | if (levelNumber == 1000) 55 | return "Mille"; 56 | if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) 57 | { 58 | result = OneThroughNineteen[thousands] + " mille"; 59 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 60 | } 61 | 62 | /* > 1000 */ 63 | if (levelNumber > 1000 && levelNumber < 2000) 64 | result = "mille "; 65 | else if (levelNumber > 2000 && levelNumber < 10000) 66 | result = OneThroughNineteen[thousands] + " mille "; 67 | 68 | /* exact hundreds */ 69 | if (hundreds > 0 && tens == 0 && ones == 0) 70 | { 71 | if (hundreds == 1) 72 | result += "cent"; 73 | else 74 | result += OneThroughNineteen[hundreds] + " cents"; 75 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 76 | } 77 | 78 | /* > 100 */ 79 | if (hundreds > 0) 80 | { 81 | if (hundreds == 1) 82 | result += "cent "; 83 | else 84 | result += OneThroughNineteen[hundreds] + " cent "; 85 | } 86 | 87 | /* exact tens */ 88 | if (tens > 0 && ones == 0) 89 | { 90 | if (tens == 8) 91 | result += "quatre-vingts"; 92 | else 93 | result += Tens[tens]; 94 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 95 | } 96 | 97 | /* 71-79 */ 98 | if (tens == 7) 99 | { 100 | if (ones == 1) 101 | result += Tens[6] + " et " + OneThroughNineteen[ones + 10]; 102 | else 103 | result += Tens[6] + "-" + OneThroughNineteen[ones + 10]; 104 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 105 | } 106 | 107 | /* 91-99 */ 108 | if (tens == 9) 109 | { 110 | result += Tens[8] + "-" + OneThroughNineteen[ones + 10]; 111 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 112 | } 113 | 114 | if (tens >= 2) 115 | { 116 | if (ones == 1 && tens != 8 && tens != 9) 117 | result += Tens[tens] + " et un"; 118 | else 119 | result += Tens[tens] + "-" + OneThroughNineteen[ones]; 120 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 121 | } 122 | 123 | if (levelNumber < 20) 124 | { 125 | result += OneThroughNineteen[tens * 10 + ones]; 126 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 127 | } 128 | 129 | result += OneThroughNineteen[tens * 10 + ones]; 130 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 131 | } 132 | if (numFmt == "ordinal") 133 | { 134 | string suffix; 135 | if (levelNumber == 1) 136 | suffix = "er"; 137 | else 138 | suffix = "e"; 139 | return levelNumber.ToString() + suffix; 140 | } 141 | if (numFmt == "ordinalText") 142 | { 143 | string result = ""; 144 | 145 | if (levelNumber == 1) 146 | return "Premier"; 147 | 148 | var sLevel = (levelNumber + 10000).ToString(); 149 | int thousands = int.Parse(sLevel.Substring(1, 1)); 150 | int hundreds = int.Parse(sLevel.Substring(2, 1)); 151 | int tens = int.Parse(sLevel.Substring(3, 1)); 152 | int ones = int.Parse(sLevel.Substring(4, 1)); 153 | 154 | /* exact thousands */ 155 | if (levelNumber == 1000) 156 | return "Millième"; 157 | if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) 158 | { 159 | result = OneThroughNineteen[thousands] + " millième"; 160 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 161 | } 162 | 163 | /* > 1000 */ 164 | if (levelNumber > 1000 && levelNumber < 2000) 165 | result = "mille "; 166 | else if (levelNumber > 2000 && levelNumber < 10000) 167 | result = OneThroughNineteen[thousands] + " mille "; 168 | 169 | /* exact hundreds */ 170 | if (hundreds > 0 && tens == 0 && ones == 0) 171 | { 172 | if (hundreds == 1) 173 | result += "centième"; 174 | else 175 | result += OneThroughNineteen[hundreds] + " centième"; 176 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 177 | } 178 | 179 | /* > 100 */ 180 | if (hundreds > 0) 181 | { 182 | if (hundreds == 1) 183 | result += "cent "; 184 | else 185 | result += OneThroughNineteen[hundreds] + " cent "; 186 | } 187 | 188 | /* exact tens */ 189 | if (tens > 0 && ones == 0) 190 | { 191 | result += OrdinalTenths[tens]; 192 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 193 | } 194 | 195 | /* 71-79 */ 196 | if (tens == 7) 197 | { 198 | if (ones == 1) 199 | result += OrdinalTenthsPlus[6] + " et " + OrdinalOneThroughNineteen[ones + 10]; 200 | else 201 | result += OrdinalTenthsPlus[6] + "-" + OrdinalOneThroughNineteen[ones + 10]; 202 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 203 | } 204 | 205 | /* 91-99 */ 206 | if (tens == 9) 207 | { 208 | result += OrdinalTenthsPlus[8] + "-" + OrdinalOneThroughNineteen[ones + 10]; 209 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 210 | } 211 | 212 | if (tens >= 2) 213 | { 214 | if (ones == 1 && tens != 8 && tens != 9) 215 | result += OrdinalTenthsPlus[tens] + " et unième"; 216 | else 217 | result += OrdinalTenthsPlus[tens] + "-" + OrdinalOneThroughNineteen[ones]; 218 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 219 | } 220 | 221 | if (levelNumber < 20) 222 | { 223 | result += OrdinalOneThroughNineteen[tens * 10 + ones]; 224 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 225 | } 226 | 227 | result += OrdinalOneThroughNineteen[tens * 10 + ones]; 228 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 229 | } 230 | return null; 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/GetListItemText_ru_RU.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | public class ListItemTextGetter_ru_RU 12 | { 13 | private static string[] OneThroughNineteen = { 14 | "один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", 15 | "девять", "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать", 16 | "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать" 17 | }; 18 | 19 | private static string[] Tens = { 20 | "десять", "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", 21 | "восемьдесят", "девяносто" 22 | }; 23 | 24 | private static string[] OrdinalOneThroughNineteen = { 25 | "первый", "второй", "третий", "четвертый", "пятый", "шестой", 26 | "седьмой", "восьмой", "девятый", "десятый", "одиннадцатый", "двенадцатый", 27 | "тринадцатый", "четырнадцатый", "пятнадцатый", "шестнадцатый", 28 | "семнадцатый", "восемнадцатый", "девятнадцатый" 29 | }; 30 | 31 | private static string[] OrdinalTenths = { 32 | "десятый", "двадцатый", "тридцатый", "сороковой", "пятидесятый", 33 | "шестидесятый", "семидесятый", "восьмидесятый", "девяностый" 34 | }; 35 | 36 | // TODO this is not correct for values above 99 37 | 38 | public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) 39 | { 40 | if (numFmt == "cardinalText") 41 | { 42 | string result = ""; 43 | int t1 = levelNumber / 1000; 44 | int t2 = levelNumber % 1000; 45 | if (t1 >= 1) 46 | result += OneThroughNineteen[t1 - 1] + " thousand"; 47 | if (t1 >= 1 && t2 == 0) 48 | return result.Substring(0, 1).ToUpper() + 49 | result.Substring(1); 50 | if (t1 >= 1) 51 | result += " "; 52 | int h1 = (levelNumber % 1000) / 100; 53 | int h2 = levelNumber % 100; 54 | if (h1 >= 1) 55 | result += OneThroughNineteen[h1 - 1] + " hundred"; 56 | if (h1 >= 1 && h2 == 0) 57 | return result.Substring(0, 1).ToUpper() + 58 | result.Substring(1); 59 | if (h1 >= 1) 60 | result += " "; 61 | int z = levelNumber % 100; 62 | if (z <= 19) 63 | result += OneThroughNineteen[z - 1]; 64 | else 65 | { 66 | int x = z / 10; 67 | int r = z % 10; 68 | result += Tens[x - 1]; 69 | if (r >= 1) 70 | result += "-" + OneThroughNineteen[r - 1]; 71 | } 72 | return result.Substring(0, 1).ToUpper() + 73 | result.Substring(1); 74 | } 75 | if (numFmt == "ordinalText") 76 | { 77 | string result = ""; 78 | int t1 = levelNumber / 1000; 79 | int t2 = levelNumber % 1000; 80 | if (t1 >= 1 && t2 != 0) 81 | result += OneThroughNineteen[t1 - 1] + " thousand"; 82 | if (t1 >= 1 && t2 == 0) 83 | { 84 | result += OneThroughNineteen[t1 - 1] + " thousandth"; 85 | return result.Substring(0, 1).ToUpper() + 86 | result.Substring(1); 87 | } 88 | if (t1 >= 1) 89 | result += " "; 90 | int h1 = (levelNumber % 1000) / 100; 91 | int h2 = levelNumber % 100; 92 | if (h1 >= 1 && h2 != 0) 93 | result += OneThroughNineteen[h1 - 1] + " hundred"; 94 | if (h1 >= 1 && h2 == 0) 95 | { 96 | result += OneThroughNineteen[h1 - 1] + " hundredth"; 97 | return result.Substring(0, 1).ToUpper() + 98 | result.Substring(1); 99 | } 100 | if (h1 >= 1) 101 | result += " "; 102 | int z = levelNumber % 100; 103 | if (z <= 19) 104 | result += OrdinalOneThroughNineteen[z - 1]; 105 | else 106 | { 107 | int x = z / 10; 108 | int r = z % 10; 109 | if (r == 0) 110 | result += OrdinalTenths[x - 1]; 111 | else 112 | result += Tens[x - 1]; 113 | if (r >= 1) 114 | result += " " + OrdinalOneThroughNineteen[r - 1]; 115 | } 116 | return result.Substring(0, 1).ToUpper() + 117 | result.Substring(1); 118 | } 119 | return null; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/GetListItemText_sv_SE.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | public class ListItemTextGetter_sv_SE 12 | { 13 | private static string[] OneThroughNineteen = { 14 | "", "ett", "två", "tre", "fyra", "fem", "sex", "sju", "åtta", 15 | "nio", "tio", "elva", "tolv", "tretton", "fjorton", 16 | "femton", "sexton", "sjutton", "arton", "nitton" 17 | }; 18 | 19 | private static string[] Tens = { 20 | "","tio", "tjugo", "trettio", "fyrtio", "femtio", "sextio", "sjuttio", "åttio", 21 | "nittio", "etthundra" 22 | }; 23 | 24 | private static string[] OrdinalOneThroughNineteen = { 25 | "", "första", "andra", "tredje", "fjärde", "femte", "sjätte", "sjunde", 26 | "åttonde", "nionde", "tionde", "elfte", "tolfte", "trettonde", 27 | "fjortonde", "femtonde", "sextonde", "sjuttonde", 28 | "artonde", "nittonde" 29 | }; 30 | 31 | public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) 32 | { 33 | switch (numFmt) 34 | { 35 | case "cardinalText": 36 | return NumberAsCardinalText(languageCultureName, levelNumber, numFmt); 37 | case "ordinalText": 38 | return NumberAsOrdinalText(languageCultureName, levelNumber, numFmt); 39 | case "ordinal": 40 | return NumberAsOrdinal(languageCultureName, levelNumber, numFmt); 41 | default: 42 | return null; 43 | } 44 | } 45 | private static string NumberAsCardinalText(string languageCultureName, int levelNumber, string numFmt) 46 | { 47 | string result = ""; 48 | 49 | var sLevel = (levelNumber + 10000).ToString(); 50 | int thousands = int.Parse(sLevel.Substring(1, 1)); 51 | int hundreds = int.Parse(sLevel.Substring(2, 1)); 52 | int tens = int.Parse(sLevel.Substring(3, 1)); 53 | int ones = int.Parse(sLevel.Substring(4, 1)); 54 | 55 | 56 | //Validation 57 | if (thousands > 19) 58 | throw new ArgumentOutOfRangeException("levelNumber", "Convering a levelNumber to ordinal text that is greater then 19 999 is not supported"); 59 | if (levelNumber == 0) 60 | return "Noll"; 61 | if (levelNumber < 0) 62 | throw new ArgumentOutOfRangeException("levelNumber", "Converting a negative levelNumber to ordinal text is not supported"); 63 | 64 | /* exact thousands */ 65 | if (levelNumber == 1000) 66 | return "Ettusen"; 67 | if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) 68 | { 69 | result = OneThroughNineteen[thousands] + "tusen"; 70 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 71 | } 72 | 73 | /* > 1000 */ 74 | if (levelNumber > 1000 && levelNumber < 2000) 75 | result = "ettusen"; 76 | else if (levelNumber > 2000 && levelNumber < 10000) 77 | result = OneThroughNineteen[thousands] + "tusen"; 78 | 79 | /* exact hundreds */ 80 | if (hundreds > 0 && tens == 0 && ones == 0) 81 | { 82 | if (hundreds == 1) 83 | result += "etthundra"; 84 | else 85 | result += OneThroughNineteen[hundreds] + "hundra"; 86 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 87 | } 88 | 89 | /* > 100 */ 90 | if (hundreds > 0) 91 | { 92 | if (hundreds == 1) 93 | result += "etthundra"; 94 | else 95 | result += OneThroughNineteen[hundreds] + "hundra"; 96 | } 97 | 98 | /* exact tens */ 99 | if (tens > 0 && ones == 0) 100 | { 101 | result += Tens[tens]; 102 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 103 | } 104 | 105 | /* > 20 */ 106 | if (tens == 1) 107 | { 108 | result += OneThroughNineteen[tens * 10 + ones]; 109 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 110 | } 111 | else if (tens > 1) 112 | { 113 | result += Tens[tens] + OneThroughNineteen[ones]; ; 114 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 115 | } 116 | else 117 | { 118 | result += OneThroughNineteen[ones]; 119 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 120 | } 121 | } 122 | private static string NumberAsOrdinalText(string languageCultureName, int levelNumber, string numFmt) 123 | { 124 | string result = ""; 125 | 126 | if (levelNumber <= 0) 127 | throw new ArgumentOutOfRangeException("levelNumber", "Converting a zero or negative levelNumber to ordinal text is not supported"); 128 | if(levelNumber >= 10000) 129 | throw new ArgumentOutOfRangeException("levelNumber", "Convering a levelNumber to ordinal text that is greater then 10000 is not supported"); 130 | 131 | if (levelNumber == 1) 132 | return "Första"; 133 | 134 | var sLevel = (levelNumber + 10000).ToString(); 135 | int thousands = int.Parse(sLevel.Substring(1, 1)); 136 | int hundreds = int.Parse(sLevel.Substring(2, 1)); 137 | int tens = int.Parse(sLevel.Substring(3, 1)); 138 | int ones = int.Parse(sLevel.Substring(4, 1)); 139 | 140 | 141 | /* exact thousands */ 142 | if (levelNumber == 1000) 143 | return "Ettusende"; 144 | if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) 145 | { 146 | result = OneThroughNineteen[thousands] + "tusende"; 147 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 148 | } 149 | 150 | /* > 1000 */ 151 | if (levelNumber > 1000 && levelNumber < 2000) 152 | result = "ettusen"; 153 | else if (levelNumber > 2000 && levelNumber < 10000) 154 | result = OneThroughNineteen[thousands] + "tusende"; 155 | 156 | /* exact hundreds */ 157 | if (hundreds > 0 && tens == 0 && ones == 0) 158 | { 159 | if (hundreds == 1) 160 | result += "etthundrade"; 161 | else 162 | result += OneThroughNineteen[hundreds] + "hundrade"; 163 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 164 | } 165 | 166 | /* > 100 */ 167 | if (hundreds > 0) 168 | { 169 | result += OneThroughNineteen[hundreds] + "hundra"; 170 | } 171 | 172 | /* exact tens */ 173 | if (tens > 0 && ones == 0) 174 | { 175 | result += Tens[tens] + "nde"; 176 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 177 | } 178 | 179 | /* > 20 */ 180 | if (tens == 1) 181 | { 182 | result += OrdinalOneThroughNineteen[tens * 10 + ones]; 183 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 184 | } 185 | else if (tens > 1) 186 | { 187 | result += Tens[tens] + OrdinalOneThroughNineteen[ones]; ; 188 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 189 | } 190 | else 191 | { 192 | result += OrdinalOneThroughNineteen[ones]; 193 | return result.Substring(0, 1).ToUpper() + result.Substring(1); 194 | } 195 | } 196 | private static string NumberAsOrdinal(string languageCultureName, int levelNumber, string numFmt) 197 | { 198 | string levelAsString = levelNumber.ToString(); 199 | 200 | if (levelAsString == null) 201 | return ""; 202 | if (levelAsString.Trim() == "") 203 | return ""; 204 | 205 | if(levelAsString.EndsWith("1")) 206 | return levelAsString + ":a"; 207 | else if(levelAsString.EndsWith("2")) 208 | return levelAsString + ":a"; 209 | else 210 | return levelAsString + ":e"; 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/GetListItemText_tr_TR.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | public class ListItemTextGetter_tr_TR 12 | { 13 | private static string[] RomanOnes = 14 | { 15 | "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" 16 | }; 17 | 18 | private static string[] RomanTens = 19 | { 20 | "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" 21 | }; 22 | 23 | private static string[] RomanHundreds = 24 | { 25 | "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M" 26 | }; 27 | 28 | private static string[] RomanThousands = 29 | { 30 | "", "M", "MM", "MMM", "MMMM", "MMMMM", "MMMMMM", "MMMMMMM", "MMMMMMMM", 31 | "MMMMMMMMM", "MMMMMMMMMM" 32 | }; 33 | 34 | private static string[] OneThroughNineteen = { 35 | "bir", "iki", "üç", "dört", "beş", "altı", "yedi", "sekiz", 36 | "dokuz", "on", "onbir", "oniki", "onüç", "ondört", 37 | "onbeş", "onaltı", "onyedi", "onsekiz", "ondokuz" 38 | }; 39 | 40 | private static string[] Tens = { 41 | "on", "yirmi", "otuz", "kırk", "elli", "altmış", "yetmiş", 42 | "seksen", "doksan" 43 | }; 44 | 45 | private static string[] OrdinalOneThroughNineteen = { 46 | "birinci", "ikinci", "üçüncü", "dördüncü", "beşinci", "altıncı", 47 | "yedinci", "sekizinci", "dokuzuncu", "onuncu", "onbirinci", "onikinci", 48 | "onüçüncü", "ondördüncü", "onbeşinci", "onaltıncı", 49 | "onyedinci", "onsekizinci", "ondokuzuncu" 50 | }; 51 | 52 | private static string[] TwoThroughNineteen = { 53 | "", "iki", "üç", "dört", "beş", "altı", "yedi", "sekiz", 54 | "dokuz", "on", "onbir", "oniki", "onüç", "ondört", 55 | "onbeş", "onaltı", "onyedi", "onsekiz", "ondokuz" 56 | }; 57 | 58 | private static string[] OrdinalTenths = { 59 | "onuncu", "yirminci", "otuzuncu", "kırkıncı", "ellinci", 60 | "altmışıncı", "yetmişinci", "sekseninci", "doksanıncı" 61 | }; 62 | 63 | public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) 64 | { 65 | #region 66 | if (numFmt == "decimal") 67 | { 68 | return levelNumber.ToString(); 69 | } 70 | if (numFmt == "decimalZero") 71 | { 72 | if (levelNumber <= 9) 73 | return "0" + levelNumber.ToString(); 74 | else 75 | return levelNumber.ToString(); 76 | } 77 | if (numFmt == "upperRoman") 78 | { 79 | int ones = levelNumber % 10; 80 | int tens = (levelNumber % 100) / 10; 81 | int hundreds = (levelNumber % 1000) / 100; 82 | int thousands = levelNumber / 1000; 83 | return RomanThousands[thousands] + RomanHundreds[hundreds] + 84 | RomanTens[tens] + RomanOnes[ones]; 85 | } 86 | if (numFmt == "lowerRoman") 87 | { 88 | int ones = levelNumber % 10; 89 | int tens = (levelNumber % 100) / 10; 90 | int hundreds = (levelNumber % 1000) / 100; 91 | int thousands = levelNumber / 1000; 92 | return (RomanThousands[thousands] + RomanHundreds[hundreds] + 93 | RomanTens[tens] + RomanOnes[ones]).ToLower(); 94 | } 95 | if (numFmt == "upperLetter") 96 | { 97 | string a = "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"; 98 | //string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 99 | int c = (levelNumber - 1) / 29; 100 | int n = (levelNumber - 1) % 29; 101 | char x = a[n]; 102 | return "".PadRight(c + 1, x); 103 | } 104 | if (numFmt == "lowerLetter") 105 | { 106 | string a = "abcçdefgğhıijklmnoöprsştuüvyz"; 107 | int c = (levelNumber - 1) / 29; 108 | int n = (levelNumber - 1) % 29; 109 | char x = a[n]; 110 | return "".PadRight(c + 1, x); 111 | } 112 | if (numFmt == "ordinal") 113 | { 114 | string suffix; 115 | /*if (levelNumber % 100 == 11 || levelNumber % 100 == 12 || 116 | levelNumber % 100 == 13) 117 | suffix = "th"; 118 | else if (levelNumber % 10 == 1) 119 | suffix = "st"; 120 | else if (levelNumber % 10 == 2) 121 | suffix = "nd"; 122 | else if (levelNumber % 10 == 3) 123 | suffix = "rd"; 124 | else 125 | suffix = "th";*/ 126 | suffix = "."; 127 | return levelNumber.ToString() + suffix; 128 | } 129 | if (numFmt == "cardinalText") 130 | { 131 | string result = ""; 132 | int t1 = levelNumber / 1000; 133 | int t2 = levelNumber % 1000; 134 | if (t1 >= 1) 135 | result += OneThroughNineteen[t1 - 1] + " yüz"; 136 | if (t1 >= 1 && t2 == 0) 137 | return result.Substring(0, 1).ToUpper() + 138 | result.Substring(1); 139 | if (t1 >= 1) 140 | result += " "; 141 | int h1 = (levelNumber % 1000) / 100; 142 | int h2 = levelNumber % 100; 143 | if (h1 >= 1) 144 | result += OneThroughNineteen[h1 - 1] + " bin"; 145 | if (h1 >= 1 && h2 == 0) 146 | return result.Substring(0, 1).ToUpper() + 147 | result.Substring(1); 148 | if (h1 >= 1) 149 | result += " "; 150 | int z = levelNumber % 100; 151 | if (z <= 19) 152 | result += OneThroughNineteen[z - 1]; 153 | else 154 | { 155 | int x = z / 10; 156 | int r = z % 10; 157 | result += Tens[x - 1]; 158 | if (r >= 1) 159 | result += /*"-" + */OneThroughNineteen[r - 1]; 160 | } 161 | return result.Substring(0, 1).ToUpper() + 162 | result.Substring(1); 163 | } 164 | #endregion 165 | if (numFmt == "ordinalText") 166 | { 167 | string result = ""; 168 | int t1 = levelNumber / 1000; 169 | int t2 = levelNumber % 1000; 170 | if (t1 >= 1 && t2 != 0) 171 | result += TwoThroughNineteen[t1 - 1] + "bin"; 172 | if (t1 >= 1 && t2 == 0) 173 | { 174 | result += TwoThroughNineteen[t1 - 1] + "bininci"; 175 | return result.Substring(0, 1).ToUpper() + 176 | result.Substring(1); 177 | } 178 | //if (t1 >= 1) 179 | // result += " "; 180 | int h1 = (levelNumber % 1000) / 100; 181 | int h2 = levelNumber % 100; 182 | if (h1 >= 1 && h2 != 0) 183 | result += TwoThroughNineteen[h1 - 1] + "yüz"; 184 | if (h1 >= 1 && h2 == 0) 185 | { 186 | result += TwoThroughNineteen[h1 - 1] + "yüzüncü"; 187 | return result.Substring(0, 1).ToUpper() + 188 | result.Substring(1); 189 | } 190 | //if (h1 >= 1) 191 | // result += " "; 192 | int z = levelNumber % 100; 193 | if (z <= 19) 194 | result += OrdinalOneThroughNineteen[z - 1]; 195 | else 196 | { 197 | int x = z / 10; 198 | int r = z % 10; 199 | if (r == 0) 200 | result += OrdinalTenths[x - 1]; 201 | else 202 | result += Tens[x - 1]; 203 | if (r >= 1) 204 | result += OrdinalOneThroughNineteen[r - 1]; //result += "-" + OrdinalOneThroughNineteen[r - 1]; 205 | } 206 | return result.Substring(0, 1).ToUpper() + 207 | result.Substring(1); 208 | } 209 | if (numFmt == "0001, 0002, 0003, ...") 210 | { 211 | return string.Format("{0:0000}", levelNumber); 212 | } 213 | if (numFmt == "bullet") 214 | return ""; 215 | return levelNumber.ToString(); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/GetListItemText_zh_CN.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | public class ListItemTextGetter_zh_CN 12 | { 13 | public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) 14 | { 15 | string[] ccTDigitCharacters = new[] { 16 | "", 17 | "一", 18 | "二", 19 | "三", 20 | "四", 21 | "五", 22 | "六", 23 | "七", 24 | "八", 25 | "九", 26 | }; 27 | string tenCharacter = "十"; 28 | string hundredCharacter = "百"; 29 | string thousandCharacter = "千"; 30 | string andCharacter = "〇"; 31 | 32 | string[] ccDigitCharacters = new[] { 33 | "○", 34 | "一", 35 | "二", 36 | "三", 37 | "四", 38 | "五", 39 | "六", 40 | "七", 41 | "八", 42 | "九", 43 | }; 44 | 45 | int thousandsRemainder = levelNumber % 1000; 46 | int hundredsRemainder = levelNumber % 100; 47 | int thousands = levelNumber / 1000; 48 | int hundreds = (levelNumber % 1000) / 100; 49 | int tens = (levelNumber % 100) / 10; 50 | int ones = levelNumber % 10; 51 | 52 | if (numFmt == "chineseCounting") 53 | { 54 | if (levelNumber >= 1 && levelNumber <= 9) 55 | return ccDigitCharacters[levelNumber]; 56 | if (levelNumber >= 10 && levelNumber <= 19) 57 | { 58 | if (levelNumber == 10) 59 | return tenCharacter; 60 | return tenCharacter + ccDigitCharacters[ones]; 61 | } 62 | if (levelNumber >= 11 && levelNumber <= 99) 63 | { 64 | if (ones == 0) 65 | return ccDigitCharacters[tens] + tenCharacter; 66 | return ccDigitCharacters[tens] + tenCharacter + ccDigitCharacters[ones]; 67 | } 68 | if (levelNumber >= 100 && levelNumber <= 999) 69 | return ccDigitCharacters[hundreds] + ccDigitCharacters[tens] + ccDigitCharacters[ones]; 70 | if (levelNumber >= 1000 && levelNumber <= 9999) 71 | return ccDigitCharacters[thousands] + ccDigitCharacters[hundreds] + ccDigitCharacters[tens] + ccDigitCharacters[ones]; 72 | return levelNumber.ToString(); 73 | } 74 | if (numFmt == "chineseCountingThousand") 75 | { 76 | if (levelNumber >= 1 && levelNumber <= 9) 77 | return ccTDigitCharacters[levelNumber]; 78 | if (levelNumber >= 10 && levelNumber <= 19) 79 | return tenCharacter + ccTDigitCharacters[ones]; 80 | if (levelNumber >= 20 && levelNumber <= 99) 81 | return ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; 82 | if (levelNumber >= 100 && levelNumber <= 999) 83 | { 84 | if (hundredsRemainder == 0) 85 | return ccTDigitCharacters[hundreds] + hundredCharacter; 86 | if (hundredsRemainder >= 1 && hundredsRemainder <= 9) 87 | return ccTDigitCharacters[hundreds] + hundredCharacter + andCharacter + ccTDigitCharacters[levelNumber % 10]; 88 | if (ones == 0) 89 | return ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter; 90 | return ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; 91 | } 92 | if (levelNumber >= 1000 && levelNumber <= 9999) 93 | { 94 | if (thousandsRemainder == 0) 95 | return ccTDigitCharacters[thousands] + thousandCharacter; 96 | if (thousandsRemainder >= 1 && thousandsRemainder <= 9) 97 | return ccTDigitCharacters[thousands] + thousandCharacter + andCharacter + GetListItemText("zh_CN", thousandsRemainder, numFmt); 98 | if (thousandsRemainder >= 10 && thousandsRemainder <= 99) 99 | return ccTDigitCharacters[thousands] + thousandCharacter + andCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; 100 | if (hundredsRemainder == 0) 101 | return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter; 102 | if (hundredsRemainder >= 1 && hundredsRemainder <= 9) 103 | return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter + andCharacter + ccTDigitCharacters[ones]; 104 | return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; 105 | } 106 | return levelNumber.ToString(); 107 | } 108 | if (numFmt == "ideographTraditional") 109 | { 110 | string[] iDigitCharacters = new[] { 111 | " ", 112 | "甲", 113 | "乙", 114 | "丙", 115 | "丁", 116 | "戊", 117 | "己", 118 | "庚", 119 | "辛", 120 | "壬", 121 | "癸", 122 | }; 123 | if (levelNumber >= 1 && levelNumber <= 10) 124 | return iDigitCharacters[levelNumber]; 125 | return levelNumber.ToString(); 126 | } 127 | return null; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/PowerToolsBlock.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using DocumentFormat.OpenXml.Packaging; 6 | 7 | namespace OpenXmlPowerTools 8 | { 9 | /// 10 | /// Provides an elegant way of wrapping a set of invocations of the PowerTools in a using 11 | /// statement that demarcates those invokations as one "block" before and after which the 12 | /// strongly typed classes provided by the Open XML SDK can be used safely. 13 | /// 14 | /// 15 | /// 16 | /// This class lends itself to scenarios where the PowerTools and Linq-to-XML are used as 17 | /// a secondary API for working with Open XML elements, next to the strongly typed classes 18 | /// provided by the Open XML SDK. In these scenarios, the class would be 19 | /// used as follows: 20 | /// 21 | /// 22 | /// [Your code using the strongly typed classes] 23 | /// 24 | /// using (new PowerToolsBlock(wordprocessingDocument)) 25 | /// { 26 | /// [Your code using the PowerTools] 27 | /// } 28 | /// 29 | /// [Your code using the strongly typed classes] 30 | /// 31 | /// 32 | /// Upon creation, instances of this class will invoke the 33 | /// method on the package 34 | /// to begin the transaction. Upon disposal, instances of this class will call the 35 | /// method on the package 36 | /// to end the transaction. 37 | /// 38 | /// 39 | /// 40 | /// 41 | /// 42 | public class PowerToolsBlock : IDisposable 43 | { 44 | private OpenXmlPackage _package; 45 | 46 | public PowerToolsBlock(OpenXmlPackage package) 47 | { 48 | if (package == null) throw new ArgumentNullException("package"); 49 | 50 | _package = package; 51 | _package.BeginPowerToolsBlock(); 52 | } 53 | 54 | public void Dispose() 55 | { 56 | if (_package == null) return; 57 | 58 | _package.EndPowerToolsBlock(); 59 | _package = null; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/PowerToolsBlockExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Linq; 6 | using System.Xml; 7 | using System.Xml.Linq; 8 | using DocumentFormat.OpenXml.Packaging; 9 | 10 | namespace OpenXmlPowerTools 11 | { 12 | public static class PowerToolsBlockExtensions 13 | { 14 | /// 15 | /// Begins a PowerTools Block by (1) removing annotations and, unless the package was 16 | /// opened in read-only mode, (2) saving the package. 17 | /// 18 | /// 19 | /// Removes and instances 20 | /// added by , 21 | /// , 22 | /// , 23 | /// , and 24 | /// . 25 | /// methods. 26 | /// 27 | /// 28 | /// A , , 29 | /// or . 30 | /// 31 | public static void BeginPowerToolsBlock(this OpenXmlPackage package) 32 | { 33 | if (package == null) throw new ArgumentNullException("package"); 34 | 35 | package.RemovePowerToolsAnnotations(); 36 | package.Save(); 37 | } 38 | 39 | /// 40 | /// Ends a PowerTools Block by reloading the root elements of all package parts 41 | /// that were changed by the PowerTools. A part is deemed changed by the PowerTools 42 | /// if it has an annotation of type . 43 | /// 44 | /// 45 | /// A , , 46 | /// or . 47 | /// 48 | public static void EndPowerToolsBlock(this OpenXmlPackage package) 49 | { 50 | if (package == null) throw new ArgumentNullException("package"); 51 | 52 | foreach (OpenXmlPart part in package.GetAllParts()) 53 | { 54 | if (part.Annotations().Any() && part.RootElement != null) 55 | part.RootElement.Reload(); 56 | } 57 | } 58 | 59 | private static void RemovePowerToolsAnnotations(this OpenXmlPackage package) 60 | { 61 | if (package == null) throw new ArgumentNullException("package"); 62 | 63 | foreach (OpenXmlPart part in package.GetAllParts()) 64 | { 65 | part.RemoveAnnotations(); 66 | part.RemoveAnnotations(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | [assembly: InternalsVisibleTo("OpenXmlPowerTools.Tests")] 7 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/RevisionAccepter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Xml.Linq; 8 | using DocumentFormat.OpenXml.Packaging; 9 | 10 | namespace OpenXmlPowerTools 11 | { 12 | public class RevisionAccepter 13 | { 14 | public static WmlDocument AcceptRevisions(WmlDocument document) 15 | { 16 | using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document)) 17 | { 18 | using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) 19 | { 20 | AcceptRevisions(doc); 21 | } 22 | return streamDoc.GetModifiedWmlDocument(); 23 | } 24 | } 25 | 26 | public static void AcceptRevisions(WordprocessingDocument doc) 27 | { 28 | RevisionProcessor.AcceptRevisions(doc); 29 | } 30 | 31 | public static bool PartHasTrackedRevisions(OpenXmlPart part) 32 | { 33 | return RevisionProcessor.PartHasTrackedRevisions(part); 34 | } 35 | 36 | public static bool HasTrackedRevisions(WmlDocument document) 37 | { 38 | return RevisionProcessor.HasTrackedRevisions(document); 39 | } 40 | 41 | public static bool HasTrackedRevisions(WordprocessingDocument doc) 42 | { 43 | return RevisionProcessor.HasTrackedRevisions(doc); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/SSFormula.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace ExcelFormula 10 | { 11 | public class ParseFormula 12 | { 13 | ExcelFormula parser; 14 | 15 | public ParseFormula(string formula) 16 | { 17 | parser = new ExcelFormula(formula, Console.Out); 18 | bool parserResult = false; 19 | try 20 | { 21 | parserResult = parser.Formula(); 22 | } 23 | catch (Peg.Base.PegException) 24 | { 25 | } 26 | if (!parserResult) 27 | { 28 | parser.Warning("Error processing " + formula); 29 | } 30 | } 31 | 32 | public string ReplaceSheetName(string oldName, string newName) 33 | { 34 | StringBuilder text = new StringBuilder(parser.GetSource()); 35 | ReplaceNode(parser.GetRoot(), (int)EExcelFormula.SheetName, oldName, newName, text); 36 | return text.ToString(); 37 | } 38 | 39 | public string ReplaceRelativeCell(int rowOffset, int colOffset) 40 | { 41 | StringBuilder text = new StringBuilder(parser.GetSource()); 42 | ReplaceRelativeCell(parser.GetRoot(), rowOffset, colOffset, text); 43 | return text.ToString(); 44 | } 45 | 46 | // Recursive function that will replace values from last to first 47 | private void ReplaceNode(Peg.Base.PegNode node, int id, string oldName, string newName, StringBuilder text) 48 | { 49 | if (node.next_ != null) 50 | ReplaceNode(node.next_, id, oldName, newName, text); 51 | if (node.id_ == id && parser.GetSource().Substring(node.match_.posBeg_, node.match_.Length) == oldName) 52 | { 53 | text.Remove(node.match_.posBeg_, node.match_.Length); 54 | text.Insert(node.match_.posBeg_, newName); 55 | } 56 | else if (node.child_ != null) 57 | ReplaceNode(node.child_, id, oldName, newName, text); 58 | } 59 | 60 | // Recursive function that will adjust relative cells from last to first 61 | private void ReplaceRelativeCell(Peg.Base.PegNode node, int rowOffset, int colOffset, StringBuilder text) 62 | { 63 | if (node.next_ != null) 64 | ReplaceRelativeCell(node.next_, rowOffset, colOffset, text); 65 | if (node.id_ == (int)EExcelFormula.A1Row && parser.GetSource().Substring(node.match_.posBeg_, 1) != "$") 66 | { 67 | int rowNumber = Convert.ToInt32(parser.GetSource().Substring(node.match_.posBeg_, node.match_.Length)); 68 | text.Remove(node.match_.posBeg_, node.match_.Length); 69 | text.Insert(node.match_.posBeg_, Convert.ToString(rowNumber + rowOffset)); 70 | } 71 | else if (node.id_ == (int)EExcelFormula.A1Column && parser.GetSource().Substring(node.match_.posBeg_, 1) != "$") 72 | { 73 | int colNumber = GetColumnNumber(parser.GetSource().Substring(node.match_.posBeg_, node.match_.Length)); 74 | text.Remove(node.match_.posBeg_, node.match_.Length); 75 | text.Insert(node.match_.posBeg_, GetColumnId(colNumber + colOffset)); 76 | } 77 | else if (node.child_ != null) 78 | ReplaceRelativeCell(node.child_, rowOffset, colOffset, text); 79 | } 80 | 81 | // Converts the column reference string to a column number (e.g. A -> 1, B -> 2) 82 | private static int GetColumnNumber(string cellReference) 83 | { 84 | int columnNumber = 0; 85 | foreach (char c in cellReference) 86 | { 87 | if (Char.IsLetter(c)) 88 | columnNumber = columnNumber * 26 + System.Convert.ToInt32(c) - System.Convert.ToInt32('A') + 1; 89 | } 90 | return columnNumber; 91 | } 92 | 93 | // Translates the column number to the column reference string (e.g. 1 -> A, 2-> B) 94 | private static string GetColumnId(int columnNumber) 95 | { 96 | string result = ""; 97 | do 98 | { 99 | result = ((char)((columnNumber - 1) % 26 + (int)'A')).ToString() + result; 100 | columnNumber = (columnNumber - 1) / 26; 101 | } while (columnNumber != 0); 102 | return result; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/ScalarTypes.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.ObjectModel; 7 | 8 | namespace OpenXmlPowerTools 9 | { 10 | internal static class DefaultScalarTypes 11 | { 12 | private static readonly Hashtable defaultScalarTypesHash; 13 | internal static bool IsTypeInList(Collection typeNames) 14 | { 15 | string text = PSObjectIsOfExactType(typeNames); 16 | return !string.IsNullOrEmpty(text) && (PSObjectIsEnum(typeNames) || DefaultScalarTypes.defaultScalarTypesHash.ContainsKey(text)); 17 | } 18 | 19 | static DefaultScalarTypes() 20 | { 21 | DefaultScalarTypes.defaultScalarTypesHash = new Hashtable(StringComparer.OrdinalIgnoreCase); 22 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.String", null); 23 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.SByte", null); 24 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Byte", null); 25 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Int16", null); 26 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.UInt16", null); 27 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Int32", 10); 28 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.UInt32", 10); 29 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Int64", null); 30 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.UInt64", null); 31 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Char", 1); 32 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Single", null); 33 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Double", null); 34 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Boolean", 5); 35 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Decimal", null); 36 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.IntPtr", null); 37 | DefaultScalarTypes.defaultScalarTypesHash.Add("System.Security.SecureString", null); 38 | } 39 | 40 | internal static string PSObjectIsOfExactType(Collection typeNames) 41 | { 42 | if (typeNames.Count != 0) 43 | { 44 | return typeNames[0]; 45 | } 46 | return null; 47 | } 48 | 49 | internal static bool PSObjectIsEnum(Collection typeNames) 50 | { 51 | return typeNames.Count >= 2 && !string.IsNullOrEmpty(typeNames[1]) && string.Equals(typeNames[1], "System.Enum", StringComparison.Ordinal); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/SmlCellFormatter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.Drawing; 8 | using System.Globalization; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Text.RegularExpressions; 12 | using System.Xml.Linq; 13 | using DocumentFormat.OpenXml.Packaging; 14 | 15 | namespace OpenXmlPowerTools 16 | { 17 | public class SmlCellFormatter 18 | { 19 | private enum CellType 20 | { 21 | General, 22 | Number, 23 | Date, 24 | }; 25 | 26 | private class FormatConfig 27 | { 28 | public CellType CellType; 29 | public string FormatCode; 30 | } 31 | 32 | private static Dictionary ExcelFormatCodeToNetFormatCodeExceptionMap = new Dictionary() 33 | { 34 | { 35 | "# ?/?", 36 | new FormatConfig 37 | { 38 | CellType = SmlCellFormatter.CellType.Number, 39 | FormatCode = "0.00", 40 | } 41 | }, 42 | { 43 | "# ??/??", 44 | new FormatConfig 45 | { 46 | CellType = SmlCellFormatter.CellType.Number, 47 | FormatCode = "0.00", 48 | } 49 | }, 50 | }; 51 | 52 | // Up to four sections of format codes can be specified. The format codes, separated by semicolons, define the 53 | // formats for positive numbers, negative numbers, zero values, and text, in that order. If only two sections are 54 | // specified, the first is used for positive numbers and zeros, and the second is used for negative numbers. If only 55 | // one section is specified, it is used for all numbers. To skip a section, the ending semicolon for that section shall 56 | // be written. 57 | public static string FormatCell(string formatCode, string value, out string color) 58 | { 59 | color = null; 60 | 61 | if (formatCode == null) 62 | formatCode = "General"; 63 | 64 | var splitFormatCode = formatCode.Split(';'); 65 | if (splitFormatCode.Length == 1) 66 | { 67 | double dv; 68 | if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out dv)) 69 | { 70 | return FormatDouble(formatCode, dv, out color); 71 | } 72 | return value; 73 | } 74 | if (splitFormatCode.Length == 2) 75 | { 76 | double dv; 77 | if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out dv)) 78 | { 79 | if (dv > 0) 80 | { 81 | return FormatDouble(splitFormatCode[0], dv, out color); 82 | } 83 | else 84 | { 85 | return FormatDouble(splitFormatCode[1], dv, out color); 86 | } 87 | } 88 | return value; 89 | 90 | } 91 | // positive, negative, zero, text 92 | // _("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_) 93 | if (splitFormatCode.Length == 4) 94 | { 95 | double dv; 96 | if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out dv)) 97 | { 98 | if (dv > 0) 99 | { 100 | var z1 = FormatDouble(splitFormatCode[0], dv, out color); 101 | return z1; 102 | } 103 | else if (dv < 0) 104 | { 105 | var z2 = FormatDouble(splitFormatCode[1], dv, out color); 106 | return z2; 107 | } 108 | else // == 0 109 | { 110 | var z3 = FormatDouble(splitFormatCode[2], dv, out color); 111 | return z3; 112 | } 113 | } 114 | string fmt = splitFormatCode[3].Replace("@", "{0}").Replace("\"", ""); 115 | try 116 | { 117 | var s = string.Format(fmt, value); 118 | return s; 119 | } 120 | catch (Exception) 121 | { 122 | return value; 123 | } 124 | } 125 | return value; 126 | } 127 | 128 | static Regex UnderRegex = new Regex("_."); 129 | 130 | // The following Regex transforms currency specifies into a character / string 131 | // that string.Format can use to properly produce the correct text. 132 | // "[$£-809]" => "£" 133 | // "[$€-2]" => "€" 134 | // "[$¥-804]" => "¥ 135 | // "[$CHF-100C]" => "CHF" 136 | static string s_CurrRegex = @"\[\$(?.*-).*\]"; 137 | 138 | private static string ConvertFormatCode(string formatCode) 139 | { 140 | var newFormatCode = formatCode 141 | .Replace("mmm-", "MMM-") 142 | .Replace("-mmm", "-MMM") 143 | .Replace("mm-", "MM-") 144 | .Replace("mmmm", "MMMM") 145 | .Replace("AM/PM", "tt") 146 | .Replace("m/", "M/") 147 | .Replace("*", "") 148 | .Replace("?", "#") 149 | ; 150 | var withTrimmedUnderscores = UnderRegex.Replace(newFormatCode, ""); 151 | var withTransformedCurrency = Regex.Replace(withTrimmedUnderscores, s_CurrRegex, m => m.Groups[1].Value.TrimEnd('-')); 152 | return withTransformedCurrency; 153 | } 154 | 155 | private static string[] ValidColors = new[] { 156 | "Black", 157 | "Blue", 158 | "Cyan", 159 | "Green", 160 | "Magenta", 161 | "Red", 162 | "White", 163 | "Yellow", 164 | }; 165 | 166 | private static string FormatDouble(string formatCode, double dv, out string color) 167 | { 168 | color = null; 169 | var trimmed = formatCode.Trim(); 170 | if (trimmed.StartsWith("[") && 171 | trimmed.Contains("]")) 172 | { 173 | var colorLen = trimmed.IndexOf(']'); 174 | color = trimmed.Substring(1, colorLen - 1); 175 | if (ValidColors.Contains(color) || 176 | color.StartsWith("Color")) 177 | { 178 | if (color.StartsWith("Color")) 179 | { 180 | var idxStr = color.Substring(5); 181 | int colorIdx; 182 | if (int.TryParse(idxStr, out colorIdx)) 183 | { 184 | if (colorIdx < SmlDataRetriever.IndexedColors.Length) 185 | color = SmlDataRetriever.IndexedColors[colorIdx]; 186 | else 187 | color = null; 188 | } 189 | } 190 | formatCode = trimmed.Substring(colorLen + 1); 191 | } 192 | else 193 | color = null; 194 | } 195 | 196 | 197 | if (formatCode == "General") 198 | return dv.ToString(CultureInfo.InvariantCulture); 199 | bool isDate = IsFormatCodeForDate(formatCode); 200 | var cfc = ConvertFormatCode(formatCode); 201 | if (isDate) 202 | { 203 | DateTime thisDate; 204 | try 205 | { 206 | thisDate = DateTime.FromOADate(dv); 207 | } 208 | catch (ArgumentException) 209 | { 210 | return dv.ToString(CultureInfo.InvariantCulture); 211 | } 212 | if (cfc.StartsWith("[h]")) 213 | { 214 | DateTime zeroHour = new DateTime(1899, 12, 30, 0, 0, 0); 215 | int deltaInHours = (int)((thisDate - zeroHour).TotalHours); 216 | var newCfc = cfc.Substring(3); 217 | var s = (deltaInHours.ToString() + thisDate.ToString(newCfc)).Trim(); 218 | return s; 219 | } 220 | if (cfc.EndsWith(".0")) 221 | { 222 | var cfc2 = cfc.Replace(".0", ":fff"); 223 | var s4 = thisDate.ToString(cfc2).Trim(); 224 | return s4; 225 | } 226 | var s2 = thisDate.ToString(cfc, CultureInfo.InvariantCulture).Trim(); 227 | return s2; 228 | } 229 | if (ExcelFormatCodeToNetFormatCodeExceptionMap.ContainsKey(formatCode)) 230 | { 231 | FormatConfig fc = ExcelFormatCodeToNetFormatCodeExceptionMap[formatCode]; 232 | var s = dv.ToString(fc.FormatCode, CultureInfo.InvariantCulture).Trim(); 233 | return s; 234 | } 235 | if ((cfc.Contains('(') && cfc.Contains(')')) || cfc.Contains('-')) 236 | { 237 | var s3 = (-dv).ToString(cfc, CultureInfo.InvariantCulture).Trim(); 238 | return s3; 239 | } 240 | else 241 | { 242 | var s4 = dv.ToString(cfc, CultureInfo.InvariantCulture).Trim(); 243 | return s4; 244 | } 245 | } 246 | 247 | private static bool IsFormatCodeForDate(string formatCode) 248 | { 249 | if (formatCode == "General") 250 | return false; 251 | return formatCode.Contains("m") || 252 | formatCode.Contains("d") || 253 | formatCode.Contains("y") || 254 | formatCode.Contains("h") || 255 | formatCode.Contains("s") || 256 | formatCode.Contains("AM") || 257 | formatCode.Contains("PM"); 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/SpreadsheetDocumentManager.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Xml.Linq; 7 | using DocumentFormat.OpenXml.Packaging; 8 | 9 | namespace OpenXmlPowerTools 10 | { 11 | /// 12 | /// Manages SpreadsheetDocument content 13 | /// 14 | public class SpreadsheetDocumentManager 15 | { 16 | private static XNamespace ns; 17 | private static XNamespace relationshipsns; 18 | private static int headerRow = 1; 19 | 20 | static SpreadsheetDocumentManager() 21 | { 22 | ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; 23 | relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; 24 | } 25 | 26 | /// 27 | /// Creates a spreadsheet document from a value table 28 | /// 29 | /// Path to store the document 30 | /// Contents of first row (header) 31 | /// Contents of data 32 | /// Row to start copying data from 33 | /// 34 | public static void Create(SpreadsheetDocument document, List headerList, string[][] valueTable, int initialRow) 35 | { 36 | headerRow = initialRow; 37 | 38 | //Creates a worksheet with given data 39 | WorksheetPart worksheet = WorksheetAccessor.Create(document, headerList, valueTable, headerRow); 40 | } 41 | 42 | /// 43 | /// Creates a spreadsheet document with a chart from a value table 44 | /// 45 | /// Path to store the document 46 | /// Contents of first row (header) 47 | /// Contents of data 48 | /// Chart type 49 | /// Column to use as category for charting 50 | /// Columns to use as data series 51 | /// Row index to start copying data 52 | /// SpreadsheetDocument 53 | //public static void Create(SpreadsheetDocument document, List headerList, string[][] valueTable, ChartType chartType, string categoryColumn, List columnsToChart, int initialRow) 54 | //{ 55 | // headerRow = initialRow; 56 | 57 | // //Creates worksheet with data 58 | // WorksheetPart worksheet = WorksheetAccessor.Create(document, headerList, valueTable, headerRow); 59 | // //Creates chartsheet with given series and category 60 | // string sheetName = GetSheetName(worksheet, document); 61 | // ChartsheetPart chartsheet = 62 | // ChartsheetAccessor.Create(document, 63 | // chartType, 64 | // GetValueReferences(sheetName, categoryColumn, headerList, columnsToChart, valueTable), 65 | // GetHeaderReferences(sheetName, categoryColumn, headerList, columnsToChart, valueTable), 66 | // GetCategoryReference(sheetName, categoryColumn, headerList, valueTable) 67 | // ); 68 | //} 69 | 70 | /// 71 | /// Gets the internal name of a worksheet from a document 72 | /// 73 | private static string GetSheetName(WorksheetPart worksheet, SpreadsheetDocument document) 74 | { 75 | //Gets the id of worksheet part 76 | string partId = document.WorkbookPart.GetIdOfPart(worksheet); 77 | XDocument workbookDocument = document.WorkbookPart.GetXDocument(); 78 | //Gets the name from sheet tag related to worksheet 79 | string sheetName = 80 | workbookDocument.Root 81 | .Element(ns + "sheets") 82 | .Elements(ns + "sheet") 83 | .Where( 84 | t => 85 | t.Attribute(relationshipsns + "id").Value == partId 86 | ).First() 87 | .Attribute("name").Value; 88 | return sheetName; 89 | } 90 | /// 91 | /// Gets the range reference for category 92 | /// 93 | /// worksheet to take data from 94 | /// name of column used as category 95 | /// column names from data 96 | /// Data values 97 | /// 98 | private static string GetCategoryReference(string sheetName, string headerColumn, List headerList, string[][] valueTable) 99 | { 100 | int categoryColumn = headerList.IndexOf(headerColumn.ToUpper()) + 1; 101 | int numRows = valueTable.GetLength(0); 102 | 103 | return GetRangeReference( 104 | sheetName, 105 | categoryColumn, 106 | headerRow + 1, 107 | categoryColumn, 108 | numRows + headerRow 109 | ); 110 | } 111 | 112 | /// 113 | /// Gets a list of range references for each of the series headers 114 | /// 115 | /// worksheet to take data from 116 | /// name of column used as category 117 | /// column names from data 118 | /// Data values 119 | /// Columns used as data series 120 | /// 121 | private static List GetHeaderReferences(string sheetName, string headerColumn, List headerList, List colsToChart, string[][] valueTable) 122 | { 123 | List valueReferenceList = new List(); 124 | 125 | foreach (string column in colsToChart) 126 | { 127 | valueReferenceList.Add( 128 | GetRangeReference( 129 | sheetName, 130 | headerList.IndexOf(column.ToUpper()) + 1, 131 | headerRow 132 | ) 133 | ); 134 | } 135 | return valueReferenceList; 136 | } 137 | 138 | /// 139 | /// Gets a list of range references for each of the series values 140 | /// 141 | /// worksheet to take data from 142 | /// name of column used as category 143 | /// column names from data 144 | /// Data values 145 | /// Columns used as data series 146 | /// 147 | private static List GetValueReferences(string sheetName, string headerColumn, List headerList, List colsToChart, string[][] valueTable) 148 | { 149 | List valueReferenceList = new List(); 150 | int numRows = valueTable.GetLength(0); 151 | 152 | foreach (string column in colsToChart) 153 | { 154 | int dataColumn = headerList.IndexOf(column.ToUpper()) + 1; 155 | valueReferenceList.Add( 156 | GetRangeReference( 157 | sheetName, 158 | dataColumn, 159 | headerRow + 1, 160 | dataColumn, 161 | numRows + headerRow 162 | ) 163 | ); 164 | } 165 | return valueReferenceList; 166 | } 167 | 168 | /// 169 | /// Gets a formatted representation of a cell range from a worksheet 170 | /// 171 | private static string GetRangeReference(string worksheet, int column, int row) 172 | { 173 | return string.Format("{0}!{1}{2}", worksheet, WorksheetAccessor.GetColumnId(column), row); 174 | } 175 | 176 | /// 177 | /// Gets a formatted representation of a cell range from a worksheet 178 | /// 179 | private static string GetRangeReference(string worksheet, int startColumn, int startRow, int endColumn, int endRow) 180 | { 181 | return string.Format("{0}!{1}{2}:{3}{4}", 182 | worksheet, 183 | WorksheetAccessor.GetColumnId(startColumn), 184 | startRow, 185 | WorksheetAccessor.GetColumnId(endColumn), 186 | endRow 187 | ); 188 | } 189 | 190 | /// 191 | /// Creates an empty (base) workbook document 192 | /// 193 | /// 194 | private static XDocument CreateEmptyWorkbook() 195 | { 196 | XDocument document = 197 | new XDocument( 198 | new XElement(ns + "workbook", 199 | new XAttribute("xmlns", ns), 200 | new XAttribute(XNamespace.Xmlns + "r", relationshipsns), 201 | new XElement(ns + "sheets") 202 | ) 203 | ); 204 | 205 | return document; 206 | } 207 | } 208 | } -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/StronglyTypedBlock.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using DocumentFormat.OpenXml.Packaging; 6 | 7 | namespace OpenXmlPowerTools 8 | { 9 | /// 10 | /// Provides an elegant way of wrapping a set of invocations of the strongly typed 11 | /// classes provided by the Open XML SDK) in a using statement that demarcates those 12 | /// invokations as one "block" before and after which the PowerTools can be used safely. 13 | /// 14 | /// 15 | /// 16 | /// This class lends itself to scenarios where the PowerTools and Linq-to-XML are used as 17 | /// the primary API for working with Open XML elements, next to the strongly typed classes 18 | /// provided by the Open XML SDK. In these scenarios, the class would be used as follows: 19 | /// 20 | /// 21 | /// [Your code using the PowerTools] 22 | /// 23 | /// using (new NonPowerToolsBlock(wordprocessingDocument)) 24 | /// { 25 | /// [Your code using the strongly typed classes] 26 | /// } 27 | /// 28 | /// [Your code using the PowerTools] 29 | /// 30 | /// 31 | /// Upon creation, instances of this class will invoke the 32 | /// method on the package 33 | /// to begin the block. Upon disposal, instances of this class will call the 34 | /// method on the package 35 | /// to end the block. 36 | /// 37 | /// 38 | /// 39 | /// 40 | /// 41 | public class StronglyTypedBlock : IDisposable 42 | { 43 | private OpenXmlPackage _package; 44 | 45 | public StronglyTypedBlock(OpenXmlPackage package) 46 | { 47 | if (package == null) throw new ArgumentNullException("package"); 48 | 49 | _package = package; 50 | _package.EndPowerToolsBlock(); 51 | } 52 | 53 | public void Dispose() 54 | { 55 | if (_package == null) return; 56 | 57 | _package.BeginPowerToolsBlock(); 58 | _package = null; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/TestUtil.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace OpenXmlPowerTools 13 | { 14 | public class TestUtil 15 | { 16 | private static bool? s_DeleteTempFiles = null; 17 | 18 | public static bool DeleteTempFiles 19 | { 20 | get 21 | { 22 | if (s_DeleteTempFiles != null) 23 | return (bool)s_DeleteTempFiles; 24 | FileInfo donotdelete = new FileInfo("donotdelete.txt"); 25 | s_DeleteTempFiles = !donotdelete.Exists; 26 | return (bool)s_DeleteTempFiles; 27 | } 28 | } 29 | 30 | private static DirectoryInfo s_TempDir = null; 31 | public static DirectoryInfo TempDir 32 | { 33 | get 34 | { 35 | if (s_TempDir != null) 36 | return s_TempDir; 37 | else 38 | { 39 | var now = DateTime.Now; 40 | var tempDirName = String.Format("Test-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", now.Year - 2000, now.Month, now.Day, now.Hour, now.Minute, now.Second); 41 | s_TempDir = new DirectoryInfo(Path.Combine(".", tempDirName)); 42 | s_TempDir.Create(); 43 | return s_TempDir; 44 | } 45 | } 46 | } 47 | 48 | public static void NotePad(string str) 49 | { 50 | var guidName = Guid.NewGuid().ToString().Replace("-", "") + ".txt"; 51 | var fi = new FileInfo(Path.Combine(TempDir.FullName, guidName)); 52 | File.WriteAllText(fi.FullName, str); 53 | var notepadExe = new FileInfo(@"C:\Program Files (x86)\Notepad++\notepad++.exe"); 54 | if (!notepadExe.Exists) 55 | notepadExe = new FileInfo(@"C:\Program Files\Notepad++\notepad++.exe"); 56 | if (!notepadExe.Exists) 57 | notepadExe = new FileInfo(@"C:\Windows\System32\notepad.exe"); 58 | ExecutableRunner.RunExecutable(notepadExe.FullName, fi.FullName, TempDir.FullName); 59 | } 60 | 61 | public static void KDiff3(FileInfo oldFi, FileInfo newFi) 62 | { 63 | var kdiffExe = new FileInfo(@"C:\Program Files (x86)\KDiff3\kdiff3.exe"); 64 | var result = ExecutableRunner.RunExecutable(kdiffExe.FullName, oldFi.FullName + " " + newFi.FullName, TempDir.FullName); 65 | } 66 | 67 | public static void Explorer(DirectoryInfo di) 68 | { 69 | Process.Start(di.FullName); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /DocXToPdfConverter/OpenXmlPowerTools/WmlDocument.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.IO.Packaging; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Xml; 11 | using System.Xml.Linq; 12 | using DocumentFormat.OpenXml.Packaging; 13 | 14 | namespace OpenXmlPowerTools 15 | { 16 | public class PtMainDocumentPart : XElement 17 | { 18 | private WmlDocument ParentWmlDocument; 19 | 20 | public PtWordprocessingCommentsPart WordprocessingCommentsPart 21 | { 22 | get 23 | { 24 | using (MemoryStream ms = new MemoryStream(ParentWmlDocument.DocumentByteArray)) 25 | using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false)) 26 | { 27 | WordprocessingCommentsPart commentsPart = wDoc.MainDocumentPart.WordprocessingCommentsPart; 28 | if (commentsPart == null) 29 | return null; 30 | XElement partElement = commentsPart.GetXDocument().Root; 31 | var childNodes = partElement.Nodes().ToList(); 32 | foreach (var item in childNodes) 33 | item.Remove(); 34 | return new PtWordprocessingCommentsPart(this.ParentWmlDocument, commentsPart.Uri, partElement.Name, partElement.Attributes(), childNodes); 35 | } 36 | } 37 | } 38 | 39 | public PtMainDocumentPart(WmlDocument wmlDocument, Uri uri, XName name, params object[] values) 40 | : base(name, values) 41 | { 42 | ParentWmlDocument = wmlDocument; 43 | this.Add( 44 | new XAttribute(PtOpenXml.Uri, uri), 45 | new XAttribute(XNamespace.Xmlns + "pt", PtOpenXml.pt) 46 | ); 47 | } 48 | } 49 | 50 | public class PtWordprocessingCommentsPart : XElement 51 | { 52 | private WmlDocument ParentWmlDocument; 53 | 54 | public PtWordprocessingCommentsPart(WmlDocument wmlDocument, Uri uri, XName name, params object[] values) 55 | : base(name, values) 56 | { 57 | ParentWmlDocument = wmlDocument; 58 | this.Add( 59 | new XAttribute(PtOpenXml.Uri, uri), 60 | new XAttribute(XNamespace.Xmlns + "pt", PtOpenXml.pt) 61 | ); 62 | } 63 | } 64 | 65 | public partial class WmlDocument 66 | { 67 | public PtMainDocumentPart MainDocumentPart 68 | { 69 | get 70 | { 71 | using (MemoryStream ms = new MemoryStream(this.DocumentByteArray)) 72 | using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false)) 73 | { 74 | XElement partElement = wDoc.MainDocumentPart.GetXDocument().Root; 75 | var childNodes = partElement.Nodes().ToList(); 76 | foreach (var item in childNodes) 77 | item.Remove(); 78 | return new PtMainDocumentPart(this, wDoc.MainDocumentPart.Uri, partElement.Name, partElement.Attributes(), childNodes); 79 | } 80 | } 81 | } 82 | 83 | public WmlDocument(WmlDocument other, params XElement[] replacementParts) 84 | : base(other) 85 | { 86 | using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(this)) 87 | { 88 | using (Package package = streamDoc.GetPackage()) 89 | { 90 | foreach (var replacementPart in replacementParts) 91 | { 92 | XAttribute uriAttribute = replacementPart.Attribute(PtOpenXml.Uri); 93 | if (uriAttribute == null) 94 | throw new OpenXmlPowerToolsException("Replacement part does not contain a Uri as an attribute"); 95 | String uri = uriAttribute.Value; 96 | var part = package.GetParts().FirstOrDefault(p => p.Uri.ToString() == uri); 97 | using (Stream partStream = part.GetStream(FileMode.Create, FileAccess.Write)) 98 | using (XmlWriter partXmlWriter = XmlWriter.Create(partStream)) 99 | replacementPart.Save(partXmlWriter); 100 | } 101 | } 102 | this.DocumentByteArray = streamDoc.GetModifiedDocument().DocumentByteArray; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /DocXToPdfConverter/Placeholders.cs: -------------------------------------------------------------------------------- 1 | /* Net-Core-DocX-HTML-To-PDF-Converter 2 | * https://github.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter 3 | * 4 | * 5 | * This application was coded (c) by Dr. Martin Weihrauch 2019 6 | * for Smart In Media GmbH & Co / https://www.smartinmedia.com 7 | * DISTRIBUTED UNDER THE MIT LICENSE 8 | * 9 | * 10 | */ 11 | 12 | 13 | using System.Collections.Generic; 14 | using System.IO; 15 | 16 | 17 | namespace DocXToPdfConverter 18 | { 19 | 20 | public class Placeholders 21 | { 22 | 23 | public Placeholders() 24 | { 25 | NewLineTag = "
"; 26 | TextPlaceholderStartTag = "##"; 27 | TextPlaceholderEndTag = "##"; 28 | TablePlaceholderStartTag = "=="; 29 | TablePlaceholderEndTag = "=="; 30 | ImagePlaceholderStartTag = "++"; 31 | ImagePlaceholderEndTag = "++"; 32 | HyperlinkPlaceholderStartTag = "//"; 33 | HyperlinkPlaceholderEndTag = "//"; 34 | 35 | TextPlaceholders = new Dictionary(); 36 | TablePlaceholders = new List>(); 37 | ImagePlaceholders = new Dictionary(); 38 | HyperlinkPlaceholders = new Dictionary(); 39 | } 40 | 41 | 42 | /// 43 | /// NewLineTags are important only for .docx as input. If you use .html as input, then just use "
" 44 | ///
45 | public string NewLineTag { get; set; } 46 | 47 | 48 | //Start and End Tags can e. g. be both "##" 49 | //A placeholder could be ##TextPlaceHolder## 50 | public string TextPlaceholderStartTag { get; set; } 51 | public string TextPlaceholderEndTag { get; set; } 52 | 53 | public Dictionary TextPlaceholders { get; set; } 54 | 55 | /* 56 | * For tables it works that way: 57 | * 1. If you have a table in the word document, create 1 row with a different Dictionary keys 58 | * Then e. g. you want to have 10 rows in the end, you add 10 values to each array of the Dictionary value 59 | * 60 | * A placeholder could be ==TextPlaceHolder== 61 | */ 62 | //Start and End Tags can e. g. be both "==" 63 | 64 | public string TablePlaceholderStartTag { get; set; } 65 | public string TablePlaceholderEndTag { get; set; } 66 | public List> TablePlaceholders { get; set; } 67 | 68 | 69 | /* 70 | * Important: The MemoryStream may carry an image. 71 | * Allowed file types: JPEG/JPG, BMP, TIFF, GIF, PNG 72 | */ 73 | 74 | // Take different replacement tags here, else there may be collision with the text replacements, e. g. "++" 75 | public string ImagePlaceholderStartTag { get; set; } 76 | public string ImagePlaceholderEndTag { get; set; } 77 | 78 | public Dictionary ImagePlaceholders { get; set; } 79 | 80 | 81 | /* 82 | * Hyperlink replacements 83 | */ 84 | public string HyperlinkPlaceholderStartTag { get; set; } 85 | public string HyperlinkPlaceholderEndTag { get; set; } 86 | 87 | public Dictionary HyperlinkPlaceholders { get; set; } 88 | } 89 | 90 | public class ImageElement 91 | { 92 | public MemoryStream MemStream { get; set; } 93 | public double Dpi { get; set; } // Dots per inch 94 | } 95 | 96 | public class HyperlinkElement 97 | { 98 | public string Link { get; set; } 99 | public string Text { get; set; } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /DocXToPdfConverter/ReportGenerator.cs: -------------------------------------------------------------------------------- 1 | /* Net-Core-DocX-HTML-To-PDF-Converter 2 | * https://github.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter 3 | * 4 | * 5 | * This application was coded (c) by Dr. Martin Weihrauch 2019 6 | * for Smart In Media GmbH & Co / https://www.smartinmedia.com 7 | * DISTRIBUTED UNDER THE MIT LICENSE 8 | * 9 | * 10 | */ 11 | 12 | 13 | using System; 14 | using System.IO; 15 | using DocXToPdfConverter.DocXToPdfHandlers; 16 | 17 | 18 | namespace DocXToPdfConverter 19 | { 20 | public class ReportGenerator 21 | { 22 | private readonly string _locationOfLibreOfficeSoffice; 23 | 24 | // If you dont need conversion to PDF, you can leave the LocationOfLibreOfficeSoffice empty 25 | // For Windows users: this must point to the ".exe" file, so \Path\Path\soffice.exe 26 | public ReportGenerator(string locationOfLibreOfficeSoffice = "") 27 | { 28 | _locationOfLibreOfficeSoffice = locationOfLibreOfficeSoffice; 29 | } 30 | 31 | public void Convert(string inputFile, string outputFile, Placeholders rep = null) 32 | { 33 | if (inputFile.EndsWith(".docx")) 34 | { 35 | if (outputFile.EndsWith(".docx")) 36 | { 37 | GenerateReportFromDocxToDocX(inputFile, outputFile, rep); 38 | } 39 | else if (outputFile.EndsWith(".pdf")) 40 | { 41 | GenerateReportFromDocxToPdf(inputFile, outputFile, rep); 42 | } 43 | else if (outputFile.EndsWith(".html") || outputFile.EndsWith(".htm")) 44 | { 45 | GenerateReportFromDocxToHtml(inputFile, outputFile, rep); 46 | } 47 | } 48 | else if (inputFile.EndsWith(".html") || inputFile.EndsWith(".htm")) 49 | { 50 | if (outputFile.EndsWith(".html") || outputFile.EndsWith(".htm")) 51 | { 52 | GenerateReportFromHtmlToHtml(inputFile, outputFile, rep); 53 | } 54 | else if (outputFile.EndsWith(".docx")) 55 | { 56 | 57 | GenerateReportFromHtmlToDocx(inputFile, outputFile, rep); 58 | } 59 | else if (outputFile.EndsWith(".pdf")) 60 | { 61 | GenerateReportFromHtmlToPdf(inputFile, outputFile, rep); 62 | } 63 | } 64 | } 65 | 66 | 67 | /// 68 | /// Prints the file and optionally generates from placeholders 69 | /// 70 | /// The input file. May be docx, html or pdf 71 | /// optional printername to print on. If this value is empty, the default printer is used 72 | /// A collection of placeholders to be applied if the input file is docx or html. 73 | public void Print(string templateFile, string printerName = null, Placeholders rep = null) 74 | { 75 | if (rep != null) 76 | { 77 | if (templateFile.EndsWith(".docx")) 78 | { 79 | PrintDocx(templateFile, printerName, rep); 80 | } 81 | else if (templateFile.EndsWith(".html") || templateFile.EndsWith(".htm")) 82 | { 83 | PrintHtml(templateFile, printerName, rep); 84 | } 85 | else 86 | { 87 | LibreOfficeWrapper.Print(templateFile, printerName, _locationOfLibreOfficeSoffice); 88 | } 89 | } 90 | else 91 | { 92 | LibreOfficeWrapper.Print(templateFile, printerName, _locationOfLibreOfficeSoffice); 93 | } 94 | } 95 | 96 | 97 | private void PrintDocx(string templateFile, string printername, Placeholders rep) 98 | { 99 | var docx = new DocXHandler(templateFile, rep); 100 | var ms = docx.ReplaceAll(); 101 | var tempFileToPrint = Path.ChangeExtension(Path.GetTempFileName(), ".docx"); 102 | StreamHandler.WriteMemoryStreamToDisk(ms, tempFileToPrint); 103 | LibreOfficeWrapper.Print(tempFileToPrint, printername, _locationOfLibreOfficeSoffice); 104 | File.Delete(tempFileToPrint); 105 | } 106 | 107 | 108 | private void PrintHtml(string templateFile, string printername, Placeholders rep) 109 | { 110 | var htmlContent = File.ReadAllText(templateFile); 111 | htmlContent = HtmlHandler.ReplaceAll(htmlContent, rep); 112 | var tempFileToPrint = Path.ChangeExtension(Path.GetTempFileName(), ".html"); 113 | File.WriteAllText(tempFileToPrint, htmlContent); 114 | LibreOfficeWrapper.Print(tempFileToPrint, printername, _locationOfLibreOfficeSoffice); 115 | File.Delete(tempFileToPrint); 116 | } 117 | 118 | 119 | //string docxSource = filename with path 120 | private void GenerateReportFromDocxToDocX(string docxSource, string docxTarget, Placeholders rep) 121 | { 122 | var docx = new DocXHandler(docxSource, rep); 123 | var ms = docx.ReplaceAll(); 124 | StreamHandler.WriteMemoryStreamToDisk(ms, docxTarget); 125 | } 126 | 127 | 128 | ////string docxSource = filename with path 129 | private void GenerateReportFromDocxToPdf(string docxSource, string pdfTarget, Placeholders rep) 130 | { 131 | var docx = new DocXHandler(docxSource, rep); 132 | var ms = docx.ReplaceAll(); 133 | var tmpFile = Path.Combine(Path.GetDirectoryName(pdfTarget), Path.GetFileNameWithoutExtension(pdfTarget) + Guid.NewGuid().ToString().Substring(0, 10) + ".docx"); 134 | StreamHandler.WriteMemoryStreamToDisk(ms, tmpFile); 135 | LibreOfficeWrapper.Convert(tmpFile, pdfTarget, _locationOfLibreOfficeSoffice); 136 | File.Delete(tmpFile); 137 | } 138 | 139 | 140 | private void GenerateReportFromDocxToHtml(string docxSource, string htmlTarget, Placeholders rep) 141 | { 142 | var docx = new DocXHandler(docxSource, rep); 143 | var ms = docx.ReplaceAll(); 144 | var tmpFile = Path.Combine(Path.GetDirectoryName(htmlTarget), Path.GetFileNameWithoutExtension(docxSource) + Guid.NewGuid().ToString().Substring(0, 10) + ".docx"); 145 | StreamHandler.WriteMemoryStreamToDisk(ms, tmpFile); 146 | LibreOfficeWrapper.Convert(tmpFile, htmlTarget, _locationOfLibreOfficeSoffice); 147 | File.Delete(tmpFile); 148 | } 149 | 150 | 151 | private void GenerateReportFromHtmlToHtml(string htmlSource, string htmlTarget, Placeholders rep) 152 | { 153 | string html = File.ReadAllText(htmlSource); 154 | html = HtmlHandler.ReplaceAll(html, rep); 155 | File.WriteAllText(htmlTarget, html); 156 | } 157 | 158 | 159 | //string htmlSource = filename to a *.html/*.htm file with path 160 | private void GenerateReportFromHtmlToDocx(string htmlSource, string docxTarget, Placeholders rep) 161 | { 162 | var tmpFile = Path.Combine(Path.GetDirectoryName(docxTarget), Path.GetFileNameWithoutExtension(htmlSource) + Guid.NewGuid().ToString().Substring(0, 10) + ".html"); 163 | GenerateReportFromHtmlToHtml(htmlSource, tmpFile, rep); 164 | LibreOfficeWrapper.Convert(tmpFile, docxTarget, _locationOfLibreOfficeSoffice); 165 | File.Delete(tmpFile); 166 | } 167 | 168 | 169 | //This requires the HtmlAgilityPack 170 | //string htmlSource = filename to a *.html/*.htm file with path 171 | private void GenerateReportFromHtmlToPdf(string htmlSource, string pdfTarget, Placeholders rep) 172 | { 173 | var tmpFile = Path.Combine(Path.GetDirectoryName(pdfTarget), Path.GetFileNameWithoutExtension(htmlSource) + Guid.NewGuid().ToString().Substring(0, 10) + ".html"); 174 | GenerateReportFromHtmlToHtml(htmlSource, tmpFile, rep); 175 | LibreOfficeWrapper.Convert(tmpFile, pdfTarget, _locationOfLibreOfficeSoffice); 176 | File.Delete(tmpFile); 177 | } 178 | 179 | 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /ExampleApplication/ExampleApplication.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | PreserveNewest 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | PreserveNewest 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | PreserveNewest 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ExampleApplication/ProductImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter/814a76ce8985d26d3d3d3acfef7d7b5d8628a1a6/ExampleApplication/ProductImage.jpg -------------------------------------------------------------------------------- /ExampleApplication/Program.cs: -------------------------------------------------------------------------------- 1 | /* Net-Core-DocX-HTML-To-PDF-Converter 2 | * https://github.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter 3 | * 4 | * 5 | * This application was coded (c) by Dr. Martin Weihrauch 2019 6 | * for Smart In Media GmbH & Co / https://www.smartinmedia.com 7 | * DISTRIBUTED UNDER THE MIT LICENSE 8 | * 9 | * 10 | */ 11 | 12 | 13 | using System.Collections.Generic; 14 | using System.IO; 15 | using System.Reflection; 16 | using DocXToPdfConverter; 17 | using DocXToPdfConverter.DocXToPdfHandlers; 18 | 19 | namespace ExampleApplication 20 | { 21 | public static class Program 22 | { 23 | 24 | public static void Main(string[] args) 25 | { 26 | /* 27 | * Your TODO: 28 | * 1. Enter your LibreOffice path below 29 | * 2. Have an input file with placeholders ready, in docx or HTML. Create your own placeholders (from the class Placeholders) 30 | * 3. Create an object for "ReportGenerator" 31 | * 4. Execute the method "Convert" on the object ReportGenerator. 32 | * Possible conversions: from HTML or from DOCX to PDF, HTML, DOCX 33 | * 34 | */ 35 | 36 | //Enter the location of your LibreOffice soffice.exe below, full path with "soffice.exe" at the end 37 | //or anything you have in Linux... 38 | 39 | string locationOfLibreOfficeSoffice = 40 | @"F:\PortableApps\LibreOfficePortable\App\libreoffice\program\soffice.exe"; 41 | 42 | 43 | //This is only to get this example to work (find the word docx and the html file, which were 44 | //shipped with this). 45 | string executableLocation = Path.GetDirectoryName( 46 | Assembly.GetExecutingAssembly().Location); 47 | 48 | //Here are the 2 test files as input. They contain placeholders 49 | string docxLocation = Path.Combine(executableLocation, "Test-Template.docx"); 50 | string htmlLocation = Path.Combine(executableLocation, "Test-HTML-page.html"); 51 | 52 | /* 53 | * 54 | * You have to create Placeholder objects from the Placeholders.cs class 55 | * Only create, what you need. 56 | * Here, we create one object for a docx conversion example and one 57 | * for an HTML conversion example. 58 | * 59 | * DOCX OBJECT 60 | * 61 | */ 62 | 63 | 64 | //Prepare texts, which you want to insert into the custom fields in the template (remember 65 | //to use start and stop tags. 66 | //NOTE that line breaks can be inserted as what you define them in ReplacementDictionaries.NewLineTag (here we use
). 67 | 68 | var placeholders = new Placeholders 69 | { 70 | NewLineTag = "
", 71 | TextPlaceholderStartTag = "##", 72 | TextPlaceholderEndTag = "##", 73 | TablePlaceholderStartTag = "==", 74 | TablePlaceholderEndTag = "==", 75 | ImagePlaceholderStartTag = "++", 76 | ImagePlaceholderEndTag = "++", 77 | 78 | //You should be able to also use other OpenXML tags in your strings 79 | TextPlaceholders = new Dictionary 80 | { 81 | {"Name", "Mr. Miller" }, 82 | {"Street", "89 Brook St" }, 83 | {"City", "Brookline MA 02115
USA" }, 84 | {"InvoiceNo", "5" }, 85 | {"Total", "U$ 4,500" }, 86 | {"Date", "28 Jul 2019" }, 87 | {"Website", "www.smartinmedia.com" } 88 | }, 89 | 90 | HyperlinkPlaceholders = new Dictionary 91 | { 92 | {"Website", new HyperlinkElement{ Link= "http://www.smartinmedia.com", Text="www.smartinmedia.com" } } 93 | }, 94 | 95 | //Table ROW replacements are a little bit more complicated: With them you can 96 | //fill out only one table row in a table and it will add as many rows as you 97 | //need, depending on the string Array. 98 | TablePlaceholders = new List> 99 | { 100 | new Dictionary() 101 | { 102 | {"Name", new string[]{ "Homer Simpson", "Mr. Burns", "Mr. Smithers" }}, 103 | {"Department", new string[]{ "Power Plant", "Administration", "Administration" }}, 104 | {"Responsibility", new string[]{ "Oversight", "CEO", "Assistant" }}, 105 | {"Telephone number", new string[]{ "888-234-2353", "888-295-8383", "888-848-2803" }} 106 | }, 107 | new Dictionary() 108 | { 109 | {"Qty", new string[]{ "2", "5", "7" }}, 110 | {"Product", new string[]{ "Software development", "Customization", "Travel expenses" }}, 111 | {"Price", new string[]{ "U$ 2,000", "U$ 1,000", "U$ 1,500" }}, 112 | } 113 | } 114 | }; 115 | 116 | //You have to add the images as a memory stream to the Dictionary! Place a key (placeholder) into the docx template. 117 | //There is a method to read files as memory streams (GetFileAsMemoryStream) 118 | //We already did that with <+++>ProductImage<+++> 119 | 120 | var productImage = StreamHandler.GetFileAsMemoryStream(Path.Combine(executableLocation, "ProductImage.jpg")); 121 | 122 | var qrImage = StreamHandler.GetFileAsMemoryStream(Path.Combine(executableLocation, "QRCode.PNG")); 123 | 124 | var productImageElement = new ImageElement() { Dpi = 96, MemStream = productImage }; 125 | var qrImageElement = new ImageElement() { Dpi = 300, MemStream = qrImage }; 126 | 127 | placeholders.ImagePlaceholders = new Dictionary 128 | { 129 | {"QRCode", qrImageElement }, 130 | {"ProductImage", productImageElement } 131 | }; 132 | 133 | /* 134 | * 135 | * 136 | * Execution of conversion tests 137 | * 138 | * 139 | */ 140 | 141 | //Most important: give the full path to the soffice.exe file including soffice.exe. 142 | //Don't know how that would be named on Linux... 143 | var test = new ReportGenerator(locationOfLibreOfficeSoffice); 144 | 145 | //Convert from HTML to HTML 146 | test.Convert(htmlLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-HTML-page-out.html"), placeholders); 147 | 148 | //Convert from HTML to PDF 149 | test.Convert(htmlLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-HTML-page-out.pdf"), placeholders); 150 | 151 | //Convert from HTML to DOCX 152 | test.Convert(htmlLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-HTML-page-out.docx"), placeholders); 153 | 154 | //Convert from DOCX to DOCX 155 | test.Convert(docxLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-Template-out.docx"), placeholders); 156 | 157 | //Convert from DOCX to HTML 158 | test.Convert(docxLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-Template-out.html"), placeholders); 159 | 160 | //Convert from DOCX to PDF 161 | test.Convert(docxLocation, Path.Combine(Path.GetDirectoryName(htmlLocation), "Test-Template-out.pdf"), placeholders); 162 | 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /ExampleApplication/QRCode.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter/814a76ce8985d26d3d3d3acfef7d7b5d8628a1a6/ExampleApplication/QRCode.PNG -------------------------------------------------------------------------------- /ExampleApplication/Test-HTML-page.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Template Engine DocX HTML to PDF 7 | 8 | 9 |

Created by ©Smart In Media 2019, //Website//

10 |

11 |

This is a simple example for a HTML template to use with the NET Core DocX/HTML to PDF Converter

12 |

13 | To:
##Name##
##Street##
##City## 14 | 15 |

16 |

Invoice ###InvoiceNo##

17 |

##Date##

18 | 19 |

20 | It has the following features: 21 | Conversion from / to these formats: 22 |

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
QuantityProductPricing
==Qty====Product====Price==
##Total##
42 | 43 |

Contact us through our website (scan QR code):

44 |
++QRCode++
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
NameDeptResponsibilityTel
==Name====Department====Responsibility====Telephone number==
61 | 62 | 63 | -------------------------------------------------------------------------------- /ExampleApplication/Test-Template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter/814a76ce8985d26d3d3d3acfef7d7b5d8628a1a6/ExampleApplication/Test-Template.docx -------------------------------------------------------------------------------- /ExampleApplication/smartinmedia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartinmedia/Net-Core-DocX-HTML-To-PDF-Converter/814a76ce8985d26d3d3d3acfef7d7b5d8628a1a6/ExampleApplication/smartinmedia.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Smart In Media GmbH & Co. KG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------