├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── cla.yml │ ├── dotnet.yml │ └── stale.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json ├── tasks.json └── updateNuget.sh ├── LICENSE ├── OpenXmlToHtml.sln ├── OpenXmlToHtml ├── ExportImageHandler.cs ├── IOpenXmlToHtml.cs ├── NugetIcon.png ├── OpenXmlToHtml.cs ├── OpenXmlToHtml.csproj ├── OpenXmlToHtml.snk ├── PageBreakHandler.cs ├── SymbolHandler.cs ├── TextSymbolToUnicodeHandler.cs ├── Tooling │ └── Linux.cs ├── WebSafeFontsHandler.cs └── docs │ └── nugetReadme.md ├── OpenXmlToHtmlCli ├── OpenXmlToHtmlCli.csproj ├── Program.cs └── Properties │ └── launchSettings.json ├── OpenXmlToHtmlOpenApi ├── .config │ └── dotnet-tools.json ├── Azure │ └── LinuxSpecificContainerSetup.cs ├── Controllers │ └── OpenXmlConverterController.cs ├── OpenXmlToHtmlOpenApi.csproj ├── OpenXmlToHtmlOpenApi │ └── OpenXmlToHtmlOpenApi.xml ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── appsettings.Development.json └── appsettings.json ├── OpenXmlToHtmlOpenApiTests ├── .editorconfig ├── AzureTests.cs ├── OpenXmlConverterControllerIntegrativeTests.cs ├── OpenXmlToHtmlOpenApiTests.csproj ├── TestInput │ └── BasicTextFormated.docx └── WebApplicationFactory.cs ├── OpenXmlToHtmlTests ├── .editorconfig ├── BreakHandlerAdapterTests.cs ├── ExpectedTestOutcome │ ├── BasicTextFormated.docx.png │ ├── EmptyDocument.docx.png │ ├── Font.docx.png │ ├── Font.docx.png.diff.linux.png │ ├── Font.docx.png.diff.win.png │ ├── Images.docx.png │ ├── Images.docx.png.diff.linux.png │ ├── SymbolRibbon.docx.png │ ├── Symbols.docx.png │ ├── Wingdings.docx.png │ └── WingdingsSymbols.docx.png ├── ExportImageHandlerTests.cs ├── OpenXmlToHtmlIntegrationTests.cs ├── OpenXmlToHtmlTests.csproj ├── SymbolHandlerTests.cs ├── TestInfrastructure │ └── DocumentAsserter.cs ├── TestInput │ ├── BasicTextFormated.docx │ ├── EmptyDocument.docx │ ├── Font.docx │ ├── Images.docx │ ├── SymbolRibbon.docx │ ├── Symbols.docx │ ├── TestInput.png │ ├── TwoPages.docx │ ├── Wingdings.docx │ └── WingdingsSymbols.pdf ├── TextSymbolToUnicodeHandlerTests.cs └── WebSafeFontsHandlerTests.cs ├── README.md ├── cla.md ├── signatures └── version1 │ └── cla.json └── testenvironments.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | [*.sh] 5 | end_of_line = lf 6 | 7 | # C# files 8 | [*.cs] 9 | 10 | #### Core EditorConfig Options #### 11 | 12 | # Indentation and spacing 13 | indent_size = 4 14 | indent_style = space 15 | tab_width = 4 16 | 17 | # New line preferences 18 | end_of_line = crlf 19 | insert_final_newline = false 20 | 21 | #### .NET Coding Conventions #### 22 | 23 | # Organize usings 24 | dotnet_separate_import_directive_groups = false 25 | dotnet_sort_system_directives_first = false 26 | 27 | # this. and Me. preferences 28 | dotnet_style_qualification_for_event = false:silent 29 | dotnet_style_qualification_for_field = false:silent 30 | dotnet_style_qualification_for_method = false:silent 31 | dotnet_style_qualification_for_property = false:silent 32 | 33 | # Language keywords vs BCL types preferences 34 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 35 | dotnet_style_predefined_type_for_member_access = true:silent 36 | 37 | # Parentheses preferences 38 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 39 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 40 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 41 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 42 | 43 | # Modifier preferences 44 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 45 | 46 | # Expression-level preferences 47 | dotnet_style_coalesce_expression = true:suggestion 48 | dotnet_style_collection_initializer = true:suggestion 49 | dotnet_style_explicit_tuple_names = true:suggestion 50 | dotnet_style_null_propagation = true:suggestion 51 | dotnet_style_object_initializer = true:suggestion 52 | dotnet_style_prefer_auto_properties = true:silent 53 | dotnet_style_prefer_compound_assignment = true:suggestion 54 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 55 | dotnet_style_prefer_conditional_expression_over_return = true:silent 56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 57 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 58 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 59 | dotnet_style_prefer_simplified_interpolation = true:suggestion 60 | 61 | # Field preferences 62 | dotnet_style_readonly_field = true:suggestion 63 | 64 | # Parameter preferences 65 | dotnet_code_quality_unused_parameters = all:suggestion 66 | 67 | #### C# Coding Conventions #### 68 | 69 | # var preferences 70 | csharp_style_var_elsewhere = true:silent 71 | csharp_style_var_for_built_in_types = true:silent 72 | csharp_style_var_when_type_is_apparent = true:silent 73 | 74 | # Expression-bodied members 75 | csharp_style_expression_bodied_accessors = true:silent 76 | csharp_style_expression_bodied_constructors = false:silent 77 | csharp_style_expression_bodied_indexers = true:silent 78 | csharp_style_expression_bodied_lambdas = true:silent 79 | csharp_style_expression_bodied_local_functions = false:silent 80 | csharp_style_expression_bodied_methods = false:silent 81 | csharp_style_expression_bodied_operators = false:silent 82 | csharp_style_expression_bodied_properties = true:silent 83 | 84 | # Pattern matching preferences 85 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 86 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 87 | csharp_style_prefer_switch_expression = true:suggestion 88 | 89 | # Null-checking preferences 90 | csharp_style_conditional_delegate_call = true:suggestion 91 | 92 | # Modifier preferences 93 | csharp_prefer_static_local_function = true:suggestion 94 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent 95 | 96 | # Code-block preferences 97 | csharp_prefer_braces = true:warning 98 | csharp_prefer_simple_using_statement = true:suggestion 99 | 100 | # Expression-level preferences 101 | csharp_prefer_simple_default_expression = true:suggestion 102 | csharp_style_deconstructed_variable_declaration = true:suggestion 103 | csharp_style_inlined_variable_declaration = true:suggestion 104 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 105 | csharp_style_prefer_index_operator = true:suggestion 106 | csharp_style_prefer_range_operator = true:suggestion 107 | csharp_style_throw_expression = true:suggestion 108 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 109 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 110 | 111 | # 'using' directive preferences 112 | csharp_using_directive_placement = outside_namespace:silent 113 | 114 | #### C# Formatting Rules #### 115 | 116 | # New line preferences 117 | csharp_new_line_before_catch = true 118 | csharp_new_line_before_else = true 119 | csharp_new_line_before_finally = true 120 | csharp_new_line_before_members_in_anonymous_types = true 121 | csharp_new_line_before_members_in_object_initializers = true 122 | csharp_new_line_before_open_brace = all 123 | csharp_new_line_between_query_expression_clauses = true 124 | 125 | # Indentation preferences 126 | csharp_indent_block_contents = true 127 | csharp_indent_braces = false 128 | csharp_indent_case_contents = true 129 | csharp_indent_case_contents_when_block = true 130 | csharp_indent_labels = one_less_than_current 131 | csharp_indent_switch_labels = true 132 | 133 | # Space preferences 134 | csharp_space_after_cast = false 135 | csharp_space_after_colon_in_inheritance_clause = true 136 | csharp_space_after_comma = true 137 | csharp_space_after_dot = false 138 | csharp_space_after_keywords_in_control_flow_statements = true 139 | csharp_space_after_semicolon_in_for_statement = true 140 | csharp_space_around_binary_operators = before_and_after 141 | csharp_space_around_declaration_statements = false 142 | csharp_space_before_colon_in_inheritance_clause = true 143 | csharp_space_before_comma = false 144 | csharp_space_before_dot = false 145 | csharp_space_before_open_square_brackets = false 146 | csharp_space_before_semicolon_in_for_statement = false 147 | csharp_space_between_empty_square_brackets = false 148 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 149 | csharp_space_between_method_call_name_and_opening_parenthesis = false 150 | csharp_space_between_method_call_parameter_list_parentheses = false 151 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 152 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 153 | csharp_space_between_method_declaration_parameter_list_parentheses = false 154 | csharp_space_between_parentheses = false 155 | csharp_space_between_square_brackets = false 156 | 157 | # Wrapping preferences 158 | csharp_preserve_single_line_blocks = true 159 | csharp_preserve_single_line_statements = true 160 | 161 | #### Naming styles #### 162 | 163 | # Naming rules 164 | 165 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 166 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 167 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 168 | 169 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 170 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 171 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 172 | 173 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 174 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 175 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 176 | 177 | # Symbol specifications 178 | 179 | dotnet_naming_symbols.interface.applicable_kinds = interface 180 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 181 | dotnet_naming_symbols.interface.required_modifiers = 182 | 183 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 184 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 185 | dotnet_naming_symbols.types.required_modifiers = 186 | 187 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 188 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 189 | dotnet_naming_symbols.non_field_members.required_modifiers = 190 | 191 | # Naming styles 192 | 193 | dotnet_naming_style.pascal_case.required_prefix = 194 | dotnet_naming_style.pascal_case.required_suffix = 195 | dotnet_naming_style.pascal_case.word_separator = 196 | dotnet_naming_style.pascal_case.capitalization = pascal_case 197 | 198 | dotnet_naming_style.begins_with_i.required_prefix = I 199 | dotnet_naming_style.begins_with_i.required_suffix = 200 | dotnet_naming_style.begins_with_i.word_separator = 201 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 202 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # More details are here: https://help.github.com/articles/about-codeowners/ 5 | 6 | # The '*' pattern is global owners. 7 | 8 | # Order is important. The last matching pattern has the most precedence. 9 | # The folders are ordered as follows: 10 | 11 | # In each subsection folders are ordered first by depth, then alphabetically. 12 | # This should make it easy to add new rules without breaking existing ones. 13 | 14 | # Global rule: 15 | * @stesee -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "docker" 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | rebase-strategy: auto 8 | 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: weekly 13 | rebase-strategy: auto 14 | 15 | - package-ecosystem: "npm" 16 | directory: "/" 17 | schedule: 18 | interval: weekly 19 | rebase-strategy: auto 20 | 21 | - package-ecosystem: "nuget" 22 | directory: "/" 23 | schedule: 24 | interval: "daily" 25 | ignore: 26 | - dependency-name: "nunit" 27 | - dependency-name: "coverlet.collector" 28 | - dependency-name: "SonarAnalyzer.CSharp" 29 | - dependency-name: "AngleSharp" 30 | - dependency-name: "Microsoft.NET.Test.Sdk" 31 | - dependency-name: "Microsoft.AspNetCore.Mvc.Testing" 32 | - dependency-name: "Moq" 33 | - dependency-name: "xunit" 34 | - dependency-name: "xunit.runner.visualstudio" 35 | - dependency-name: "MSTest.TestAdapter" 36 | - dependency-name: "MSTest.TestFramework" 37 | - dependency-name: "Microsoft.AspNetCore.Identity.UI" 38 | - dependency-name: "Microsoft.VisualStudio.Web.CodeGeneration.Design" 39 | - dependency-name: "Codeuctivity.ImageSharpCompare" 40 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | name: "CLA Assistant" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened,closed,synchronize] 7 | 8 | jobs: 9 | CLAssistant: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "CLA Assistant" 13 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 14 | uses: cla-assistant/github-action@v2.6.1 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | # the below token should have repo scope and must be manually added by you in the repository's secret 18 | PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }} 19 | with: 20 | path-to-signatures: 'signatures/version1/cla.json' 21 | path-to-document: 'https://github.com/Codeuctivity/OpenXmlToHtml/blob/main/cla.md' # e.g. a CLA or a DCO document 22 | # branch should not be protected 23 | branch: 'cla' 24 | allowlist: dependabot[bot],stesee 25 | 26 | #below are the optional inputs - If the optional inputs are not given, then default values will be taken 27 | #remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) 28 | #remote-repository-name: enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository) 29 | #create-file-commit-message: 'For example: Creating file for storing CLA Signatures' 30 | #signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo' 31 | #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign' 32 | #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' 33 | #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' 34 | #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) 35 | #use-dco-flag: true - If you are using DCO instead of CLA -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET build and test 2 | env: 3 | CURRENT_VERSION: 2.0.${{ github.run_number }} 4 | LAST_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} 5 | 6 | on: 7 | push: 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest, windows-latest] 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v4 20 | with: 21 | dotnet-version: 8.0.x 22 | - name: Restore dependencies 23 | run: dotnet restore 24 | - name: Build 25 | run: dotnet build --configuration Release --no-restore 26 | - name: Test 27 | run: dotnet test --no-build --verbosity normal --configuration Release 28 | - name: Publish Unit Test Results 29 | uses: actions/upload-artifact@v4 30 | if: failure() 31 | with: 32 | name: TestResult 33 | path: "TestResult/**/*" 34 | 35 | deployRelease: 36 | if: ${{ github.ref == 'refs/heads/release' }} 37 | runs-on: windows-latest 38 | needs: build 39 | steps: 40 | - uses: actions/checkout@v4 41 | - name: Setup .NET 42 | uses: actions/setup-dotnet@v4 43 | with: 44 | dotnet-version: 8.0.x 45 | - name: Restore dependencies 46 | run: dotnet restore 47 | - name: Build 48 | run: | 49 | dotnet build --configuration Release --no-restore 50 | dotnet publish ./OpenXmlToHtmlOpenApi -c Release /p:WebPublishMethod=Package 51 | dotnet publish ./OpenXmlToHtmlCli -c Release 52 | - name: Zip 53 | uses: thedoctor0/zip-release@main 54 | with: 55 | path: .\OpenXmlToHtmlCli\bin\Release\net8.0\win-x64\publish 56 | type: 'zip' 57 | filename: 'OpenXmlToHtmlCli.zip' 58 | - name: NugetPush 59 | env: 60 | NUGET_TOKEN_EXISTS: ${{ secrets.NUGET_TOKEN }} 61 | if: env.NUGET_TOKEN_EXISTS != '' 62 | run: | 63 | dotnet nuget push .\OpenXmlToHtml\bin\Release\*.nupkg --skip-duplicate --api-key ${{secrets.NUGET_TOKEN}} --source https://api.nuget.org/v3/index.json 64 | - name: Github Prelease 65 | shell: bash 66 | env: 67 | GITHUB_TOKEN: ${{ github.TOKEN }} 68 | if: env.GITHUB_TOKEN != '' 69 | run: | 70 | gh release create ${{env.CURRENT_VERSION}} ./OpenXmlToHtmlOpenApi/bin/Release/net8.0/OpenXmlToHtmlOpenApi.zip ./OpenXmlToHtmlCli.zip ./OpenXmlToHtml/bin/Release/*.*nupkg --generate-notes 71 | 72 | 73 | deployTest: 74 | if: ${{ github.ref == 'refs/heads/main' }} 75 | runs-on: windows-latest 76 | needs: build 77 | steps: 78 | - uses: actions/checkout@v4 79 | - name: Setup .NET 80 | uses: actions/setup-dotnet@v4 81 | with: 82 | dotnet-version: 8.0.x 83 | - name: Restore dependencies 84 | run: dotnet restore 85 | - name: Build 86 | run: | 87 | dotnet build --configuration Release --no-restore 88 | dotnet publish ./OpenXmlToHtmlOpenApi -c Release /p:WebPublishMethod=Package 89 | dotnet publish ./OpenXmlToHtmlCli -c Release 90 | - name: Zip 91 | uses: thedoctor0/zip-release@main 92 | with: 93 | path: .\OpenXmlToHtmlCli\bin\Release\net8.0\win-x64\publish 94 | type: 'zip' 95 | filename: 'OpenXmlToHtmlCli.zip' 96 | - name: NugetPush 97 | env: 98 | NUGET_TOKEN_EXISTS: ${{ secrets.NUGET_TEST_TOKEN }} 99 | if: env.NUGET_TOKEN_EXISTS != '' 100 | run: | 101 | dotnet nuget push .\OpenXmlToHtml\bin\Release\*.nupkg --skip-duplicate --api-key ${{secrets.NUGET_TEST_TOKEN}} --source https://apiint.nugettest.org/v3/index.json 102 | - name: Github Prelease 103 | shell: bash 104 | env: 105 | GITHUB_TOKEN: ${{ github.TOKEN }} 106 | if: env.GITHUB_TOKEN != '' 107 | run: | 108 | gh release create ${{env.CURRENT_VERSION}} ./OpenXmlToHtmlOpenApi/bin/Release/net8.0/OpenXmlToHtmlOpenApi.zip ./OpenXmlToHtmlCli.zip ./OpenXmlToHtml/bin/Release/*.*nupkg --prerelease --generate-notes 109 | 110 | deployAzure: 111 | if: ${{ github.ref == 'refs/heads/release' || github.ref == 'refs/heads/AzureAppPublishUsingGithubActions'}} 112 | runs-on: ubuntu-latest 113 | needs: build 114 | steps: 115 | - uses: actions/checkout@v4 116 | - name: Setup .NET 117 | uses: actions/setup-dotnet@v4 118 | with: 119 | dotnet-version: 8.0.x 120 | - name: Restore dependencies 121 | run: dotnet restore 122 | - name: Build 123 | run: dotnet build --configuration Release --no-restore 124 | - name: Publish 125 | run: dotnet publish ./OpenXmlToHtmlOpenApi/OpenXmlToHtmlOpenApi.csproj --configuration Release -o ./Publish 126 | - uses: azure/webapps-deploy@v3 127 | with: 128 | publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }} 129 | package: './Publish' 130 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 2 | # 3 | # You can adjust the behavior by modifying this file. 4 | # For more information, see: 5 | # https://github.com/actions/stale 6 | name: Mark stale issues and pull requests 7 | 8 | on: 9 | schedule: 10 | - cron: '44 1 * * *' 11 | 12 | jobs: 13 | stale: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | pull-requests: write 19 | 20 | steps: 21 | - uses: actions/stale@v9 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | stale-issue-message: 'Stale issue message' 25 | stale-pr-message: 'Stale pull request message' 26 | stale-issue-label: 'no-issue-activity' 27 | stale-pr-label: 'no-pr-activity' 28 | -------------------------------------------------------------------------------- /.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 | # JustCode is a .NET coding add-in 131 | .JustCode 132 | 133 | # TeamCity is a build add-in 134 | _TeamCity* 135 | 136 | # DotCover is a Code Coverage Tool 137 | *.dotCover 138 | 139 | # AxoCover is a Code Coverage Tool 140 | .axoCover/* 141 | !.axoCover/settings.json 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 | OpenXmlToHtmlOpenApi/.local-chromium -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-dotnettools.csharp" 4 | ] 5 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "OpenXmlToHtmlOpenApi", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | "program": "${workspaceFolder}/bin/Debug/net5/OpenXmlToHtmlOpenApi.dll", 13 | "args": [], 14 | "cwd": "${workspaceFolder}", 15 | "stopAtEntry": false, 16 | "serverReadyAction": { 17 | "action": "openExternally", 18 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 19 | }, 20 | "env": { 21 | "ASPNETCORE_ENVIRONMENT": "Development" 22 | }, 23 | "sourceFileMap": { 24 | "/Views": "${workspaceFolder}/Views" 25 | } 26 | }, 27 | { 28 | "name": ".NET Core Attach", 29 | "type": "coreclr", 30 | "request": "attach", 31 | "processId": "${command:pickProcess}" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Codacy", 4 | "Codeuctivity", 5 | "Nuget", 6 | "Nunit", 7 | "Rasterized", 8 | "Rgba", 9 | "nupkg", 10 | "snupkg" 11 | ], 12 | "editor.formatOnSave": true 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/OpenXmlToHtmlTests/OpenXmlToHtmlTests.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/OpenXmlToHtmlTests/OpenXmlToHtmlTests.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/OpenXmlToHtmlTests/OpenXmlToHtmlTests.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | }, 41 | { 42 | "label": "update nuget", 43 | "command": "${workspaceFolder}/.vscode/updateNuget.sh", 44 | "args": [], 45 | "group": "build", 46 | "presentation": { 47 | "reveal": "always" 48 | }, 49 | "problemMatcher": "$msCompile" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /.vscode/updateNuget.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | regex='PackageReference Include="([^"]*)" Version="([^"]*)"' 3 | find . -name "*.*proj" | while read proj; do 4 | while read line; do 5 | if [[ $line =~ $regex ]]; then 6 | name="${BASH_REMATCH[1]}" 7 | version="${BASH_REMATCH[2]}" 8 | if [[ $version != *-* ]]; then 9 | dotnet add "$proj" package "$name" 10 | fi 11 | fi 12 | done <"$proj" 13 | done 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OpenXmlToHtml.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32526.322 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenXmlToHtml", "OpenXmlToHtml\OpenXmlToHtml.csproj", "{D3FE5D0D-4ED4-41D4-B3EF-116F5140B1CB}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FC3F4498-FA14-48A8-AA41-8D9095A0844F}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | cla.md = cla.md 12 | .github\workflows\cla.yml = .github\workflows\cla.yml 13 | .github\dependabot.yml = .github\dependabot.yml 14 | .github\workflows\dotnet.yml = .github\workflows\dotnet.yml 15 | README.md = README.md 16 | .github\workflows\stale.yml = .github\workflows\stale.yml 17 | EndProjectSection 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenXmlToHtmlTests", "OpenXmlToHtmlTests\OpenXmlToHtmlTests.csproj", "{A1A7AF98-904A-4B85-B9FF-3DC985DEA382}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenXmlToHtmlCli", "OpenXmlToHtmlCli\OpenXmlToHtmlCli.csproj", "{BE60CFAD-B5B5-40AE-B459-8AB75886E717}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenXmlToHtmlOpenApi", "OpenXmlToHtmlOpenApi\OpenXmlToHtmlOpenApi.csproj", "{292B4D3E-0821-4907-A875-52DF1BE21DE9}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenXmlToHtmlOpenApiTests", "OpenXmlToHtmlOpenApiTests\OpenXmlToHtmlOpenApiTests.csproj", "{B07C63A5-87D2-4620-BD60-04073AED6AC0}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Release|Any CPU = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {D3FE5D0D-4ED4-41D4-B3EF-116F5140B1CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {D3FE5D0D-4ED4-41D4-B3EF-116F5140B1CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {D3FE5D0D-4ED4-41D4-B3EF-116F5140B1CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {D3FE5D0D-4ED4-41D4-B3EF-116F5140B1CB}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {A1A7AF98-904A-4B85-B9FF-3DC985DEA382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {A1A7AF98-904A-4B85-B9FF-3DC985DEA382}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {A1A7AF98-904A-4B85-B9FF-3DC985DEA382}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {A1A7AF98-904A-4B85-B9FF-3DC985DEA382}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {BE60CFAD-B5B5-40AE-B459-8AB75886E717}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {BE60CFAD-B5B5-40AE-B459-8AB75886E717}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {BE60CFAD-B5B5-40AE-B459-8AB75886E717}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {BE60CFAD-B5B5-40AE-B459-8AB75886E717}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {292B4D3E-0821-4907-A875-52DF1BE21DE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {292B4D3E-0821-4907-A875-52DF1BE21DE9}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {292B4D3E-0821-4907-A875-52DF1BE21DE9}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {292B4D3E-0821-4907-A875-52DF1BE21DE9}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {B07C63A5-87D2-4620-BD60-04073AED6AC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {B07C63A5-87D2-4620-BD60-04073AED6AC0}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {B07C63A5-87D2-4620-BD60-04073AED6AC0}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {B07C63A5-87D2-4620-BD60-04073AED6AC0}.Release|Any CPU.Build.0 = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(SolutionProperties) = preSolution 55 | HideSolutionNode = FALSE 56 | EndGlobalSection 57 | GlobalSection(ExtensibilityGlobals) = postSolution 58 | SolutionGuid = {A194188E-5DA7-498B-A165-1250B4A43640} 59 | EndGlobalSection 60 | EndGlobal 61 | -------------------------------------------------------------------------------- /OpenXmlToHtml/ExportImageHandler.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools; 2 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Xml.Linq; 7 | 8 | namespace Codeuctivity.OpenXmlToHtml 9 | { 10 | /// 11 | /// Exports every image of an Open XML 12 | /// 13 | public class ExportImageHandler : IImageHandler 14 | { 15 | /// 16 | /// Images of Open XML 17 | /// 18 | public IDictionary Images { get; } 19 | 20 | /// 21 | /// Transforms OpenXml Images to HTML embeddable images 22 | /// 23 | /// 24 | public ExportImageHandler(IDictionary images) 25 | { 26 | Images = images; 27 | } 28 | 29 | /// 30 | /// Transforms images to Content-ID based embedded value 31 | /// 32 | /// 33 | /// 34 | public XElement TransformImage(ImageInfo imageInfo) 35 | { 36 | var cid = Guid.NewGuid().ToString(); 37 | using var memoryStream = new MemoryStream(); 38 | imageInfo.Image.CopyTo(memoryStream); 39 | 40 | Images.Add(cid, memoryStream.ToArray()); 41 | 42 | var cidReference = $"cid: {cid}"; 43 | 44 | return new XElement(Xhtml.img, new XAttribute(NoNamespace.src, cidReference), imageInfo.ImgStyleAttribute, imageInfo?.AltText != null ? new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/IOpenXmlToHtml.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | 4 | namespace Codeuctivity.OpenXmlToHtml 5 | { 6 | /// 7 | /// Converts DOCX to HTML 8 | /// 9 | public interface IOpenXmlToHtml 10 | { 11 | /// 12 | /// Converts DOCX to HTML 13 | /// 14 | /// 15 | /// 16 | /// selfContainedHtmlFilePath 17 | Task ConvertToHtmlAsync(string sourceOpenXmlFilePath, string destinationHtmlFilePath); 18 | 19 | /// 20 | /// Converts DOCX to HTML 21 | /// 22 | /// 23 | /// 24 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 25 | /// selfContainedHtmlFilePath 26 | Task ConvertToHtmlAsync(string sourceOpenXmlFilePath, string destinationHtmlFilePath, bool useWebSafeFonts); 27 | 28 | /// 29 | /// Converts DOCX to HTML 30 | /// 31 | /// 32 | /// selfContainedHtml 33 | Task ConvertToHtmlAsync(Stream sourceOpenXml); 34 | 35 | /// 36 | /// Converts DOCX to HTML 37 | /// 38 | /// 39 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 40 | /// selfContainedHtml 41 | Task ConvertToHtmlAsync(Stream sourceOpenXml, bool useWebSafeFonts); 42 | 43 | /// 44 | /// Converts DOCX to HTML 45 | /// 46 | /// 47 | /// 48 | /// selfContainedHtml 49 | Task ConvertToHtmlAsync(Stream sourceOpenXml, string fallbackPageTitle); 50 | 51 | /// 52 | /// Converts DOCX to HTML 53 | /// 54 | /// 55 | /// 56 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 57 | /// selfContainedHtml 58 | Task ConvertToHtmlAsync(Stream sourceOpenXml, string fallbackPageTitle, bool useWebSafeFonts); 59 | } 60 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/NugetIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtml/NugetIcon.png -------------------------------------------------------------------------------- /OpenXmlToHtml/OpenXmlToHtml.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools; 2 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 3 | using DocumentFormat.OpenXml.Packaging; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using System.Xml.Linq; 11 | 12 | namespace Codeuctivity.OpenXmlToHtml 13 | { 14 | /// 15 | /// Converts DOCX to HTML 16 | /// 17 | public class OpenXmlToHtml : IOpenXmlToHtml 18 | { 19 | /// 20 | /// Converts DOCX to HTML 21 | /// 22 | /// 23 | /// 24 | /// selfContainedHtmlFilePath 25 | public Task ConvertToHtmlAsync(string sourceOpenXmlFilePath, string destinationHtmlFilePath) 26 | { 27 | return ConvertToHtmlAsync(sourceOpenXmlFilePath, destinationHtmlFilePath, false); 28 | } 29 | 30 | /// 31 | /// Converts DOCX to HTML 32 | /// 33 | /// 34 | /// 35 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 36 | /// selfContainedHtmlFilePath 37 | public async Task ConvertToHtmlAsync(string sourceOpenXmlFilePath, string destinationHtmlFilePath, bool useWebSafeFonts) 38 | { 39 | if (!File.Exists(sourceOpenXmlFilePath)) 40 | { 41 | throw new FileNotFoundException(sourceOpenXmlFilePath); 42 | } 43 | 44 | using var sourceIpenXml = new FileStream(sourceOpenXmlFilePath, FileMode.Open, FileAccess.Read); 45 | using var html = await ConvertToHtmlAsync(sourceIpenXml, sourceOpenXmlFilePath, useWebSafeFonts).ConfigureAwait(false); 46 | using var destinationHtmlFile = new FileStream(destinationHtmlFilePath, FileMode.CreateNew, FileAccess.Write); 47 | await html.CopyToAsync(destinationHtmlFile).ConfigureAwait(false); 48 | } 49 | 50 | /// 51 | /// Converts DOCX to HTML 52 | /// 53 | /// 54 | /// selfContainedHtml 55 | public Task ConvertToHtmlAsync(Stream sourceOpenXml) 56 | { 57 | return ConvertToHtmlAsync(sourceOpenXml, false); 58 | } 59 | 60 | /// 61 | /// Converts DOCX to HTML 62 | /// 63 | /// 64 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 65 | /// selfContainedHtml 66 | public Task ConvertToHtmlAsync(Stream sourceOpenXml, bool useWebSafeFonts) 67 | { 68 | return ConvertToHtmlAsync(sourceOpenXml, string.Empty, useWebSafeFonts); 69 | } 70 | 71 | /// 72 | /// Converts DOCX to HTML 73 | /// 74 | /// 75 | /// 76 | /// selfContainedHtml 77 | public Task ConvertToHtmlAsync(Stream sourceOpenXml, string fallbackPageTitle) 78 | { 79 | return ConvertToHtmlAsync(sourceOpenXml, fallbackPageTitle, false); 80 | } 81 | 82 | /// 83 | /// Converts DOCX to HTML 84 | /// 85 | /// 86 | /// 87 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 88 | /// selfContainedHtml 89 | public Task ConvertToHtmlAsync(Stream sourceOpenXml, string fallbackPageTitle, bool useWebSafeFonts) 90 | { 91 | if (sourceOpenXml == null) 92 | { 93 | throw new ArgumentNullException(nameof(sourceOpenXml)); 94 | } 95 | 96 | return ConvertToHtmlInternalAsync(sourceOpenXml, fallbackPageTitle, new ImageHandler(), useWebSafeFonts); 97 | } 98 | 99 | /// 100 | /// Converts DOCX to HTML 101 | /// 102 | /// 103 | /// 104 | /// 105 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 106 | /// selfContainedHtml 107 | public static Task ConvertToHtmlAsync(Stream sourceOpenXml, string fallbackPageTitle, IDictionary images, bool useWebSafeFonts) 108 | { 109 | if (sourceOpenXml == null) 110 | { 111 | throw new ArgumentNullException(nameof(sourceOpenXml)); 112 | } 113 | 114 | return ConvertToHtmlInternalAsync(sourceOpenXml, fallbackPageTitle, new ExportImageHandler(images), useWebSafeFonts); 115 | } 116 | 117 | private static async Task ConvertToHtmlInternalAsync(Stream sourceOpenXml, string fallbackPageTitle, IImageHandler imageHandler, bool useWebSafeFonts) 118 | { 119 | using var memoryStream = new MemoryStream(); 120 | await sourceOpenXml.CopyToAsync(memoryStream).ConfigureAwait(false); 121 | sourceOpenXml = memoryStream; 122 | 123 | using var wordProcessingDocument = WordprocessingDocument.Open(sourceOpenXml, true); 124 | var coreFilePropertiesPart = wordProcessingDocument.CoreFilePropertiesPart; 125 | var computedPageTitle = coreFilePropertiesPart?.GetXDocument().Descendants(DC.title).FirstOrDefault(); 126 | var pageTitle = string.IsNullOrEmpty(computedPageTitle?.Value) ? fallbackPageTitle : computedPageTitle!.Value; 127 | 128 | var htmlElement = WmlToHtmlConverter.ConvertToHtml(wordProcessingDocument, CreateHtmlConverterSettings(pageTitle, imageHandler, useWebSafeFonts ? new WebSafeFontsHandler() : new FontHandler())); 129 | var html = new XDocument(new XDocumentType("html", "-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd", null), htmlElement); 130 | var memoryStreamHtml = new MemoryStream(); 131 | html.Save(memoryStreamHtml, SaveOptions.DisableFormatting); 132 | memoryStreamHtml.Position = 0; 133 | return memoryStreamHtml; 134 | } 135 | 136 | private static WmlToHtmlConverterSettings CreateHtmlConverterSettings(string pageTitle, IImageHandler imageHandler, IFontHandler fontHandler) 137 | { 138 | var settings = new WmlToHtmlConverterSettings(pageTitle, imageHandler, new TextSymbolToUnicodeHandler(), new SymbolHandler(), new PageBreakHandler(new BreakHandler()), fontHandler, true, string.Empty, "@page { size: A4 } body { margin: 1cm auto; max-width: 20cm; padding: 0; }", "Codeuctivity-"); 139 | 140 | return settings; 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/OpenXmlToHtml.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | true 5 | true 6 | https://github.com/Codeuctivity/OpenXmlToHtml 7 | openxml docx html 8 | Stefan Seeland 9 | Codeuctivity 10 | $(CURRENT_VERSION) 11 | 0.0.1 12 | $(Version) 13 | $(Version) 14 | $(Version)-prerelease 15 | $(LAST_COMMIT_MESSAGE) 16 | NugetIcon.png 17 | https://github.com/Codeuctivity/OpenXmlToHtml 18 | Converts docx to html without the need to setup any dependency 19 | Apache-2.0 20 | true 21 | OpenXmlToHtml.snk 22 | true 23 | true 24 | snupkg 25 | true 26 | true 27 | 9.0 28 | enable 29 | Codeuctivity.OpenXmlToHtml 30 | nugetReadme.md 31 | OpenXmlToHtml 32 | Codeuctivity.OpenXmlToHtml 33 | Codeuctivity.OpenXmlToHtml 34 | true 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | all 47 | runtime; build; native; contentfiles; analyzers; buildtransitive 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /OpenXmlToHtml/OpenXmlToHtml.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtml/OpenXmlToHtml.snk -------------------------------------------------------------------------------- /OpenXmlToHtml/PageBreakHandler.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools; 2 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 3 | using System.Collections.Generic; 4 | using System.Xml.Linq; 5 | 6 | namespace Codeuctivity.OpenXmlToHtml 7 | { 8 | /// 9 | /// 10 | /// 11 | public class PageBreakHandler : IBreakHandler 12 | { 13 | /// 14 | /// DefaultBreakHandler is used if TransformBreak is not applied to a page break 15 | /// 16 | public IBreakHandler DefaultBreakHandler { get; } 17 | 18 | /// 19 | /// 20 | /// 21 | /// 22 | public PageBreakHandler(IBreakHandler defaultBreakHandler) 23 | { 24 | DefaultBreakHandler = defaultBreakHandler; 25 | } 26 | 27 | /// 28 | /// Default handler that transforms breaks into some HTML specific equivalent 29 | /// 30 | /// 31 | /// 32 | public IEnumerable TransformBreak(XElement element) 33 | { 34 | if (element.Attribute(W.type)?.Value == "page") 35 | { 36 | var pageBreakDiv = new XElement(Xhtml.div); 37 | pageBreakDiv.Add(new XAttribute(H.Style, "break-before: page;")); 38 | return new XNode[] { pageBreakDiv }; 39 | } 40 | 41 | return DefaultBreakHandler.TransformBreak(element); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/SymbolHandler.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools; 2 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 3 | using System.Collections.Generic; 4 | using System.Xml.Linq; 5 | 6 | namespace Codeuctivity.OpenXmlToHtml 7 | { 8 | /// 9 | /// Handler that transforms every symbol into some HTML encoded font specific char 10 | /// 11 | public class SymbolHandler : ISymbolHandler 12 | { 13 | private OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter.SymbolHandler DefaultSymbolHandler { get; set; } 14 | 15 | /// 16 | /// Dictionary codes from symbol char to Unicode 17 | /// 18 | public static Dictionary SymbolCharDictonary => new Dictionary 19 | { 20 | {"F020"," "}, 21 | {"F021","!"}, 22 | {"F022","∀"}, 23 | {"F023","#"}, 24 | {"F024","∃"}, 25 | {"F025","%"}, 26 | {"F026","&"}, 27 | {"F027","∋"}, 28 | {"F028","("}, 29 | {"F029",")"}, 30 | {"F02A","*"}, 31 | {"F02B","+"}, 32 | {"F02C",","}, 33 | {"F02D","-"}, 34 | {"F02E","."}, 35 | {"F02F","/"}, 36 | {"F030","0"}, 37 | {"F031","1"}, 38 | {"F032","2"}, 39 | {"F033","3"}, 40 | {"F034","4"}, 41 | {"F035","5"}, 42 | {"F036","6"}, 43 | {"F037","7"}, 44 | {"F038","8"}, 45 | {"F039","9"}, 46 | {"F03A",":"}, 47 | {"F03B",";"}, 48 | {"F03C","<"}, 49 | {"F03D","="}, 50 | {"F03E",">"}, 51 | {"F03F","?"}, 52 | {"F040","≅"}, 53 | {"F041","A"}, 54 | {"F042","B"}, 55 | {"F043","X"}, 56 | {"F044","Δ"}, 57 | {"F045","E"}, 58 | {"F046","Φ"}, 59 | {"F047","Γ"}, 60 | {"F048","H"}, 61 | {"F049","I"}, 62 | {"F04A","ϑ"}, 63 | {"F04B","K"}, 64 | {"F04C","∧"}, 65 | {"F04D","M"}, 66 | {"F04E","N"}, 67 | {"F04F","O"}, 68 | {"F050","Π"}, 69 | {"F051","Θ"}, 70 | {"F052","P"}, 71 | {"F053","∑"}, 72 | {"F054","T"}, 73 | {"F055","Y"}, 74 | {"F056","Z"}, 75 | {"F057","Ω"}, 76 | {"F058","Ξ"}, 77 | {"F059","Ψ"}, 78 | {"F05A","Z"}, 79 | {"F05B","["}, 80 | {"F05C","∴"}, 81 | {"F05D","]"}, 82 | {"F05E","⊥"}, 83 | {"F05F","_"}, 84 | // Not sure, but I think that overline has a context specific effect on the following char 85 | {"F060","\u0305"}, 86 | {"F061","α"}, 87 | {"F062","β"}, 88 | {"F063","χ"}, 89 | {"F064","δ"}, 90 | {"F065","ε"}, 91 | {"F066","ϕ"}, 92 | {"F067","γ"}, 93 | {"F068","η"}, 94 | {"F069","ι"}, 95 | {"F06A","φ"}, 96 | {"F06B","κ"}, 97 | {"F06C","λ"}, 98 | {"F06D","μ"}, 99 | {"F06E","ν"}, 100 | {"F06F","ο"}, 101 | {"F070","π"}, 102 | {"F071","θ"}, 103 | {"F072","ρ"}, 104 | {"F073","σ"}, 105 | {"F074","τ"}, 106 | {"F075","υ"}, 107 | {"F076","ϖ"}, 108 | {"F077","ω"}, 109 | {"F078","ξ"}, 110 | {"F079","ψ"}, 111 | {"F07A","ζ"}, 112 | {"F07B","{"}, 113 | {"F07C","|"}, 114 | {"F07D","}"}, 115 | {"F07E","∼"}, 116 | {"F07F","□"}, 117 | {"F080","□"}, 118 | {"F081","□"}, 119 | {"F082","□"}, 120 | {"F083","□"}, 121 | {"F084","□"}, 122 | {"F085","□"}, 123 | {"F086","□"}, 124 | {"F087","□"}, 125 | {"F088","□"}, 126 | {"F089","□"}, 127 | {"F08A","□"}, 128 | {"F08B","□"}, 129 | {"F08C","□"}, 130 | {"F08D","□"}, 131 | {"F08E","□"}, 132 | {"F08F","□"}, 133 | {"F090","□"}, 134 | {"F091","□"}, 135 | {"F092","□"}, 136 | {"F093","□"}, 137 | {"F094","□"}, 138 | {"F095","□"}, 139 | {"F096","□"}, 140 | {"F097","□"}, 141 | {"F098","□"}, 142 | {"F099","□"}, 143 | {"F09A","□"}, 144 | {"F09B","□"}, 145 | {"F09C","□"}, 146 | {"F09D","□"}, 147 | {"F09E","□"}, 148 | {"F09F","□"}, 149 | {"F0A0","□"}, 150 | {"F0A1","ϒ"}, 151 | {"F0A2","′"}, 152 | {"F0A3","≤"}, 153 | {"F0A4","∞"}, 154 | {"F0A5","⁄"}, 155 | {"F0A6","ƒ"}, 156 | {"F0A7","♣"}, 157 | {"F0A8","♦"}, 158 | {"F0A9","♥"}, 159 | {"F0AA","♠"}, 160 | {"F0AB","↔"}, 161 | {"F0AC","←"}, 162 | {"F0AD","↑"}, 163 | {"F0AE","→"}, 164 | {"F0AF","↓"}, 165 | {"F0B0","°"}, 166 | {"F0B1","±"}, 167 | {"F0B2","″"}, 168 | {"F0B3","≥"}, 169 | {"F0B4","×"}, 170 | {"F0B5","∝"}, 171 | {"F0B6","∂"}, 172 | {"F0B7","•"}, 173 | {"F0B8","÷"}, 174 | {"F0B9","≠"}, 175 | {"F0BA","≡"}, 176 | {"F0BB","≈"}, 177 | {"F0BC","…"}, 178 | {"F0BD","⏐"}, 179 | {"F0BE","⎯"}, 180 | {"F0BF","↵"}, 181 | {"F0C0","ℵ"}, 182 | {"F0C1","ℑ"}, 183 | {"F0C2","ℜ"}, 184 | {"F0C3","℘"}, 185 | {"F0C4","⊗"}, 186 | {"F0C5","⊕"}, 187 | {"F0C6","∅"}, 188 | {"F0C7","∩"}, 189 | {"F0C8","∪"}, 190 | {"F0C9","⊃"}, 191 | {"F0CA","⊇"}, 192 | {"F0CB","⊄"}, 193 | {"F0CC","⊂"}, 194 | {"F0CD","⊆"}, 195 | {"F0CE","∈"}, 196 | {"F0CF","∉"}, 197 | {"F0D0","∠"}, 198 | {"F0D1","∇"}, 199 | {"F0D2","®️"}, 200 | {"F0D3","©️"}, 201 | {"F0D4","™️"}, 202 | {"F0D5","∏"}, 203 | {"F0D6","√"}, 204 | {"F0D7","⋅"}, 205 | {"F0D8","¬"}, 206 | {"F0D9","∧"}, 207 | {"F0DA","∨"}, 208 | {"F0DB","⇔"}, 209 | {"F0DC","⇐"}, 210 | {"F0DD","⇑"}, 211 | {"F0DE","⇒"}, 212 | {"F0DF","⇓"}, 213 | {"F0E0","◊"}, 214 | {"F0E1","⟨"}, 215 | {"F0E2","®"}, 216 | {"F0E3","©"}, 217 | {"F0E4","™"}, 218 | {"F0E5","∑"}, 219 | {"F0E6","⎛"}, 220 | {"F0E7","⎜"}, 221 | {"F0E8","⎝"}, 222 | {"F0E9","⎡"}, 223 | {"F0EA","⎢"}, 224 | {"F0EB","⎣"}, 225 | {"F0EC","⎧"}, 226 | {"F0ED","⎨"}, 227 | {"F0EE","⎩"}, 228 | {"F0EF","⎪"}, 229 | {"F0F0","□"}, 230 | {"F0F1","〉"}, 231 | {"F0F2","∫"}, 232 | {"F0F3","⌠"}, 233 | {"F0F4","⎮"}, 234 | {"F0F5","⌡"}, 235 | {"F0F6","⎞"}, 236 | {"F0F7","⎟"}, 237 | {"F0F8","⎠"}, 238 | {"F0F9","⎤"}, 239 | {"F0FA","⎥"}, 240 | {"F0FB","⎦"}, 241 | {"F0FC","⎫"}, 242 | {"F0FD","⎬"}, 243 | {"F0FE","⎭"}, 244 | {"F0FF","□"} 245 | }; 246 | 247 | /// 248 | /// Dictonary codes from wingdings char to unicode 249 | /// 250 | public static Dictionary WingdingsCharDictonary => new Dictionary 251 | { 252 | {"F020"," "}, 253 | {"F021","🖉"}, 254 | {"F022","✂"}, 255 | {"F023","✁"}, 256 | {"F024","👓"}, 257 | {"F025","🕭"}, 258 | {"F026","🕮"}, 259 | {"F027","🕯"}, 260 | {"F028","🕿"}, 261 | {"F029","✆"}, 262 | {"F02A","🖂"}, 263 | {"F02B","🖃"}, 264 | {"F02C","📪"}, 265 | {"F02D","📫"}, 266 | {"F02E","📬"}, 267 | {"F02F","📭"}, 268 | {"F030","🗀"}, 269 | {"F031","🗁"}, 270 | {"F032","🖹"}, 271 | {"F033","🗏"}, 272 | {"F034","🗐"}, 273 | {"F035","🗄"}, 274 | {"F036","⌛"}, 275 | {"F037","🖮"}, 276 | {"F038","🖰"}, 277 | {"F039","🖲"}, 278 | {"F03A","🖳"}, 279 | {"F03B","🖴"}, 280 | {"F03C","🖫"}, 281 | {"F03D","🖬"}, 282 | {"F03E","✇"}, 283 | {"F03F","✍"}, 284 | {"F040","🖎"}, 285 | {"F041","✌"}, 286 | {"F042","👌"}, 287 | {"F043","👍"}, 288 | {"F044","👎"}, 289 | {"F045","☜"}, 290 | {"F046","☞"}, 291 | {"F047","☝"}, 292 | {"F048","☟"}, 293 | {"F049","🖐"}, 294 | {"F04A","☺"}, 295 | {"F04B","😐"}, 296 | {"F04C","☹"}, 297 | {"F04D","💣"}, 298 | {"F04E","☠"}, 299 | {"F04F","🏳"}, 300 | {"F050","🏱"}, 301 | {"F051","✈"}, 302 | {"F052","☼"}, 303 | {"F053","💧"}, 304 | {"F054","❄"}, 305 | {"F055","🕆"}, 306 | {"F056","✞"}, 307 | {"F057","🕈"}, 308 | {"F058","✠"}, 309 | {"F059","✡"}, 310 | {"F05A","☪"}, 311 | {"F05B","☯"}, 312 | {"F05C","ॐ"}, 313 | {"F05D","☸"}, 314 | {"F05E","♈"}, 315 | {"F05F","♉"}, 316 | {"F060","♊"}, 317 | {"F061","♋"}, 318 | {"F062","♌"}, 319 | {"F063","♍"}, 320 | {"F064","♎"}, 321 | {"F065","♏"}, 322 | {"F066","♐"}, 323 | {"F067","♑"}, 324 | {"F068","♒"}, 325 | {"F069","♓"}, 326 | {"F06A","🙰"}, 327 | {"F06B","🙵"}, 328 | {"F06C","●"}, 329 | {"F06D","🔾"}, 330 | {"F06E","■"}, 331 | {"F06F","□"}, 332 | {"F070","🞐"}, 333 | {"F071","❑"}, 334 | {"F072","❒"}, 335 | {"F073","⬧"}, 336 | {"F074","⧫"}, 337 | {"F075","◆"}, 338 | {"F076","❖"}, 339 | {"F077","⬥"}, 340 | {"F078","⌧"}, 341 | {"F079","⮹"}, 342 | {"F07A","⌘"}, 343 | {"F07B","🏵"}, 344 | {"F07C","🏶"}, 345 | {"F07D","🙶"}, 346 | {"F07E","🙷"}, 347 | {"F07F","□"}, 348 | {"F080","⓪"}, 349 | {"F081","①"}, 350 | {"F082","②"}, 351 | {"F083","③"}, 352 | {"F084","④"}, 353 | {"F085","⑤"}, 354 | {"F086","⑥"}, 355 | {"F087","⑦"}, 356 | {"F088","⑧"}, 357 | {"F089","⑨"}, 358 | {"F08A","⑩"}, 359 | {"F08B","⓿"}, 360 | {"F08C","❶"}, 361 | {"F08D","❷"}, 362 | {"F08E","❸"}, 363 | {"F08F","❹"}, 364 | {"F090","❺"}, 365 | {"F091","❻"}, 366 | {"F092","❼"}, 367 | {"F093","❽"}, 368 | {"F094","❾"}, 369 | {"F095","❿"}, 370 | {"F096","🙢"}, 371 | {"F097","🙠"}, 372 | {"F098","🙡"}, 373 | {"F099","🙣"}, 374 | {"F09A","🙞"}, 375 | {"F09B","🙜"}, 376 | {"F09C","🙝"}, 377 | {"F09D","🙟"}, 378 | {"F09E","·"}, 379 | {"F09f","•"}, 380 | {"F0A0","▪"}, 381 | {"F0A1","⚪"}, 382 | {"F0A2","🞆"}, 383 | {"F0A3","🞈"}, 384 | {"F0A4","◉"}, 385 | {"F0A5","◎"}, 386 | {"F0A6","🔿"}, 387 | {"F0A7","▪"}, 388 | {"F0A8","◻"}, 389 | {"F0A9","🟂"}, 390 | {"F0AA","✦"}, 391 | {"F0AB","★"}, 392 | {"F0AC","✶"}, 393 | {"F0AD","✴"}, 394 | {"F0AE","✹"}, 395 | {"F0AF","✵"}, 396 | {"F0B0","⯐"}, 397 | {"F0B1","⌖"}, 398 | {"F0B2","⟡"}, 399 | {"F0B3","⌑"}, 400 | {"F0B4","⯑"}, 401 | {"F0B5","✪"}, 402 | {"F0B6","✰"}, 403 | {"F0B7","🕐"}, 404 | {"F0B8","🕑"}, 405 | {"F0B9","🕒"}, 406 | {"F0BA","🕓"}, 407 | {"F0BB","🕔"}, 408 | {"F0BC","🕕"}, 409 | {"F0BD","🕖"}, 410 | {"F0BE","🕗"}, 411 | {"F0BF","🕘"}, 412 | {"F0C0","🕙"}, 413 | {"F0C1","🕚"}, 414 | {"F0C2","🕛"}, 415 | {"F0C3","⮰"}, 416 | {"F0C4","⮱"}, 417 | {"F0C5","⮲"}, 418 | {"F0C6","⮳"}, 419 | {"F0C7","⮴"}, 420 | {"F0C8","⮵"}, 421 | {"F0C9","⮶"}, 422 | {"F0CA","⮷"}, 423 | {"F0CB","🙪"}, 424 | {"F0CC","🙫"}, 425 | {"F0CD","🙕"}, 426 | {"F0CE","🙔"}, 427 | {"F0CF","🙗"}, 428 | {"F0D0","🙖"}, 429 | {"F0D1","🙐"}, 430 | {"F0D2","🙑"}, 431 | {"F0D3","🙒"}, 432 | {"F0D4","🙓"}, 433 | {"F0D5","⌫"}, 434 | {"F0D6","⌦"}, 435 | {"F0D7","⮘"}, 436 | {"F0D8","⮚"}, 437 | {"F0D9","⮙"}, 438 | {"F0DA","⮛"}, 439 | {"F0DB","⮈"}, 440 | {"F0DC","⮊"}, 441 | {"F0DD","⮉"}, 442 | {"F0DE","⮋"}, 443 | {"F0DF","🡨"}, 444 | {"F0E0","🡪"}, 445 | {"F0E1","🡩"}, 446 | {"F0E2","🡫"}, 447 | {"F0E3","🡬"}, 448 | {"F0E4","🡭"}, 449 | {"F0E5","🡯"}, 450 | {"F0E6","🡯"}, 451 | {"F0E7","🡮"}, 452 | {"F0E8","🡸"}, 453 | {"F0E9","🡺"}, 454 | {"F0EA","🡹"}, 455 | {"F0EB","🡻"}, 456 | {"F0EC","🡼"}, 457 | {"F0ED","🡽"}, 458 | {"F0EE","🡿"}, 459 | {"F0EF","🡾"}, 460 | {"F0F0","⇦"}, 461 | {"F0F1","⇨"}, 462 | {"F0F2","⇧"}, 463 | {"F0F3","⇩"}, 464 | {"F0F4","⬄"}, 465 | {"F0F5","⇳"}, 466 | {"F0F6","⬁"}, 467 | {"F0F7","⬃"}, 468 | {"F0F8","⬂"}, 469 | {"F0F9","🢬"}, 470 | {"F0FA","🢭"}, 471 | {"F0FB","🗶"}, 472 | {"F0FC","✔"}, 473 | {"F0FD","🗷"}, 474 | {"F0FE","🗹"}, 475 | }; 476 | 477 | /// 478 | /// Default ctor 479 | /// 480 | public SymbolHandler() 481 | { 482 | DefaultSymbolHandler = new OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter.SymbolHandler(); 483 | } 484 | 485 | /// 486 | /// Default handler that transforms every symbol into some html encoded font specific char 487 | /// 488 | /// 489 | /// 490 | /// 491 | public XElement TransformSymbol(XElement element, Dictionary fontFamily) 492 | { 493 | if (fontFamily.TryGetValue("font-family", out var currentSymbolFontFamily) && currentSymbolFontFamily == "Symbol") 494 | { 495 | var customChar = element.Attribute(W._char)?.Value; 496 | 497 | if (customChar != null && SymbolCharDictonary.TryGetValue(customChar, out var commonChar)) 498 | { 499 | return new XElement(Xhtml.span, commonChar); 500 | } 501 | } 502 | else if (fontFamily.TryGetValue("font-family", out var currentWingdingsFontFamily) && currentWingdingsFontFamily == "Wingdings") 503 | { 504 | var customChar = element.Attribute(W._char)?.Value; 505 | 506 | if (customChar != null && WingdingsCharDictonary.TryGetValue(customChar, out var commonChar)) 507 | { 508 | return new XElement(Xhtml.span, commonChar); 509 | } 510 | } 511 | 512 | return DefaultSymbolHandler.TransformSymbol(element, fontFamily); 513 | } 514 | } 515 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/TextSymbolToUnicodeHandler.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 2 | using System.Collections.Generic; 3 | 4 | namespace Codeuctivity.OpenXmlToHtml 5 | { 6 | /// 7 | /// Replaces any char of wingdings with the Unicode equivalent 8 | /// 9 | public class TextSymbolToUnicodeHandler : ITextHandler 10 | { 11 | /// 12 | /// Dictionary used to translate symbol chars to Unicode 13 | /// 14 | private static readonly Dictionary SymbolToUnicode = new Dictionary 15 | { 16 | { '','•' } 17 | }; 18 | 19 | /// 20 | /// Replaces any char of wingdings with the Unicode equivalent 21 | /// 22 | public string TransformText(string text, Dictionary fontFamily) 23 | { 24 | if (fontFamily.TryGetValue("font-family", out var currentFontFamily) && currentFontFamily == "Symbol") 25 | { 26 | foreach (var item in SymbolToUnicode) 27 | { 28 | text = text.Replace(item.Key, item.Value); 29 | } 30 | } 31 | 32 | return text; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/Tooling/Linux.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace Codeuctivity.OpenXmlToHtml.Tooling 6 | { 7 | public class Linux 8 | { 9 | public static readonly string ChromiumInstallCommand = "export DEBIAN_FRONTEND=noninteractive && apt update && apt upgrade -y && apt install mc libgconf-2-4 libatk1.0-0 libatk-bridge2.0-0 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm-dev libasound2 libnss3 -y"; 10 | 11 | public static void SetupDependencies() 12 | { 13 | var azureLinuxAppChromeDependencies = ChromiumInstallCommand; 14 | 15 | var escapedArgs = azureLinuxAppChromeDependencies.Replace("\"", "\\\""); 16 | var process = new Process 17 | { 18 | StartInfo = new ProcessStartInfo 19 | { 20 | FileName = "/bin/bash", 21 | Arguments = $"-c \"{escapedArgs}\"", 22 | RedirectStandardOutput = true, 23 | UseShellExecute = false, 24 | CreateNoWindow = true, 25 | } 26 | }; 27 | 28 | process.Start(); 29 | process.StandardOutput.ReadToEnd(); 30 | process.WaitForExit(); 31 | 32 | if (process.ExitCode != 0) 33 | { 34 | throw new Exception($"Failed to execute '{ChromiumInstallCommand}'"); 35 | } 36 | } 37 | 38 | public static bool IsRunningOnAzureLinux() 39 | { 40 | var websiteSku = Environment.GetEnvironmentVariable("WEBSITE_SKU"); 41 | 42 | if (string.IsNullOrEmpty(websiteSku)) 43 | { 44 | return false; 45 | } 46 | 47 | return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && websiteSku.Contains("Linux"); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/WebSafeFontsHandler.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | 5 | namespace Codeuctivity.OpenXmlToHtml 6 | { 7 | /// 8 | /// Replaces any font that is not part a white list of fonts with Arial 9 | /// 10 | public class WebSafeFontsHandler : IFontHandler 11 | { 12 | /// 13 | /// Web Safe Fonts for HTML and CSS https://www.w3schools.com/cssref/css_websafe_fonts.asp 14 | /// 15 | public string[] WebSafeFontNames { get; private set; } 16 | 17 | private FontHandler FontHandler { get; } 18 | 19 | /// 20 | /// Default ctor 21 | /// 22 | public WebSafeFontsHandler() 23 | { 24 | WebSafeFontNames = new[] { "Arial", "Verdana", "Helvetica", "Tahoma", "Trebuchet MS", "Times New Roman", "Georgia", "Garamond", "Courier New", "Brush Script MT" }; 25 | FontHandler = new FontHandler(); 26 | } 27 | 28 | /// 29 | /// Use this ctor to use a custom list of WebSafeFontNames 30 | /// 31 | /// 32 | public WebSafeFontsHandler(string[] webSafeFontNames) 33 | { 34 | WebSafeFontNames = webSafeFontNames; 35 | FontHandler = new FontHandler(); 36 | } 37 | 38 | /// 39 | /// Replaces any font not white listed 40 | /// 41 | /// 42 | /// 43 | public string TranslateParagraphStyleFont(XElement paragraph) 44 | { 45 | var unsafeFont = FontHandler.TranslateParagraphStyleFont(paragraph); 46 | 47 | return tranlateToWebSafeFont(unsafeFont); 48 | } 49 | 50 | /// 51 | /// Replaces any font not white listed 52 | /// 53 | /// 54 | /// 55 | public string TranslateRunStyleFont(XElement run) 56 | { 57 | var unsafeFont = FontHandler.TranslateRunStyleFont(run); 58 | 59 | return tranlateToWebSafeFont(unsafeFont); 60 | } 61 | 62 | private string tranlateToWebSafeFont(string unsafeFont) 63 | { 64 | if (WebSafeFontNames.Contains(unsafeFont)) 65 | { 66 | return unsafeFont; 67 | } 68 | 69 | return "Arial"; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /OpenXmlToHtml/docs/nugetReadme.md: -------------------------------------------------------------------------------- 1 | Converts DOCX to HTML: 2 | 3 | ```csharp 4 | await new OpenXmlToHtml().ConvertToHtmlAsync(inputPathDocx, outputPathHtml); 5 | ``` 6 | -------------------------------------------------------------------------------- /OpenXmlToHtmlCli/OpenXmlToHtmlCli.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | true 7 | win-x64 8 | true 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /OpenXmlToHtmlCli/Program.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlToHtml; 2 | using System; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | namespace OpenXmlToHtmlCli 7 | { 8 | internal class Program 9 | { 10 | private static async Task Main(string[] args) 11 | { 12 | if (args.Length != 2) 13 | { 14 | Console.WriteLine("Usage: OpenXmlToHtmlCli "); 15 | return 1; 16 | } 17 | 18 | var inputPathDocx = args[0]; 19 | var outputPathHtml = args[1]; 20 | 21 | if (!File.Exists(inputPathDocx)) 22 | { 23 | Console.WriteLine($"Could not find source {inputPathDocx}."); 24 | return 1; 25 | } 26 | 27 | if (File.Exists(outputPathHtml)) 28 | { 29 | Console.WriteLine($"Destination {outputPathHtml} already exists."); 30 | return 1; 31 | } 32 | 33 | Console.WriteLine($"Converting {inputPathDocx} to {outputPathHtml}"); 34 | await new OpenXmlToHtml().ConvertToHtmlAsync(inputPathDocx, outputPathHtml); 35 | return 0; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlCli/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "OpenXmlToHtmlCli": { 4 | "commandName": "Project", 5 | "commandLineArgs": "source.docx destination.html" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "5.0.4", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/Azure/LinuxSpecificContainerSetup.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlToHtml.Tooling; 2 | using Microsoft.Extensions.Hosting; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace OpenXmlToHtmlOpenApi.Azure 7 | { 8 | /// 9 | /// Setup dependencies on azure linux containers 10 | /// w 11 | public class AzureAndWslSpecificContainerSetup : IHostedService 12 | { 13 | /// 14 | public Task StartAsync(CancellationToken cancellationToken) 15 | { 16 | Linux.SetupDependencies(); 17 | 18 | return Task.CompletedTask; 19 | } 20 | 21 | /// 22 | public Task StopAsync(CancellationToken cancellationToken) 23 | { 24 | { return Task.CompletedTask; } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/Controllers/OpenXmlConverterController.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.HtmlRenderer; 2 | using Codeuctivity.OpenXmlToHtml; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System; 6 | using System.IO; 7 | using System.Threading.Tasks; 8 | 9 | namespace OpenXmlToHtmlOpenApi.Controllers 10 | { 11 | /// 12 | /// OpenXml converter 13 | /// 14 | [ApiController] 15 | [Route("[controller]")] 16 | public class OpenXmlConverterController : ControllerBase 17 | { 18 | private readonly IOpenXmlToHtml _openXmlToHtml; 19 | 20 | /// 21 | /// OpenXmlConverter ctor 22 | /// 23 | /// 24 | public OpenXmlConverterController(IOpenXmlToHtml openXmlToHtml) 25 | { 26 | _openXmlToHtml = openXmlToHtml; 27 | } 28 | 29 | /// 30 | /// Converts OpenXmlFile to HTML 31 | /// 32 | /// 33 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 34 | /// HTML 35 | [HttpPost] 36 | [ProducesResponseType(200)] 37 | [ProducesResponseType(400)] 38 | public async Task ConvertToHtml(IFormFile openXmlFile, bool useWebSafeFonts) 39 | { 40 | if (openXmlFile.Length > 0) 41 | { 42 | var htmlStream = await _openXmlToHtml.ConvertToHtmlAsync(openXmlFile.OpenReadStream(), useWebSafeFonts); 43 | return File(htmlStream, "text/html"); 44 | } 45 | return BadRequest("Request contains no document"); 46 | } 47 | 48 | /// 49 | /// Converts OpenXmlFile to PDF 50 | /// 51 | /// 52 | /// Use 'true' to replace every non web safe font with some fallback. Default is false. 53 | /// PDF 54 | [HttpPost] 55 | [Route("ConvertToPdf")] 56 | [ProducesResponseType(200)] 57 | [ProducesResponseType(400)] 58 | public async Task ConvertToPdf(IFormFile openXmlFile, bool useWebSafeFonts) 59 | { 60 | await using var chromiumRenderer = await Renderer.CreateAsync(); 61 | if (openXmlFile.Length > 0) 62 | { 63 | var pathHtml = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.html"); 64 | var pathPdf = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.pdf"); 65 | 66 | var htmlStream = await _openXmlToHtml.ConvertToHtmlAsync(openXmlFile.OpenReadStream(), useWebSafeFonts); 67 | try 68 | { 69 | using var fileStreamHtml = new FileStream(pathHtml, FileMode.CreateNew); 70 | await htmlStream.CopyToAsync(fileStreamHtml); 71 | await chromiumRenderer.ConvertHtmlToPdf(pathHtml, pathPdf); 72 | var pdf = await System.IO.File.ReadAllBytesAsync(pathPdf); 73 | return File(pdf, "application/pdf", $"{openXmlFile.FileName}.pdf"); 74 | } 75 | finally 76 | { 77 | System.IO.File.Delete(pathHtml); 78 | System.IO.File.Delete(pathPdf); 79 | } 80 | } 81 | return BadRequest("Request contains no document"); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/OpenXmlToHtmlOpenApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | fd376f00-03ce-419b-883f-bfa8af935ee4 6 | $(CURRENT_VERSION) 7 | 0.0.1 8 | $(Version) 9 | $(Version) 10 | $(Version) 11 | 12 | 13 | 14 | .\OpenXmlToHtmlOpenApi\OpenXmlToHtmlOpenApi.xml 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/OpenXmlToHtmlOpenApi/OpenXmlToHtmlOpenApi.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenXmlToHtmlOpenApi 5 | 6 | 7 | 8 | 9 | Setup dependencies on azure linux containers 10 | w 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | OpenXml converter 21 | 22 | 23 | 24 | 25 | OpenXmlConverter ctor 26 | 27 | 28 | 29 | 30 | 31 | Converts OpenXmlFile to HTML 32 | 33 | 34 | Use 'true' to replace every non web safe font with some fallback. Default is false. 35 | HTML 36 | 37 | 38 | 39 | Converts OpenXmlFile to PDF 40 | 41 | 42 | Use 'true' to replace every non web safe font with some fallback. Default is false. 43 | PDF 44 | 45 | 46 | 47 | Program 48 | 49 | 50 | 51 | 52 | Main entry point 53 | 54 | 55 | 56 | 57 | 58 | Called on start 59 | 60 | 61 | 62 | 63 | 64 | 65 | Asp .net application startup code 66 | 67 | 68 | 69 | 70 | Startup 71 | 72 | 73 | 74 | 75 | 76 | Application configuration 77 | 78 | 79 | 80 | 81 | This method gets called by the runtime. Use this method to add services to the container. 82 | 83 | 84 | 85 | 86 | 87 | This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace OpenXmlToHtmlOpenApi 5 | { 6 | /// 7 | /// Program 8 | /// 9 | public class Program 10 | { 11 | /// 12 | /// Main entry point 13 | /// 14 | /// 15 | public static void Main(string[] args) 16 | { 17 | CreateHostBuilder(args).Build().Run(); 18 | } 19 | 20 | /// 21 | /// Called on start 22 | /// 23 | /// 24 | /// 25 | public static IHostBuilder CreateHostBuilder(string[] args) 26 | { 27 | return Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => 28 | { 29 | webBuilder.UseStartup(); 30 | }); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:54015", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "index.html", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "OpenXmlToHtmlOpenApi": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "index.html", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/Startup.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlToHtml; 2 | using Codeuctivity.OpenXmlToHtml.Tooling; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.OpenApi.Models; 9 | using OpenXmlToHtmlOpenApi.Azure; 10 | using System; 11 | using System.IO; 12 | using System.Reflection; 13 | 14 | namespace OpenXmlToHtmlOpenApi 15 | { 16 | /// 17 | /// Asp .net application startup code 18 | /// 19 | public class Startup 20 | { 21 | /// 22 | /// Startup 23 | /// 24 | /// 25 | public Startup(IConfiguration configuration) 26 | { 27 | Configuration = configuration; 28 | } 29 | 30 | /// 31 | /// Application configuration 32 | /// 33 | public IConfiguration Configuration { get; } 34 | 35 | private const string GithubProjectAdress = "https://github.com/Codeuctivity/OpenXmlToHtml"; 36 | 37 | /// 38 | /// This method gets called by the runtime. Use this method to add services to the container. 39 | /// 40 | /// 41 | public void ConfigureServices(IServiceCollection services) 42 | { 43 | services.AddSingleton(); 44 | 45 | if (Linux.IsRunningOnAzureLinux()) 46 | { 47 | services.AddSingleton(); 48 | } 49 | 50 | services.AddApplicationInsightsTelemetry(); 51 | services.AddControllers(); 52 | services.AddSwaggerGen(c => 53 | { 54 | c.SwaggerDoc("v1", new OpenApiInfo 55 | { 56 | Version = "v2", 57 | Title = $"{Assembly.GetEntryAssembly().GetName().Name} {Assembly.GetEntryAssembly().GetName().Version}", 58 | Description = "A simple OpenApi wrapping access to OpenXmlToHtml", 59 | TermsOfService = new Uri(GithubProjectAdress), 60 | Contact = new OpenApiContact 61 | { 62 | Name = "Codeuctivity", 63 | Email = string.Empty, 64 | Url = new Uri(GithubProjectAdress), 65 | }, 66 | License = new OpenApiLicense 67 | { 68 | Name = "Use under AGPL", 69 | Url = new Uri($"{GithubProjectAdress}/blob/main/LICENSE"), 70 | } 71 | }); 72 | 73 | // Set the comments path for the Swagger JSON and UI. 74 | var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; 75 | var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); 76 | c.IncludeXmlComments(xmlPath); 77 | }); 78 | } 79 | 80 | /// 81 | /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 82 | /// 83 | /// 84 | /// 85 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 86 | { 87 | if (env.IsDevelopment()) 88 | { 89 | app.UseDeveloperExceptionPage(); 90 | } 91 | 92 | app.UseHttpsRedirection(); 93 | 94 | // Enable middleware to serve generated Swagger as a JSON endpoint. 95 | app.UseSwagger(); 96 | 97 | // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint. 98 | app.UseSwaggerUI(c => 99 | { 100 | c.SwaggerEndpoint("/swagger/v1/swagger.json", "OpenXmlToHtml V1"); 101 | c.RoutePrefix = string.Empty; 102 | }); 103 | 104 | app.UseRouting(); 105 | 106 | app.UseAuthorization(); 107 | 108 | app.UseEndpoints(endpoints => 109 | { 110 | endpoints.MapControllers(); 111 | }); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApiTests/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA2007: Consider calling ConfigureAwait on the awaited task 4 | dotnet_diagnostic.CA2007.severity = none 5 | -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApiTests/AzureTests.cs: -------------------------------------------------------------------------------- 1 | using OpenXmlToHtmlOpenApi.Azure; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using System.Threading; 5 | using Xunit; 6 | 7 | namespace OpenXmlToHtmlOpenApiTests 8 | { 9 | public class AzureTests 10 | { 11 | [SkippableFact] 12 | public async void ShoulSetupChromeiumDependencies() 13 | { 14 | Skip.IfNot(RuntimeInformation.IsOSPlatform(OSPlatform.Linux), "Setup code is designed for Ubuntu"); 15 | Skip.If(System.Environment.UserName != "root"); 16 | var azureSpecificContainerSetup = new AzureAndWslSpecificContainerSetup(); 17 | await azureSpecificContainerSetup.StartAsync(new CancellationToken()); 18 | } 19 | 20 | public static bool IsRunningOnWsl() 21 | { 22 | if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 23 | { 24 | return false; 25 | } 26 | 27 | var version = File.ReadAllText("/proc/version"); 28 | var IsWsl = version.Contains("WSL"); 29 | return IsWsl; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApiTests/OpenXmlConverterControllerIntegrativeTests.cs: -------------------------------------------------------------------------------- 1 | using AngleSharp; 2 | using Microsoft.AspNetCore.Mvc.Testing; 3 | using System; 4 | using System.IO; 5 | using System.Net.Http; 6 | using System.Runtime.InteropServices; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | 10 | namespace OpenXmlToHtmlOpenApiTests 11 | { 12 | public class OpenXmlConverterControllerIntegrativeTests : IClassFixture> 13 | { 14 | private readonly WebApplicationFactory _factory; 15 | 16 | public OpenXmlConverterControllerIntegrativeTests(WebApplicationFactory factory) 17 | { 18 | _factory = factory; 19 | } 20 | 21 | [SkippableTheory] 22 | [InlineData("/", "text/html; charset=utf-8")] 23 | [InlineData("/swagger/v1/swagger.json", "application/json; charset=utf-8")] 24 | public async Task ShouldAccessEndpointSuccessfull(string route, string contentType) 25 | { 26 | Skip.If(IsRunningOnWsl()); 27 | 28 | // Arrange 29 | var client = _factory.CreateClient(); 30 | var expectedUrl = new Uri($"https://localhost{route}"); 31 | 32 | // Act 33 | var response = await client.GetAsync(expectedUrl).ConfigureAwait(false); 34 | 35 | // Assert 36 | response.EnsureSuccessStatusCode(); 37 | Assert.Equal(contentType, response.Content.Headers.ContentType.ToString()); 38 | } 39 | 40 | private static bool IsRunningOnWsl() 41 | { 42 | if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 43 | { 44 | return false; 45 | } 46 | 47 | var version = File.ReadAllText("/proc/version"); 48 | var IsWsl = version.Contains("Microsoft", StringComparison.InvariantCultureIgnoreCase); 49 | return IsWsl; 50 | } 51 | 52 | [SkippableFact] 53 | public async Task ShouldConvertOpenXmlToHtml() 54 | { 55 | Skip.If(IsRunningOnWsl()); 56 | 57 | // Arrange 58 | var client = _factory.CreateClient(); 59 | using var request = new HttpRequestMessage(new HttpMethod("POST"), "https://localhost/OpenXmlConverter"); 60 | using var file = new ByteArrayContent(File.ReadAllBytes("../../../TestInput/BasicTextFormated.docx")); 61 | file.Headers.Add("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); 62 | var multipartContent = new MultipartFormDataContent 63 | { 64 | { file, "openXmlFile", Path.GetFileName("BasicTextFormated.docx") } 65 | }; 66 | request.Content = multipartContent; 67 | 68 | // Act 69 | var response = await client.SendAsync(request).ConfigureAwait(false); 70 | 71 | // Assert 72 | response.EnsureSuccessStatusCode(); 73 | Assert.Equal("text/html", response.Content.Headers.ContentType.MediaType); 74 | await AssertHtmlContentAsync(response.Content, "Lorem Ipsum"); 75 | } 76 | 77 | [SkippableFact] 78 | public async Task ShouldConvertOpenXmlToPdf() 79 | { 80 | Skip.If(IsRunningOnWsl()); 81 | 82 | // Arrange 83 | var client = _factory.CreateClient(); 84 | using var request = new HttpRequestMessage(new HttpMethod("POST"), "https://localhost/OpenXmlConverter/ConvertToPdf"); 85 | using var file = new ByteArrayContent(File.ReadAllBytes("../../../TestInput/BasicTextFormated.docx")); 86 | file.Headers.Add("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); 87 | var multipartContent = new MultipartFormDataContent 88 | { 89 | { file, "openXmlFile", Path.GetFileName("BasicTextFormated.docx") } 90 | }; 91 | request.Content = multipartContent; 92 | 93 | // Act 94 | var response = await client.SendAsync(request).ConfigureAwait(false); 95 | 96 | // Assert 97 | response.EnsureSuccessStatusCode(); 98 | Assert.Equal("application/pdf", response.Content.Headers.ContentType.MediaType); 99 | } 100 | 101 | private static async Task AssertHtmlContentAsync(HttpContent content, string expectedText) 102 | { 103 | var context = BrowsingContext.New(Configuration.Default); 104 | 105 | var convertedMarkup = await content.ReadAsStringAsync(); 106 | var document = await context.OpenAsync(req => req.Content(convertedMarkup)); 107 | 108 | var actualText = document.QuerySelector(".Codeuctivity-000001")?.TextContent; 109 | Assert.Equal(expectedText, actualText); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApiTests/OpenXmlToHtmlOpenApiTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApiTests/TestInput/BasicTextFormated.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlOpenApiTests/TestInput/BasicTextFormated.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlOpenApiTests/WebApplicationFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.AspNetCore.Mvc.Testing; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace OpenXmlToHtmlOpenApiTests 6 | { 7 | public class OpenXmlToHtmlOpenApiTestFactory : WebApplicationFactory where TStartup : class 8 | { 9 | protected override void ConfigureWebHost(IWebHostBuilder builder) 10 | { 11 | // see https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests for things you could place here 12 | 13 | builder?.ConfigureServices(services => 14 | { 15 | // Build the service provider. 16 | services.BuildServiceProvider(); 17 | }); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CA2007: Consider calling ConfigureAwait on the awaited task 4 | dotnet_diagnostic.CA2007.severity = none 5 | 6 | # S125: Sections of code should not be commented out 7 | dotnet_diagnostic.S125.severity = silent 8 | -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/BreakHandlerAdapterTests.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools; 2 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 3 | using Codeuctivity.OpenXmlToHtml; 4 | using Moq; 5 | using System.Linq; 6 | using System.Xml.Linq; 7 | using Xunit; 8 | 9 | namespace OpenXmlToHtmlTests 10 | { 11 | public class BreakHandlerAdapterTests 12 | { 13 | [Fact] 14 | public void ShouldTranslatePageBreaks() 15 | { 16 | var breakHandler = new Mock(); 17 | var breakHandlerAdapter = new PageBreakHandler(breakHandler.Object); 18 | 19 | var element = new XElement("br", new XAttribute(W.type, "page")); 20 | 21 | var actual = breakHandlerAdapter.TransformBreak(element); 22 | 23 | Assert.Equal("
", actual.Single().ToString()); 24 | breakHandler.Verify(m => m.TransformBreak(It.IsAny()), Times.Never()); 25 | } 26 | 27 | [Fact] 28 | public void ShouldTranslatePage() 29 | { 30 | var breakHandler = new Mock(); 31 | var breakHandlerAdapter = new PageBreakHandler(breakHandler.Object); 32 | 33 | var element = new XElement("br"); 34 | 35 | breakHandlerAdapter.TransformBreak(element); 36 | 37 | breakHandler.Verify(m => m.TransformBreak(element), Times.Once); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/BasicTextFormated.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/BasicTextFormated.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/EmptyDocument.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/EmptyDocument.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/Font.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/Font.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/Font.docx.png.diff.linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/Font.docx.png.diff.linux.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/Font.docx.png.diff.win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/Font.docx.png.diff.win.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/Images.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/Images.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/Images.docx.png.diff.linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/Images.docx.png.diff.linux.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/SymbolRibbon.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/SymbolRibbon.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/Symbols.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/Symbols.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/Wingdings.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/Wingdings.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExpectedTestOutcome/WingdingsSymbols.docx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/ExpectedTestOutcome/WingdingsSymbols.docx.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/ExportImageHandlerTests.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; 2 | using Codeuctivity.OpenXmlToHtml; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | 9 | namespace OpenXmlToHtmlTests 10 | { 11 | public class ExportImageHandlerTests 12 | { 13 | [Fact] 14 | public async Task ShouldUseExportImageHandlerAsync() 15 | { 16 | var sourcePngFilePath = $"../../../TestInput/TestInput.png"; 17 | var exportTarget = new Dictionary(); 18 | using var fileStream = new FileStream(sourcePngFilePath, FileMode.Open, FileAccess.Read); 19 | using var memorystream = new MemoryStream(); 20 | await fileStream.CopyToAsync(memorystream); 21 | var expectedImage = memorystream.ToArray(); 22 | 23 | fileStream.Position = 0; 24 | 25 | var imageInfo = new ImageInfo 26 | { 27 | AltText = "AltText", 28 | Image = fileStream, 29 | }; 30 | 31 | var exportImageHandler = new ExportImageHandler(exportTarget); 32 | 33 | var actual = exportImageHandler.TransformImage(imageInfo); 34 | 35 | Assert.True(exportTarget.Count == 1); 36 | var exportedImage = exportTarget.Single(); 37 | 38 | Assert.Equal(exportedImage.Value, expectedImage); 39 | Assert.Equal($"\"AltText\"", actual.ToString()); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/OpenXmlToHtmlIntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.HtmlRenderer; 2 | using Codeuctivity.OpenXmlToHtml; 3 | using PdfSharp.Pdf.IO; 4 | using SixLabors.ImageSharp; 5 | using SixLabors.ImageSharp.PixelFormats; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Xml; 12 | using Xunit; 13 | 14 | namespace OpenXmlToHtmlTests 15 | { 16 | public class OpenXmlToHtmlIntegrationTests 17 | { 18 | private const string xhtmlPrimer = "(); 58 | 59 | var actuelHtml = await OpenXmlToHtml.ConvertToHtmlAsync(sourceIpenXml, "fallbackTitle", exportedImages, false); 60 | 61 | Assert.Equal(2, exportedImages.Count); 62 | 63 | Assert.True(IsValidBitmap(exportedImages.First().Value)); 64 | Assert.True(IsValidBitmap(exportedImages.Last().Value)); 65 | 66 | AssertXhtmlIsValid(actuelHtml); 67 | } 68 | 69 | private static bool IsValidBitmap(byte[] blob) 70 | { 71 | var image = Image.Load(blob); 72 | return image.Width > 1 && image.Height > 1; 73 | } 74 | 75 | [Fact] 76 | public async Task ShouldConvertDocumentIntegrativeWithToExpectedPageQuantityTest() 77 | { 78 | var testFileName = "TwoPages.docx"; 79 | var sourceOpenXmlFilePath = $"../../../TestInput/{testFileName}"; 80 | var actualHtmlFilePath = Path.Combine(Path.GetTempPath(), $"Actual{testFileName}.html"); 81 | 82 | if (File.Exists(actualHtmlFilePath)) 83 | { 84 | File.Delete(actualHtmlFilePath); 85 | } 86 | 87 | await openXmlToHtml.ConvertToHtmlAsync(sourceOpenXmlFilePath, actualHtmlFilePath); 88 | 89 | AssertXhtmlIsValid(actualHtmlFilePath); 90 | await using var chromiumRenderer = await Renderer.CreateAsync(); 91 | var pathPdfizedHtml = actualHtmlFilePath + ".pdf"; 92 | await chromiumRenderer.ConvertHtmlToPdf(actualHtmlFilePath, pathPdfizedHtml); 93 | AssertPdfPageCount(pathPdfizedHtml, 2); 94 | } 95 | 96 | private static void AssertPdfPageCount(string pathPdfizedHtml, int expectePageQuantity) 97 | { 98 | var pdfReader = PdfReader.Open(pathPdfizedHtml, PdfDocumentOpenMode.ReadOnly); 99 | Assert.Equal(expectePageQuantity, pdfReader.PageCount); 100 | } 101 | 102 | private static void AssertXhtmlIsValid(string actualHtmlFilePath) 103 | { 104 | var messages = new StringBuilder(); 105 | var settings = new XmlReaderSettings { ValidationType = ValidationType.Schema, DtdProcessing = DtdProcessing.Ignore }; 106 | settings.ValidationEventHandler += (sender, args) => messages.AppendLine(args.Message); 107 | var reader = XmlReader.Create(actualHtmlFilePath, settings); 108 | #pragma warning disable S108 // Nested blocks of code should not be left empty 109 | while (reader.Read()) { } 110 | #pragma warning restore S108 // Nested blocks of code should not be left empty 111 | 112 | if (!File.ReadAllText(actualHtmlFilePath).Contains(xhtmlPrimer)) 113 | { 114 | messages.AppendLine("Xhtml root element missing"); 115 | } 116 | 117 | Assert.True(messages.Length == 0, messages.ToString()); 118 | } 119 | 120 | private void AssertXhtmlIsValid(Stream actualHtml) 121 | { 122 | var messages = new StringBuilder(); 123 | var settings = new XmlReaderSettings { ValidationType = ValidationType.Schema, DtdProcessing = DtdProcessing.Ignore }; 124 | settings.ValidationEventHandler += (sender, args) => messages.AppendLine(args.Message); 125 | var reader = XmlReader.Create(actualHtml, settings); 126 | #pragma warning disable S108 // Nested blocks of code should not be left empty 127 | while (reader.Read()) { } 128 | #pragma warning restore S108 // Nested blocks of code should not be left empty 129 | 130 | Assert.True(messages.Length == 0, messages.ToString()); 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/OpenXmlToHtmlTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | false 6 | 9.0 7 | enable 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | all 24 | 25 | 26 | 27 | 28 | runtime; build; native; contentfiles; analyzers; buildtransitive 29 | all 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/SymbolHandlerTests.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools; 2 | using Codeuctivity.OpenXmlToHtml; 3 | using System.Collections.Generic; 4 | using System.Xml.Linq; 5 | using Xunit; 6 | 7 | namespace OpenXmlToHtmlTests 8 | { 9 | public class SymbolHandlerTests 10 | { 11 | [Fact] 12 | public void ShouldTranslateSymbolsBulletPointToUnicodeMiddlePoint() 13 | { 14 | var fontFamily = new Dictionary 15 | { 16 | { "font-family", "Symbol" } 17 | }; 18 | 19 | var symbolHandler = new SymbolHandler(); 20 | var element = new XElement("symbol", new XAttribute(W._char, "F0D7")); 21 | 22 | var actual = symbolHandler.TransformSymbol(element, fontFamily); 23 | 24 | Assert.Equal("", actual.ToString()); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInfrastructure/DocumentAsserter.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.HtmlRenderer; 2 | using Codeuctivity.OpenXmlToHtml.Tooling; 3 | using Codeuctivity.SkiaSharpCompare; 4 | using SkiaSharp; 5 | using System; 6 | using System.IO; 7 | using System.Runtime.InteropServices; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | 11 | namespace OpenXmlToHtmlTests 12 | { 13 | internal static class DocumentAsserter 14 | { 15 | internal static async Task AssertRenderedHtmlIsEqual(string actualFilePath, string expectReferenceFilePath, int allowedPixelErrorCount) 16 | { 17 | var actualFullPath = Path.GetFullPath(actualFilePath); 18 | var expectFullPath = Path.GetFullPath(expectReferenceFilePath); 19 | 20 | Assert.True(File.Exists(actualFullPath), $"actualFilePath not found {actualFullPath}"); 21 | 22 | var pathRasterizedHtml = actualFilePath + ".png"; 23 | try 24 | { 25 | await using var chromiumRenderer = await Renderer.CreateAsync(); 26 | await chromiumRenderer.ConvertHtmlToPng(actualFilePath, pathRasterizedHtml); 27 | } 28 | catch (PuppeteerSharp.ProcessException exception) 29 | { 30 | if (exception.Message.Contains("Failed to launch browser!")) 31 | { 32 | throw new Exception($"Run '{Linux.ChromiumInstallCommand}' in your test sytem.", exception); 33 | } 34 | 35 | throw; 36 | } 37 | Assert.True(File.Exists(expectFullPath), $"ExpectReferenceFilePath not found \n{expectFullPath}\n copy over \n{pathRasterizedHtml}\n if this is a new test case."); 38 | 39 | await AssertImageIsEqualAsync(pathRasterizedHtml, expectReferenceFilePath, allowedPixelErrorCount); 40 | } 41 | 42 | internal static async Task AssertImageIsEqualAsync(string actualImagePath, string expectImageFilePath, int allowedPixelErrorCount) 43 | { 44 | var actualFullPath = Path.GetFullPath(actualImagePath); 45 | var expectFullPath = Path.GetFullPath(expectImageFilePath); 46 | 47 | // Uncomment following line to update or create an expectation file 48 | //File.Copy(actualImagePath, expectImageFilePath, true); 49 | 50 | Assert.True(File.Exists(actualFullPath), $"actualImagePath not found {actualFullPath}"); 51 | Assert.True(File.Exists(expectFullPath), $"ExpectReferenceImagePath not found \n{expectFullPath}\n copy over \n{actualFullPath}\n if this is a new test case."); 52 | 53 | var filePathInTestResultFolderOfExpectation = SaveToTestresults(expectImageFilePath, "Expected" + Path.GetFileName(expectImageFilePath)); 54 | var filePathInTestResultFolderOfActual = SaveToTestresults(actualImagePath, Path.GetFileName(actualFullPath)); 55 | 56 | if (Compare.ImagesAreEqual(actualFullPath, expectFullPath)) 57 | { 58 | DroptFilesFromTestResultFolder(filePathInTestResultFolderOfExpectation, filePathInTestResultFolderOfActual); 59 | return; 60 | } 61 | 62 | var osSpecificDiffFileSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "win"; 63 | 64 | var allowedDiffImage = $"{expectFullPath}.diff.{osSpecificDiffFileSuffix}.png"; 65 | var newDiffImage = $"{actualFullPath}.diff.png"; 66 | 67 | if (!Compare.ImagesHaveEqualSize(actualFullPath, expectFullPath)) 68 | { 69 | Assert.Fail($"Actual Dimension differs from expected \nExpected {expectFullPath}\ndiffers to actual {actualFullPath} \nReplace {expectFullPath} with the new value."); 70 | } 71 | 72 | using (var maskImage = Compare.CalcDiffMaskImage(actualFullPath, expectFullPath)) 73 | { 74 | var png = maskImage.Encode(SKEncodedImageFormat.Png, 100); 75 | await File.WriteAllBytesAsync(newDiffImage, png.ToArray()); 76 | } 77 | 78 | // Uncomment following line to update or create an allowed diff file 79 | // File.Copy(newDiffImage, allowedDiffImage, true); 80 | 81 | if (File.Exists(allowedDiffImage)) 82 | { 83 | if (!Compare.ImagesHaveEqualSize(actualFullPath, allowedDiffImage)) 84 | { 85 | Assert.Fail($"AllowedDiffImage Dimension differs from allowed \nReplace {allowedDiffImage} with {actualFullPath}."); 86 | } 87 | 88 | var resultWithAllowedDiff = Compare.CalcDiff(actualFullPath, expectFullPath, allowedDiffImage); 89 | 90 | Assert.True(resultWithAllowedDiff.PixelErrorCount <= allowedPixelErrorCount, $"Expected PixelErrorCount beyond {allowedPixelErrorCount} but was {resultWithAllowedDiff.PixelErrorCount}\nExpected {expectFullPath}\ndiffers to actual {actualFullPath}\n Diff is {newDiffImage}\n"); 91 | 92 | DroptFilesFromTestResultFolder(filePathInTestResultFolderOfExpectation, filePathInTestResultFolderOfActual); 93 | return; 94 | } 95 | 96 | var result = Compare.CalcDiff(actualFullPath, expectFullPath); 97 | 98 | Assert.True(result.PixelErrorCount <= allowedPixelErrorCount, $"Expected PixelErrorCount beyond {allowedPixelErrorCount} but was {result.PixelErrorCount}\nExpected {expectFullPath}\ndiffers to actual {actualFullPath}\n Diff is {newDiffImage} \nReplace {actualFullPath} with the new value or store the diff as {allowedDiffImage}."); 99 | 100 | DroptFilesFromTestResultFolder(filePathInTestResultFolderOfExpectation, filePathInTestResultFolderOfActual); 101 | } 102 | 103 | private static void DroptFilesFromTestResultFolder(string filePathInTestResultFolderOfExpectation, string filePathInTestResultFolderOfActual) 104 | { 105 | File.Delete(filePathInTestResultFolderOfExpectation); 106 | File.Delete(filePathInTestResultFolderOfActual); 107 | } 108 | 109 | private static string SaveToTestresults(string filePath, string filename) 110 | { 111 | var netEnvironment = $"NetRuntime{Environment.Version}"; 112 | 113 | var testResultDirectory = Path.Combine(Environment.CurrentDirectory, "../../../../TestResult"); 114 | if (!Directory.Exists(testResultDirectory)) 115 | { 116 | Directory.CreateDirectory(testResultDirectory); 117 | } 118 | var destinationPathInTestResultDirectory = Path.Combine(testResultDirectory, netEnvironment + filename); 119 | 120 | if (File.Exists(destinationPathInTestResultDirectory)) 121 | { 122 | File.Delete(destinationPathInTestResultDirectory); 123 | } 124 | 125 | File.Copy(filePath, destinationPathInTestResultDirectory); 126 | 127 | return destinationPathInTestResultDirectory; 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/BasicTextFormated.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/BasicTextFormated.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/EmptyDocument.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/EmptyDocument.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/Font.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/Font.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/Images.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/Images.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/SymbolRibbon.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/SymbolRibbon.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/Symbols.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/Symbols.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/TestInput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/TestInput.png -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/TwoPages.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/TwoPages.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/Wingdings.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/Wingdings.docx -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TestInput/WingdingsSymbols.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeuctivity/OpenXmlToHtml/649ac1b8d6f6ccb18f32e0c217b8a770d566a45b/OpenXmlToHtmlTests/TestInput/WingdingsSymbols.pdf -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/TextSymbolToUnicodeHandlerTests.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlToHtml; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | 5 | namespace OpenXmlToHtmlTests 6 | { 7 | public class TextSymbolToUnicodeHandlerTests 8 | { 9 | [Theory] 10 | [InlineData("1", "•1", "Symbol")] 11 | [InlineData("1", "1", "arial")] 12 | public void ShouldTranslateTextWithCustomGlyphToUnicode(string original, string expectedEquivalent, string fontFamily) 13 | { 14 | var currentStyle = new Dictionary { { "font-family", fontFamily } }; 15 | 16 | var WordprocessingTextSymbolToUnicodeHandler = new TextSymbolToUnicodeHandler(); 17 | 18 | var actual = WordprocessingTextSymbolToUnicodeHandler.TransformText(original, currentStyle); 19 | 20 | Assert.Equal(expectedEquivalent, actual); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /OpenXmlToHtmlTests/WebSafeFontsHandlerTests.cs: -------------------------------------------------------------------------------- 1 | using Codeuctivity.OpenXmlPowerTools; 2 | using Codeuctivity.OpenXmlToHtml; 3 | 4 | using System.Xml.Linq; 5 | using Xunit; 6 | 7 | namespace OpenXmlToHtmlTests 8 | { 9 | public class WebSafeFontsHandlerTests 10 | { 11 | [Fact] 12 | public void ShouldTranslateFontInRunSymbolWithFontHandler() 13 | { 14 | var fontHandler = new WebSafeFontsHandler(); 15 | 16 | var element = new XElement("run", new XElement(W.sym, new XAttribute(W.font, "SomeBadSymbolFont")), new XAttribute(PtOpenXml.FontName, "SomeBadRunFont")); 17 | 18 | var actual = fontHandler.TranslateRunStyleFont(element); 19 | 20 | Assert.Equal("Arial", actual); 21 | } 22 | 23 | [Fact] 24 | public void ShouldTranslateFontInRunWithFontHandler() 25 | { 26 | var fontHandler = new WebSafeFontsHandler(); 27 | 28 | var element = new XElement("run", new XAttribute(PtOpenXml.FontName, "SomeBadRunFont")); 29 | 30 | var actual = fontHandler.TranslateRunStyleFont(element); 31 | 32 | Assert.Equal("Arial", actual); 33 | } 34 | 35 | [Fact] 36 | public void ShouldTranslateFontInParagraphWithFontHandler() 37 | { 38 | var fontHandler = new WebSafeFontsHandler(); 39 | 40 | var element = new XElement("run", new XAttribute(PtOpenXml.FontName, "SomeBadRunFont")); 41 | 42 | var actual = fontHandler.TranslateParagraphStyleFont(element); 43 | 44 | Assert.Equal("Arial", actual); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenXmlToHtml 2 | 3 | Converts docx to html 4 | 5 | [![Nuget](https://img.shields.io/nuget/v/Codeuctivity.OpenXmlToHtml.svg)](https://www.nuget.org/packages/Codeuctivity.OpenXmlToHtml/) [![Build](https://github.com/Codeuctivity/OpenXmlToHtml/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Codeuctivity/OpenXmlToHtml/actions/workflows/dotnet.yml) 6 | 7 | - .net implementation 8 | - No external infrastructure needed (No Microsoft Office or Libre Office needed) 9 | - Focused on Windows and Linux support 10 | - Demo CLI Api [OpenXmlToHtmlCli.zip](https://github.com/Codeuctivity/OpenXmlToHtml/releases) 11 | 12 | ```c# 13 | await OpenXmlToHtml.ConvertToHtmlAsync(inputPathDocx, outputPathHtml); 14 | ``` 15 | -------------------------------------------------------------------------------- /cla.md: -------------------------------------------------------------------------------- 1 | # Codeuctivity Individual Contributor License Agreement 2 | 3 | Thank you for your interest in contributing to open source software projects (“Projects”) made available by Codeuctivity. This Individual Contributor License Agreement (“Agreement”) sets out the terms governing any source code, object code, bug fixes, configuration changes, tools, specifications, documentation, data, materials, feedback, information or other works of authorship that you submit or have submitted, in any form and in any manner, to Codeuctivity in respect of any of the Projects (collectively “Contributions”). 4 | 5 | You agree that the following terms apply to all of your past, present and future Contributions. Except for the licenses granted in this Agreement, you retain all of your right, title and interest in and to your Contributions. 6 | 7 | **Copyright License.** You hereby grant, and agree to grant, to Codeuctivity a non-exclusive, perpetual, irrevocable, worldwide, fully-paid, royalty-free, transferable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute your Contributions and such derivative works, with the right to sublicense the foregoing rights through multiple tiers of sublicensees. 8 | 9 | **Patent License.** You hereby grant, and agree to grant, to Codeuctivity a non-exclusive, perpetual, irrevocable, 10 | worldwide, fully-paid, royalty-free, transferable patent license to make, have made, use, offer to sell, sell, 11 | import, and otherwise transfer your Contributions, where such license applies only to those patent claims 12 | licensable by you that are necessarily infringed by your Contributions alone or by combination of your 13 | Contributions with the Project to which such Contributions were submitted, with the right to sublicense the 14 | foregoing rights through multiple tiers of sublicensees. 15 | 16 | **Moral Rights.** To the fullest extent permitted under applicable law, you hereby waive, and agree not to 17 | assert, all of your “moral rights” in or relating to your Contributions for the benefit of Codeuctivity, its assigns, and 18 | their respective direct and indirect sublicensees. 19 | 20 | **Third Party Content/Rights.** If your Contribution includes or is based on any source code, object code, bug 21 | fixes, configuration changes, tools, specifications, documentation, data, materials, feedback, information or 22 | other works of authorship that were not authored by you (“Third Party Content”) or if you are aware of any 23 | third party intellectual property or proprietary rights associated with your Contribution (“Third Party Rights”), 24 | then you agree to include with the submission of your Contribution full details respecting such Third Party 25 | Content and Third Party Rights, including, without limitation, identification of which aspects of your 26 | Contribution contain Third Party Content or are associated with Third Party Rights, the owner/author of the 27 | Third Party Content and Third Party Rights, where you obtained the Third Party Content, and any applicable 28 | third party license terms or restrictions respecting the Third Party Content and Third Party Rights. For greater 29 | certainty, the foregoing obligations respecting the identification of Third Party Content and Third Party Rights 30 | do not apply to any portion of a Project that is incorporated into your Contribution to that same Project. 31 | 32 | **Representations.** You represent that, other than the Third Party Content and Third Party Rights identified by 33 | you in accordance with this Agreement, you are the sole author of your Contributions and are legally entitled 34 | to grant the foregoing licenses and waivers in respect of your Contributions. If your Contributions were 35 | created in the course of your employment with your past or present employer(s), you represent that such 36 | employer(s) has authorized you to make your Contributions on behalf of such employer(s) or such employer 37 | (s) has waived all of their right, title or interest in or to your Contributions. 38 | 39 | **Disclaimer.** To the fullest extent permitted under applicable law, your Contributions are provided on an "asis" 40 | basis, without any warranties or conditions, express or implied, including, without limitation, any implied 41 | warranties or conditions of non-infringement, merchantability or fitness for a particular purpose. You are not 42 | required to provide support for your Contributions, except to the extent you desire to provide support. 43 | 44 | **No Obligation.** You acknowledge that Codeuctivity is under no obligation to use or incorporate your Contributions 45 | into any of the Projects. The decision to use or incorporate your Contributions into any of the Projects will be 46 | made at the sole discretion of Codeuctivity or its authorized delegates. 47 | 48 | **Disputes.** This Agreement shall be governed by and construed in accordance with the laws of Austria, without giving effect to its principles or rules regarding conflicts of laws, 49 | other than such principles directing application of Austrian law. The parties hereby submit to venue in, and 50 | jurisdiction of the courts located in Vienna, Austria for purposes relating to this Agreement. In the event 51 | that any of the provisions of this Agreement shall be held by a court or other tribunal of competent jurisdiction 52 | to be unenforceable, the remaining portions hereof shall remain in full force and effect. 53 | 54 | **Assignment.** You agree that Codeuctivity may assign this Agreement, and all of its rights, obligations and licenses 55 | hereunder. 56 | -------------------------------------------------------------------------------- /signatures/version1/cla.json: -------------------------------------------------------------------------------- 1 | { 2 | "signedContributors": [] 3 | } -------------------------------------------------------------------------------- /testenvironments.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1", 3 | "environments": [ 4 | { 5 | "name": "Ubuntu", 6 | "type": "wsl", 7 | "wslDistribution": "Ubuntu" 8 | } 9 | ] 10 | } 11 | --------------------------------------------------------------------------------