diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..fa9d759e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,209 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true:silent +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent + +# Code-block preferences +csharp_prefer_braces = true:warning +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = suggestion + + +# IDE0011: Add braces +dotnet_diagnostic.IDE0011.severity = warning +csharp_style_prefer_method_group_conversion = true:silent \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..7b0e7e65 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,15 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. + +# More details are here: https://help.github.com/articles/about-codeowners/ + +# The '*' pattern is global owners. + +# Order is important. The last matching pattern has the most precedence. +# The folders are ordered as follows: + +# In each subsection folders are ordered first by depth, then alphabetically. +# This should make it easy to add new rules without breaking existing ones. + +# Global rule: +* @stesee \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..523c45ce --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,47 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: weekly + rebase-strategy: auto + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: weekly + rebase-strategy: auto + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: weekly + rebase-strategy: auto + + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "daily" + ignore: + - dependency-name: "SixLabors.ImageSharp" + versions: ["3.x", "4.x"] + - dependency-name: "coverlet.collector" + - dependency-name: "Codeuctivity.HtmlRenderer" + - dependency-name: "nunit" + - dependency-name: "SonarAnalyzer.CSharp" + - dependency-name: "AngleSharp" + - dependency-name: "Microsoft.NET.Test.Sdk" + - dependency-name: "Microsoft.AspNetCore.Mvc.Testing" + - dependency-name: "Moq" + - dependency-name: "xunit" + - dependency-name: "xunit.runner.visualstudio" + - dependency-name: "MSTest.TestAdapter" + - dependency-name: "MSTest.TestFramework" + - dependency-name: "Codeuctivity.ImageSharpCompare" + - dependency-name: "NUnit3TestAdapter" + - dependency-name: "xunit.runner.console" \ No newline at end of file diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml new file mode 100644 index 00000000..6ebcdab1 --- /dev/null +++ b/.github/workflows/cla.yml @@ -0,0 +1,36 @@ +name: "CLA Assistant" +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +jobs: + CLAssistant: + runs-on: ubuntu-latest + steps: + - name: "CLA Assistant" + 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' + # Beta Release + uses: cla-assistant/github-action@v2.6.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # the below token should have repo scope and must be manually added by you in the repository's secret + PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + with: + path-to-signatures: "signatures/version1/cla.json" + path-to-document: "https://github.com/Codeuctivity/OpenXmlPowerTools/blob/main/cla.md" # e.g. a CLA or a DCO document + # branch should not be protected + branch: "cla" + allowlist: dependabot[bot],stesee + + #below are the optional inputs - If the optional inputs are not given, then default values will be taken + #remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) + #remote-repository-name: enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository) + #create-file-commit-message: 'For example: Creating file for storing CLA Signatures' + #signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo' + #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign' + #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' + #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' + #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) + #use-dco-flag: true - If you are using DCO instead of CLA diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 00000000..5955b1ac --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,91 @@ +name: .NET build and test +env: + CURRENT_VERSION: 9.0.${{ github.run_number }} + LAST_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + +on: + push: + pull_request: + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v5 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --configuration Release --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal --configuration Release --logger "trx;LogFileName=${{ runner.workspace }}/OpenXmlPowerTools/TestResult/test_results.trx" + - name: Publish Unit Test Results + uses: actions/upload-artifact@v4 + if: failure() + with: + name: TestResult + path: "TestResult/**/*" + + deployRelease: + if: github.ref == 'refs/heads/release' + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v5 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --configuration Release --no-restore + - name: NugetPush + env: + NUGET_TOKEN_EXISTS: ${{ secrets.NUGET_TOKEN }} + if: env.NUGET_TOKEN_EXISTS != '' + run: | + dotnet nuget push ./OpenXmlPowerTools/bin/Release/*.nupkg --skip-duplicate --api-key ${{secrets.NUGET_TOKEN}} --source https://api.nuget.org/v3/index.json + - name: Github Release + shell: bash + env: + GITHUB_TOKEN: ${{ github.TOKEN }} + if: env.GITHUB_TOKEN != '' + run: | + gh release create ${{env.CURRENT_VERSION}} ./OpenXmlPowerTools/bin/Release/*.*nupkg --generate-notes + + deployTest: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v5 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --configuration Release --no-restore + - name: NugetPush + env: + NUGET_TOKEN_EXISTS: ${{ secrets.NUGET_TEST_TOKEN }} + if: env.NUGET_TOKEN_EXISTS != '' + run: | + ls ./OpenXmlPowerTools/bin/Release + dotnet nuget push ./OpenXmlPowerTools/bin/Release/*.nupkg --skip-duplicate --api-key ${{secrets.NUGET_TEST_TOKEN}} --source https://apiint.nugettest.org/v3/index.json + + - name: Github Prerelease + shell: bash + env: + GITHUB_TOKEN: ${{ github.TOKEN }} + if: env.GITHUB_TOKEN != '' + run: | + gh release create ${{env.CURRENT_VERSION}} ./OpenXmlPowerTools/bin/Release/*.*nupkg --prerelease --generate-notes diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..6736c096 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,27 @@ +# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. +# +# You can adjust the behavior by modifying this file. +# For more information, see: +# https://github.com/actions/stale +name: Mark stale issues and pull requests + +on: + schedule: + - cron: '44 1 * * *' + +jobs: + stale: + + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v10 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'Stale issue message' + stale-pr-message: 'Stale pull request message' + stale-issue-label: 'no-issue-activity' + stale-pr-label: 'no-pr-activity' diff --git a/.gitignore b/.gitignore index d66f2aea..1f6ad508 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ TestResults/ # JetBrains .idea/ _ReSharper.Caches/ + +TestResult/ \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..e7a120f8 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "redhat.vscode-yaml" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..bace6e86 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/OpenXmlPowerToolsExamples/DocumentAssembler/bin/Debug/net6.0/DocumentAssembler.dll", + "args": [], + "cwd": "${workspaceFolder}/OpenXmlPowerToolsExamples/DocumentAssembler", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..20a7d4c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cSpell.language": "en", + "cSpell.words": [ + "PPTX" + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..18459304 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,34 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "shell", + "args": [ + "build", + // Ask dotnet build to generate full paths for file names. + "/property:GenerateFullPaths=true", + // Do not generate summary otherwise it leads to duplicate errors in Problems panel + "/consoleloggerparameters:NoSummary" + ], + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "update nuget", + "command": "./.vscode/updateNuget.sh", + "args": [], + "group": "build", + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + } + ] +} diff --git a/.vscode/updateNuget.sh b/.vscode/updateNuget.sh new file mode 100755 index 00000000..691c854e --- /dev/null +++ b/.vscode/updateNuget.sh @@ -0,0 +1,13 @@ +#!/bin/bash +regex='PackageReference Include="([^"]*)" Version="([^"]*)"' +find . -name "*.*proj" | while read proj; do + while read line; do + if [[ $line =~ $regex ]]; then + name="${BASH_REMATCH[1]}" + version="${BASH_REMATCH[2]}" + if [[ $version != *-* ]]; then + dotnet add "$proj" package "$name" + fi + fi + done <"$proj" +done diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..45341b2e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,21 @@ +# Build and Test Instructions + +## Prerequisites +- .NET 8 SDK (`dotnet-sdk-8.0`) must be installed. + ```bash + apt-get update && apt-get install -y dotnet-sdk-8.0 + ``` + +## Build +From the repository root, run: +```bash +dotnet build +``` +This restores NuGet packages and compiles the library and example projects. + +## Test +Execute the unit tests with: +```bash +dotnet test +``` +The command builds required projects and runs the test suite. diff --git a/Directory.Build.props b/Directory.Build.props deleted file mode 100644 index 91ee077a..00000000 --- a/Directory.Build.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - true - - - - $(MSBuildThisFileDirectory)\rules.ruleset - - diff --git a/Directory.Build.targets b/Directory.Build.targets deleted file mode 100644 index c1f3257c..00000000 --- a/Directory.Build.targets +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - All - - - diff --git a/LICENSE b/LICENSE.md similarity index 97% rename from LICENSE rename to LICENSE.md index cce6ed6a..06188368 100644 --- a/LICENSE +++ b/LICENSE.md @@ -1,4 +1,4 @@ -The MIT License (MIT) +# The MIT License (MIT) Copyright (c) Microsoft Corporation diff --git a/NewDocxDocuments/GenerateNewDocxCmdlet.ps1 b/NewDocxDocuments/GenerateNewDocxCmdlet.ps1 index b3b04b13..4b3bd8b8 100644 --- a/NewDocxDocuments/GenerateNewDocxCmdlet.ps1 +++ b/NewDocxDocuments/GenerateNewDocxCmdlet.ps1 @@ -1,35 +1,24 @@ [environment]::CurrentDirectory = $(Get-Location) -if (-not $(Test-Path .\GenerateNewDocxCmdlet.ps1)) -{ +if (-not $(Test-Path .\GenerateNewDocxCmdlet.ps1)) { Throw "You must run this script from within the NewDocxDocuments directory" } $dx = "..\Cmdlets\DocxLib.ps1" -if (Test-Path $dx) { del $dx } +if (Test-Path $dx) { Remove-Item $dx } $lineBreak = [System.Environment]::NewLine [System.Text.StringBuilder]$sbDxl = New-Object -TypeName System.Text.StringBuilder $copyrightString = @" -<#*************************************************************************** - -Copyright (c) Microsoft Corporation 2015. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -Published at http://OpenXmlDeveloper.org -Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx - -***************************************************************************#> +<# Copyright (c) Microsoft. All rights reserved. + Licensed under the MIT license. See LICENSE file in the project root for full license information.#> "@ [void]$sbDxl.Append($copyrightString + $lineBreak) -dir *.docx | % { +Get-ChildItem *.docx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbDxl.Append("`$SampleDocx$($_.BaseName) =" + $lineBreak) $b64 = $(ConvertTo-Base64 $_ -PowerShellLiteral) @@ -42,8 +31,7 @@ $template = [System.IO.File]::ReadAllLines("..\Cmdlets\New-Docx-Template.ps1") $paramDocs = -1; $paramDecl = -1; $paramUse = -1; -for ($i = 0; $i -lt $template.Length; $i++) -{ +for ($i = 0; $i -lt $template.Length; $i++) { $t = $template[$i] if ($t.Contains("ParameterDocumentation")) { $paramDocs = $i } if ($t.Contains("ParameterDeclaration")) { $paramDecl = $i } @@ -51,44 +39,39 @@ for ($i = 0; $i -lt $template.Length; $i++) } $ndx = "..\Cmdlets\New-Docx.ps1" -if (Test-Path $ndx) -{ +if (Test-Path $ndx) { Remove-Item $ndx } $sbGenNewDocx = New-Object System.Text.StringBuilder; $template[0..($paramDocs - 1)] | % { [void]$sbGenNewDocx.Append($_ + $lineBreak) } -dir *.docx | % { +Get-ChildItem *.docx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbGenNewDocx.Append(" .PARAMETER $($fi.BaseName)" + $lineBreak) $fiDesc = New-Object System.IO.FileInfo $($_.BaseName + ".txt") - if ($fiDesc.Exists) - { - Get-Content $($fiDesc.FullName) | % { [void]$sbGenNewDocx.Append(' ' + $_ + $lineBreak) } + if ($fiDesc.Exists) { + Get-Content $($fiDesc.FullName) | ForEach-Object { [void]$sbGenNewDocx.Append(' ' + $_ + $lineBreak) } } - else - { + else { $errMessage = "Error: $($fi.BaseName).docx does not have a corresponding $($fi.BaseName).txt" - Write-Host -ForegroundColor Red $errMessage + Write-Error $errMessage [void]$sbGenNewDocx.Append(' ' + $errMessage + $lineBreak) } } $start = $paramDocs + 1 $end = $paramDecl - 1 -$template[$start..$end] | % { [void]$sbGenNewDocx.Append($_ + $lineBreak) } -$last = (($(dir *.docx) | measure).Count) - 1 +$template[$start..$end] | ForEach-Object { [void]$sbGenNewDocx.Append($_ + $lineBreak) } +$last = (($(Get-ChildItem *.docx) | Measure-Object).Count) - 1 $count = 0 -dir *.docx | % { +Get-ChildItem *.docx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbGenNewDocx.Append(' [Parameter(Mandatory=$False)]' + $lineBreak) [void]$sbGenNewDocx.Append(' [Switch]' + $lineBreak) - if ($count -ne $last) - { + if ($count -ne $last) { [void]$sbGenNewDocx.Append(" [bool]`$$($_.BaseName)," + $lineBreak) } - else - { + else { [void]$sbGenNewDocx.Append(" [bool]`$$($_.BaseName)" + $lineBreak) } [void]$sbGenNewDocx.Append($lineBreak) @@ -96,12 +79,12 @@ dir *.docx | % { } $start = $paramDecl + 1 $end = $paramUse - 1 -$template[$start..$end] | % { [void]$sbGenNewDocx.Append($_ + $lineBreak) } -dir *.docx | % { +$template[$start..$end] | ForEach-Object { [void]$sbGenNewDocx.Append($_ + $lineBreak) } +Get-ChildItem *.docx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbGenNewDocx.Append(" if (`$All -or `$$($fi.BaseName)) { AppendDoc `$srcList `$SampleDocx$($fi.BaseName) `"$($fi.BaseName)`" }" + $lineBreak) } $start = $paramUse + 1 -$template[$start..99999] | % { [void]$sbGenNewDocx.Append($_ + $lineBreak) } +$template[$start..99999] | ForEach-Object { [void]$sbGenNewDocx.Append($_ + $lineBreak) } Set-Content -Value $sbGenNewDocx.ToString() -Path $ndx -Encoding UTF8 diff --git a/NewPptxPresentations/GenerateNewPptxCmdlet.ps1 b/NewPptxPresentations/GenerateNewPptxCmdlet.ps1 index 2ceb7325..a877aa7b 100644 --- a/NewPptxPresentations/GenerateNewPptxCmdlet.ps1 +++ b/NewPptxPresentations/GenerateNewPptxCmdlet.ps1 @@ -1,35 +1,24 @@ [environment]::CurrentDirectory = $(Get-Location) -if (-not $(Test-Path .\GenerateNewPptxCmdlet.ps1)) -{ +if (-not $(Test-Path .\GenerateNewPptxCmdlet.ps1)) { Throw "You must run this script from within the NewPptxPresentations directory" } $dx = "..\Cmdlets\PptxLib.ps1" -if (Test-Path $dx) { del $dx} +if (Test-Path $dx) { Remove-Item $dx } $lineBreak = [System.Environment]::NewLine [System.Text.StringBuilder]$sbDxl = New-Object -TypeName System.Text.StringBuilder $copyrightString = @" -<#*************************************************************************** - -Copyright (c) Microsoft Corporation 2015. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -Published at http://OpenXmlDeveloper.org -Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx - -***************************************************************************#> +<#Copyright (c) Microsoft. All rights reserved. +Licensed under the MIT license. See LICENSE file in the project root for full license information.#> "@ [void]$sbDxl.Append($copyrightString + $lineBreak) -dir *.pptx | % { +Get-ChildItem *.pptx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbDxl.Append("`$SamplePptx$($_.BaseName) =" + $lineBreak) $b64 = $(ConvertTo-Base64 $_ -PowerShellLiteral) @@ -42,8 +31,7 @@ $template = [System.IO.File]::ReadAllLines("..\Cmdlets\New-Pptx-Template.ps1") $paramDocs = -1; $paramDecl = -1; $paramUse = -1; -for ($i = 0; $i -lt $template.Length; $i++) -{ +for ($i = 0; $i -lt $template.Length; $i++) { $t = $template[$i] if ($t.Contains("ParameterDocumentation")) { $paramDocs = $i } if ($t.Contains("ParameterDeclaration")) { $paramDecl = $i } @@ -51,44 +39,39 @@ for ($i = 0; $i -lt $template.Length; $i++) } $npx = "..\Cmdlets\New-Pptx.ps1" -if (Test-Path $npx) -{ +if (Test-Path $npx) { Remove-Item $npx } $sbGenNewPptx = New-Object System.Text.StringBuilder; -$template[0..($paramDocs - 1)] | % { [void]$sbGenNewPptx.Append($_ + $lineBreak) } -dir *.pptx | % { +$template[0..($paramDocs - 1)] | ForEach-Object { [void]$sbGenNewPptx.Append($_ + $lineBreak) } +Get-ChildItem *.pptx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbGenNewPptx.Append(" .PARAMETER $($fi.BaseName)" + $lineBreak) $fiDesc = New-Object System.IO.FileInfo $($_.BaseName + ".txt") - if ($fiDesc.Exists) - { - Get-Content $($fiDesc.FullName) | % { [void]$sbGenNewPptx.Append(' ' + $_ + $lineBreak) } + if ($fiDesc.Exists) { + Get-Content $($fiDesc.FullName) | ForEach-Object { [void]$sbGenNewPptx.Append(' ' + $_ + $lineBreak) } } - else - { + else { $errMessage = "Error: $($fi.BaseName).pptx does not have a corresponding $($fi.BaseName).txt" - Write-Host -ForegroundColor Red $errMessage + Write-Error $errMessage [void]$sbGenNewPptx.Append(' ' + $errMessage + $lineBreak) } } $start = $paramDocs + 1 $end = $paramDecl - 1 -$template[$start..$end] | % { [void]$sbGenNewPptx.Append($_ + $lineBreak) } -$last = (($(dir *.pptx) | measure).Count) - 1 +$template[$start..$end] | ForEach-Object { [void]$sbGenNewPptx.Append($_ + $lineBreak) } +$last = (($(Get-ChildItem *.pptx) | Measure-Object).Count) - 1 $count = 0 -dir *.pptx | % { +Get-ChildItem *.pptx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbGenNewPptx.Append(' [Parameter(Mandatory=$False)]' + $lineBreak) [void]$sbGenNewPptx.Append(' [Switch]' + $lineBreak) - if ($count -ne $last) - { + if ($count -ne $last) { [void]$sbGenNewPptx.Append(" [bool]`$$($_.BaseName)," + $lineBreak) } - else - { + else { [void]$sbGenNewPptx.Append(" [bool]`$$($_.BaseName)" + $lineBreak) } [void]$sbGenNewPptx.Append($lineBreak) @@ -96,12 +79,12 @@ dir *.pptx | % { } $start = $paramDecl + 1 $end = $paramUse - 1 -$template[$start..$end] | % { [void]$sbGenNewPptx.Append($_ + $lineBreak) } -dir *.pptx | % { +$template[$start..$end] | ForEach-Object { [void]$sbGenNewPptx.Append($_ + $lineBreak) } +Get-ChildItem *.pptx | ForEach-Object { $fi = New-Object System.IO.FileInfo $_ [void]$sbGenNewPptx.Append(" if (`$All -or `$$($fi.BaseName)) { AppendPresentation `$srcList `$SamplePptx$($fi.BaseName) `"$($fi.BaseName)`" }" + $lineBreak) } $start = $paramUse + 1 -$template[$start..99999] | % { [void]$sbGenNewPptx.Append($_ + $lineBreak) } +$template[$start..99999] | ForEach-Object { [void]$sbGenNewPptx.Append($_ + $lineBreak) } Set-Content -Value $sbGenNewPptx.ToString() -Path $npx -Encoding UTF8 diff --git a/OpenXmlPowerTools.Tests.OA/HtmlToWmlConverterTests2.cs b/OpenXmlPowerTools.Tests.OA/HtmlToWmlConverterTests2.cs deleted file mode 100644 index 9ebce63a..00000000 --- a/OpenXmlPowerTools.Tests.OA/HtmlToWmlConverterTests2.cs +++ /dev/null @@ -1,558 +0,0 @@ -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2012-2015. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -Published at http://OpenXmlDeveloper.org -Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx - -Developer: Eric White -Blog: http://www.ericwhite.com -Twitter: @EricWhiteDev -Email: eric@ericwhite.com - -***************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; -using Xunit; -using HtmlAgilityPack; -using System.Text.RegularExpressions; - -/******************************************************************************************* - * HtmlToWmlConverter expects the HTML to be passed as an XElement, i.e. as XML. While the HTML test files that - * are included in Open-Xml-PowerTools are able to be read as XML, most HTML is not able to be read as XML. - * The best solution is to use the HtmlAgilityPack, which can parse HTML and save as XML. The HtmlAgilityPack - * is licensed under the Ms-PL (same as Open-Xml-PowerTools) so it is convenient to include it in your solution, - * and thereby you can convert HTML to XML that can be processed by the HtmlToWmlConverter. - * - * A convenient way to get the DLL that has been checked out with HtmlToWmlConverter is to clone the repo at - * https://github.com/EricWhiteDev/HtmlAgilityPack - * - * That repo contains only the DLL that has been checked out with HtmlToWmlConverter. - * - * Of course, you can also get the HtmlAgilityPack source and compile it to get the DLL. You can find it at - * http://codeplex.com/HtmlAgilityPack - * - * We don't include the HtmlAgilityPack in Open-Xml-PowerTools, to simplify installation. The XUnit tests in - * this module do not require the HtmlAgilityPack to run. -*******************************************************************************************/ - -#if DO_CONVERSION_VIA_WORD -using Word = Microsoft.Office.Interop.Word; -#endif - -/*************************************************************************************************** - * The XUnit tests in this module are not included in the standard Open-Xml-PowerTools tests because - * they use either Word automation, the HtmlAgilityPack, or both. -***************************************************************************************************/ - -namespace OxPt -{ - public class HwTests2 - { - static bool s_ProduceAnnotatedHtml = true; - - // PowerShell oneliner that generates InlineData for all files in a directory - // dir | % { '[InlineData("' + $_.Name + '")]' } | clip - - [Theory] - [InlineData("HW002-Table01.docx")] - [InlineData("HW002-Table02.docx")] - [InlineData("HW002-Table03.docx")] - [InlineData("HW002-Table04.docx")] - [InlineData("HW002-Table05.docx")] - [InlineData("HW002-Table06.docx")] - [InlineData("HW002-Table07.docx")] - [InlineData("HW002-Table08.docx")] - [InlineData("HW002-Table09.docx")] - [InlineData("HW002-Table10.docx")] - [InlineData("HW002-Table11.docx")] - [InlineData("HW002-Table12.docx")] - [InlineData("HW002-Table13.docx")] - [InlineData("HW002-Table14.docx")] - [InlineData("HW002-Table15.docx")] - [InlineData("HW002-Table16.docx")] - [InlineData("HW002-Table17.docx")] - [InlineData("HW002-Table18.docx")] - public void HW002(string name) - { - var sourceDocxFi = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, name)); - - var sourceCopiedToDestDocxFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-2-Source.docx"))); - var sourceCopiedToDestHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-2-Source.html"))); - var destCssFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-3.css"))); - var destDocxFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-4-ConvertedByHtmlToWml.docx"))); - var annotatedHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-5-Annotated.txt"))); - - File.Copy(sourceDocxFi.FullName, sourceCopiedToDestDocxFi.FullName); - - WordAutomationUtilities.SaveAsHtmlUsingWord(sourceDocxFi, sourceCopiedToDestHtmlFi); - XElement html = null; - int cnt = 0; - while (true) - { - try - { - html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceCopiedToDestHtmlFi); - break; - } - catch (XmlException e) - { - throw e; - } - catch (IOException i) - { - if (++cnt == 20) - throw i; - System.Threading.Thread.Sleep(50); - continue; - } - } - - string usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); - File.WriteAllText(destCssFi.FullName, usedAuthorCss); - - HtmlToWmlConverterSettings settings = HtmlToWmlConverter.GetDefaultSettings(); - // image references in HTML files contain the path to the subdir that contains the images, so base URI is the name of the directory - // that contains the HTML files - settings.BaseUriForImages = Path.Combine(TestUtil.TempDir.FullName); - - WmlDocument doc = HtmlToWmlConverter.ConvertHtmlToWml( - defaultCss, - usedAuthorCss, - userCss, - html, - settings, - null, // use the default EmptyDocument - s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null); - - Assert.NotNull(doc); - - if (doc != null) - SaveValidateAndFormatMainDocPart(destDocxFi, doc); - -#if DO_CONVERSION_VIA_WORD - var newAltChunkBeforeFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-5-AltChunkBefore.docx"))); - var newAltChunkAfterFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-6-ConvertedViaWord.docx"))); - WordAutomationUtilities.DoConversionViaWord(newAltChunkBeforeFi, newAltChunkAfterFi, html); -#endif - } - - [Theory] - [InlineData("T0015.html")] - public void HW003(string name) - { - string testDocPrefix = "HW003_"; - var sourceHtmlFi = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, name)); - - var sourceCopiedToDestHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, (testDocPrefix + sourceHtmlFi.Name).Replace(".html", "-1-Source.html"))); - var destCssFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, (testDocPrefix + sourceHtmlFi.Name).Replace(".html", "-2.css"))); - var destDocxFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, (testDocPrefix + sourceHtmlFi.Name).Replace(".html", "-3-ConvertedByHtmlToWml.docx"))); - var annotatedHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, (testDocPrefix + sourceHtmlFi.Name).Replace(".html", "-4-Annotated.txt"))); - - File.Copy(sourceHtmlFi.FullName, sourceCopiedToDestHtmlFi.FullName); - XElement html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceCopiedToDestHtmlFi); - - string usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); - File.WriteAllText(destCssFi.FullName, usedAuthorCss); - - HtmlToWmlConverterSettings settings = HtmlToWmlConverter.GetDefaultSettings(); - settings.BaseUriForImages = Path.Combine(TestUtil.TempDir.FullName); - settings.DefaultBlockContentMargin = "36pt"; - - WmlDocument doc = HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings, null, s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null); - Assert.NotNull(doc); - if (doc != null) - SaveValidateAndFormatMainDocPart(destDocxFi, doc); - } - - [Theory] - [InlineData("HW010-Symbols01.docx")] - [InlineData("HW010-Symbols02.docx")] - [InlineData("HW010-TableWithEmptyRows.docx")] - [InlineData("HW010-TableWithThreeEmptyRows.docx")] - [InlineData("HW010-TableWithImage.docx")] - [InlineData("HW010-SpanWithSingleSpace.docx")] - [InlineData("HW010-Tab01.docx")] - - public void HW010(string name) - { - var sourceDocxFi = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, name)); - - var sourceCopiedToDestDocxFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-2-Source.docx"))); - var sourceCopiedToDestHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-2-Source.html"))); - var destCssFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-3.css"))); - var destDocxFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-4-ConvertedByHtmlToWml.docx"))); - var annotatedHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-5-Annotated.txt"))); - - File.Copy(sourceDocxFi.FullName, sourceCopiedToDestDocxFi.FullName); - - SaveAsHtmlUsingHtmlConverter(sourceCopiedToDestDocxFi.FullName, sourceCopiedToDestDocxFi.DirectoryName); - XElement html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceCopiedToDestHtmlFi); - - string usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); - File.WriteAllText(destCssFi.FullName, usedAuthorCss); - - var settingsWmlDocument = new WmlDocument(sourceCopiedToDestDocxFi.FullName); - HtmlToWmlConverterSettings settings = HtmlToWmlConverter.GetDefaultSettings(settingsWmlDocument); - // image references in HTML files contain the path to the subdir that contains the images, so base URI is the name of the directory - // that contains the HTML files - settings.BaseUriForImages = Path.Combine(TestUtil.TempDir.FullName); - - WmlDocument doc = HtmlToWmlConverter.ConvertHtmlToWml( - defaultCss, - usedAuthorCss, - userCss, - html, - settings, - null, // use the default EmptyDocument - s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null); - - Assert.NotNull(doc); - - if (doc != null) - SaveValidateAndFormatMainDocPart(destDocxFi, doc); - -#if DO_CONVERSION_VIA_WORD - var newAltChunkBeforeFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-5-AltChunkBefore.docx"))); - var newAltChunkAfterFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-6-ConvertedViaWord.docx"))); - WordAutomationUtilities.DoConversionViaWord(newAltChunkBeforeFi, newAltChunkAfterFi, html); -#endif - } - - private static void SaveAsHtmlUsingHtmlConverter(string file, string outputDirectory) - { - var fi = new FileInfo(file); - Console.WriteLine(fi.Name); - byte[] byteArray = File.ReadAllBytes(fi.FullName); - using (MemoryStream memoryStream = new MemoryStream()) - { - memoryStream.Write(byteArray, 0, byteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) - { - var destFileName = new FileInfo(fi.Name.Replace(".docx", ".html")); - if (outputDirectory != null && outputDirectory != string.Empty) - { - DirectoryInfo di = new DirectoryInfo(outputDirectory); - if (!di.Exists) - { - throw new OpenXmlPowerToolsException("Output directory does not exist"); - } - destFileName = new FileInfo(Path.Combine(di.FullName, destFileName.Name)); - } - var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - int imageCounter = 0; - - var pageTitle = fi.FullName; - var part = wDoc.CoreFilePropertiesPart; - if (part != null) - { - pageTitle = (string)part.GetXDocument().Descendants(DC.title).FirstOrDefault() ?? fi.FullName; - } - - // TODO: Determine max-width from size of content area. - HtmlConverterSettings settings = new HtmlConverterSettings() - { - AdditionalCss = "body { margin: 1cm auto; max-width: 20cm; padding: 0; }", - PageTitle = pageTitle, - FabricateCssClasses = true, - CssClassPrefix = "pt-", - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - ImageHandler = imageInfo => - { - DirectoryInfo localDirInfo = new DirectoryInfo(imageDirectoryName); - if (!localDirInfo.Exists) - localDirInfo.Create(); - ++imageCounter; - string extension = imageInfo.ContentType.Split('/')[1].ToLower(); - ImageFormat imageFormat = null; - if (extension == "png") - imageFormat = ImageFormat.Png; - else if (extension == "gif") - imageFormat = ImageFormat.Gif; - else if (extension == "bmp") - imageFormat = ImageFormat.Bmp; - else if (extension == "jpeg") - imageFormat = ImageFormat.Jpeg; - else if (extension == "tiff") - { - // Convert tiff to gif. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "x-wmf") - { - extension = "wmf"; - imageFormat = ImageFormat.Wmf; - } - - // If the image format isn't one that we expect, ignore it, - // and don't return markup for the link. - if (imageFormat == null) - return null; - - string imageFileName = imageDirectoryName + "/image" + - imageCounter.ToString() + "." + extension; - try - { - imageInfo.Bitmap.Save(imageFileName, imageFormat); - } - catch (System.Runtime.InteropServices.ExternalException) - { - return null; - } - string imageSource = localDirInfo.Name + "/image" + - imageCounter.ToString() + "." + extension; - - XElement img = new XElement(Xhtml.img, - new XAttribute(NoNamespace.src, imageSource), - imageInfo.ImgStyleAttribute, - imageInfo.AltText != null ? - new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); - return img; - } - }; - XElement htmlElement = HtmlConverter.ConvertToHtml(wDoc, settings); - - // Produce HTML document with declaration to tell the browser - // we are using HTML5. - var html = new XDocument( - new XDocumentType("html", null, null, null), - htmlElement); - - // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type - // XEntity. PtOpenXmlUtil.cs define the XEntity class. See - // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx - // for detailed explanation. - // - // If you further transform the XML tree returned by ConvertToHtmlTransform, you - // must do it correctly, or entities will not be serialized properly. - - var htmlString = html.ToString(SaveOptions.DisableFormatting); - File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); - } - } - } - - private static void SaveValidateAndFormatMainDocPart(FileInfo destDocxFi, WmlDocument doc) - { - WmlDocument formattedDoc; - - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(doc.DocumentByteArray, 0, doc.DocumentByteArray.Length); - using (WordprocessingDocument document = WordprocessingDocument.Open(ms, true)) - { - XDocument xDoc = document.MainDocumentPart.GetXDocument(); - document.MainDocumentPart.PutXDocumentWithFormatting(); - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(document); - var errorsString = errors - .Select(e => e.Description + Environment.NewLine) - .StringConcatenate(); - - // Assert that there were no errors in the generated document. - Assert.Equal("", errorsString); - } - formattedDoc = new WmlDocument(destDocxFi.FullName, ms.ToArray()); - } - formattedDoc.SaveAs(destDocxFi.FullName); - } - - - /* - * display property: - * - inline - * - block - * - list-item - * - inline-block - * - table - * - inline-table - * - table-row-group - * - table-header-group - * - table-footer-group - * - table-row - * - table-column-group - * - table-column - * - table-cell - * - table-caption - * - none - * - inherit - * - * position property: - * - static - * - relative - * - absolute - * - fixed - * - inherit - * - * top, left, bottom, right properties: - * (only apply if position property is not static) - */ - - static string defaultCss = - @"html, address, -blockquote, -body, dd, div, -dl, dt, fieldset, form, -frame, frameset, -h1, h2, h3, h4, -h5, h6, noframes, -ol, p, ul, center, -dir, hr, menu, pre { display: block; unicode-bidi: embed } -li { display: list-item } -head { display: none } -table { display: table } -tr { display: table-row } -thead { display: table-header-group } -tbody { display: table-row-group } -tfoot { display: table-footer-group } -col { display: table-column } -colgroup { display: table-column-group } -td, th { display: table-cell } -caption { display: table-caption } -th { font-weight: bolder; text-align: center } -caption { text-align: center } -body { margin: auto; } -h1 { font-size: 2em; margin: auto; } -h2 { font-size: 1.5em; margin: auto; } -h3 { font-size: 1.17em; margin: auto; } -h4, p, -blockquote, ul, -fieldset, form, -ol, dl, dir, -menu { margin: auto } -a { color: blue; } -h5 { font-size: .83em; margin: auto } -h6 { font-size: .75em; margin: auto } -h1, h2, h3, h4, -h5, h6, b, -strong { font-weight: bolder } -blockquote { margin-left: 40px; margin-right: 40px } -i, cite, em, -var, address { font-style: italic } -pre, tt, code, -kbd, samp { font-family: monospace } -pre { white-space: pre } -button, textarea, -input, select { display: inline-block } -big { font-size: 1.17em } -small, sub, sup { font-size: .83em } -sub { vertical-align: sub } -sup { vertical-align: super } -table { border-spacing: 2px; } -thead, tbody, -tfoot { vertical-align: middle } -td, th, tr { vertical-align: inherit } -s, strike, del { text-decoration: line-through } -hr { border: 1px inset } -ol, ul, dir, -menu, dd { margin-left: 40px } -ol { list-style-type: decimal } -ol ul, ul ol, -ul ul, ol ol { margin-top: 0; margin-bottom: 0 } -u, ins { text-decoration: underline } -br:before { content: ""\A""; white-space: pre-line } -center { text-align: center } -:link, :visited { text-decoration: underline } -:focus { outline: thin dotted invert } -/* Begin bidirectionality settings (do not change) */ -BDO[DIR=""ltr""] { direction: ltr; unicode-bidi: bidi-override } -BDO[DIR=""rtl""] { direction: rtl; unicode-bidi: bidi-override } -*[DIR=""ltr""] { direction: ltr; unicode-bidi: embed } -*[DIR=""rtl""] { direction: rtl; unicode-bidi: embed } - -"; - - // original defaultCss - // static string defaultCss = - // @"html, address, - //blockquote, - //body, dd, div, - //dl, dt, fieldset, form, - //frame, frameset, - //h1, h2, h3, h4, - //h5, h6, noframes, - //ol, p, ul, center, - //dir, hr, menu, pre { display: block; unicode-bidi: embed } - //li { display: list-item } - //head { display: none } - //table { display: table } - //tr { display: table-row } - //thead { display: table-header-group } - //tbody { display: table-row-group } - //tfoot { display: table-footer-group } - //col { display: table-column } - //colgroup { display: table-column-group } - //td, th { display: table-cell } - //caption { display: table-caption } - //th { font-weight: bolder; text-align: center } - //caption { text-align: center } - //body { margin: 8px; } - //h1 { font-size: 2em; margin: .67em 0em; } - //h2 { font-size: 1.5em; margin: .75em 0em; } - //h3 { font-size: 1.17em; margin: .83em 0em; } - //h4, p, - //blockquote, ul, - //fieldset, form, - //ol, dl, dir, - //menu { margin: 1.12em 0 } - //a { color: blue; } - //h5 { font-size: .83em; margin: 1.5em 0 } - //h6 { font-size: .75em; margin: 1.67em 0 } - //h1, h2, h3, h4, - //h5, h6, b, - //strong { font-weight: bolder } - //blockquote { margin-left: 40px; margin-right: 40px } - //i, cite, em, - //var, address { font-style: italic } - //pre, tt, code, - //kbd, samp { font-family: monospace } - //pre { white-space: pre } - //button, textarea, - //input, select { display: inline-block } - //big { font-size: 1.17em } - //small, sub, sup { font-size: .83em } - //sub { vertical-align: sub } - //sup { vertical-align: super } - //table { border-spacing: 2px; } - //thead, tbody, - //tfoot { vertical-align: middle } - //td, th, tr { vertical-align: inherit } - //s, strike, del { text-decoration: line-through } - //hr { border: 1px inset } - //ol, ul, dir, - //menu, dd { margin-left: 40px } - //ol { list-style-type: decimal } - //ol ul, ul ol, - //ul ul, ol ol { margin-top: 0; margin-bottom: 0 } - //u, ins { text-decoration: underline } - //br:before { content: ""\A""; white-space: pre-line } - //center { text-align: center } - //:link, :visited { text-decoration: underline } - //:focus { outline: thin dotted invert } - ///* Begin bidirectionality settings (do not change) */ - //BDO[DIR=""ltr""] { direction: ltr; unicode-bidi: bidi-override } - //BDO[DIR=""rtl""] { direction: rtl; unicode-bidi: bidi-override } - //*[DIR=""ltr""] { direction: ltr; unicode-bidi: embed } - //*[DIR=""rtl""] { direction: rtl; unicode-bidi: embed }"; - - static string userCss = @""; - } -} diff --git a/OpenXmlPowerTools.Tests.OA/ListItemRetrieverTests.cs b/OpenXmlPowerTools.Tests.OA/ListItemRetrieverTests.cs deleted file mode 100644 index a87fffe1..00000000 --- a/OpenXmlPowerTools.Tests.OA/ListItemRetrieverTests.cs +++ /dev/null @@ -1,271 +0,0 @@ -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2012-2015. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -Published at http://OpenXmlDeveloper.org -Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx - -Developer: Eric White -Blog: http://www.ericwhite.com -Twitter: @EricWhiteDev -Email: eric@ericwhite.com - -***************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; -using HtmlAgilityPack; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; -using Xunit; - -namespace OxPt -{ - public class LiTests - { - - // PowerShell oneliner that generates InlineData for all files in a directory - // dir | % { '[InlineData("' + $_.Name + '")]' } | clip - - [Theory] - [InlineData("LIR001-en-US-ordinal.docx")] - [InlineData("LIR002-en-US-ordinalText.docx")] - [InlineData("LIR003-en-US-upperLetter.docx")] - [InlineData("LIR004-en-US-upperRoman.docx")] - [InlineData("LIR005-fr-FR-cardinalText.docx")] - [InlineData("LIR006-fr-FR-ordinalText.docx")] - [InlineData("LIR006-fr-FR-ordinal.docx")] - // [InlineData("LIR007-ru-RU-ordinalText.docx")] // todo this fails, the code needs updated. - [InlineData("LIR008-zh-CH-chineseCountingThousand.docx")] - [InlineData("LIR009-zh-CN-chineseCounting.docx")] - [InlineData("LIR010-zh-CN-ideographTraditional.docx")] - [InlineData("LIR011-en-US-00001.docx")] - [InlineData("LIR012-en-US-0001.docx")] - [InlineData("LIR013-en-US-001.docx")] - [InlineData("LIR014-en-US-01.docx")] - [InlineData("LIR015-en-US-cardinalText.docx")] - [InlineData("LIR016-en-US-decimal.docx")] - [InlineData("LIR017-en-US-decimalEnclosedCircle.docx")] - [InlineData("LIR018-en-US-decimalZero.docx")] - [InlineData("LIR019-en-US-lowerLetter.docx")] - [InlineData("LIR020-en-US-lowerRoman.docx")] - public void LIR001(string file) - { - FileInfo lirFile = new FileInfo(Path.Combine(TestUtil.SourceDir.FullName, file)); - WmlDocument wmlDoc = new WmlDocument(lirFile.FullName); - - var wordHtmlFile = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, lirFile.Name.Replace(".docx", "-Word.html"))); - WordAutomationUtilities.SaveAsHtmlUsingWord(lirFile, wordHtmlFile); - - var ptHtmlFile = ConvertToHtml(lirFile.FullName, TestUtil.TempDir.FullName); - var fiPtXml = SaveHtmlAsXml(ptHtmlFile); - - // read and write to get the BOM on the file - var wh = File.ReadAllText(wordHtmlFile.FullName, Encoding.Default); - File.WriteAllText(wordHtmlFile.FullName, wh, Encoding.UTF8); - - var wordXml = SaveHtmlAsXml(wordHtmlFile); - CompareNumbering(fiPtXml, wordXml); - } - - private static void CompareNumbering(FileInfo fiPtXml, FileInfo wordXml) - { - char splitChar = '|'; - - Console.WriteLine("Comparing {0} to {1}", fiPtXml.Name, wordXml.Name); - var xdWord = XDocument.Load(wordXml.FullName); - List wordRawParagraphText = xdWord.Descendants("h2").Select(p => p.DescendantNodes().OfType().Select(t => t.Value).Aggregate((s, i) => s + i)).ToList(); - var wordTextToCompare = wordRawParagraphText.Where(p => p.Contains(splitChar.ToString())).Select(p => p.Split(splitChar)[0]).ToList(); - - var xdPt = XDocument.Load(fiPtXml.FullName); - XNamespace xhtml = "http://www.w3.org/1999/xhtml"; - List ptRawParagraphText = xdPt.Descendants(xhtml + "h2").Select(p => p.DescendantNodes().OfType().Select(t => t.Value).Aggregate((s, i) => s + i)).ToList(); - var ptTextToCompare = ptRawParagraphText.Where(p => p.Contains(splitChar.ToString())).Select(p => new - { - ListItem = p.Split(splitChar)[0], - ParaText = p.Split(splitChar)[1], - }).ToList(); - - if (!wordTextToCompare.Any()) - { - throw new Exception("Internal error - no items selected"); - } - if (wordTextToCompare.Count() != ptTextToCompare.Count()) - { - Assert.True(false); - //Console.WriteLine("Error, differing line counts"); - //Console.WriteLine("Word line count: {0}", wordTextToCompare.Count()); - //Console.WriteLine("Pt line count: {0}", ptTextToCompare.Count()); - return; - } - var zipped = wordTextToCompare.Zip(ptTextToCompare, (w, p) => new - { - WordText = w, - PtText = p.ListItem, - ParagraphText = p.ParaText, - }); - var mismatchList = zipped.Where(z => - { - var w = z.WordText.Replace('\n', ' ').Trim(); - var p = z.PtText.Replace('\n', ' ').Trim(); - var match = w == p; - if (match) - return false; - return true; - }).Select(z => z.ParagraphText).ToList(); - if (mismatchList.Any()) - { - Assert.True(false); - //Console.WriteLine("Mismatches: {0}", mismatchList.Count()); - //foreach (var item in mismatchList.Take(20)) - //{ - // Console.WriteLine(item); - //} - } - } - - public static FileInfo ConvertToHtml(string file, string outputDirectory) - { - var fi = new FileInfo(file); - byte[] byteArray = File.ReadAllBytes(fi.FullName); - FileInfo destFileName; - using (MemoryStream memoryStream = new MemoryStream()) - { - memoryStream.Write(byteArray, 0, byteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) - { - destFileName = new FileInfo(fi.Name.Replace(".docx", ".html")); - if (outputDirectory != null && outputDirectory != string.Empty) - { - DirectoryInfo di = new DirectoryInfo(outputDirectory); - if (!di.Exists) - { - throw new OpenXmlPowerToolsException("Output directory does not exist"); - } - destFileName = new FileInfo(Path.Combine(di.FullName, destFileName.Name)); - } - var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - int imageCounter = 0; - var pageTitle = (string)wDoc.CoreFilePropertiesPart.GetXDocument().Descendants(DC.title).FirstOrDefault(); - if (pageTitle == null) - pageTitle = fi.FullName; - - HtmlConverterSettings settings = new HtmlConverterSettings() - { - PageTitle = pageTitle, - FabricateCssClasses = true, - CssClassPrefix = "pt-", - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - ImageHandler = imageInfo => - { - DirectoryInfo localDirInfo = new DirectoryInfo(imageDirectoryName); - if (!localDirInfo.Exists) - localDirInfo.Create(); - ++imageCounter; - string extension = imageInfo.ContentType.Split('/')[1].ToLower(); - ImageFormat imageFormat = null; - if (extension == "png") - { - // Convert png to jpeg. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "gif") - imageFormat = ImageFormat.Gif; - else if (extension == "bmp") - imageFormat = ImageFormat.Bmp; - else if (extension == "jpeg") - imageFormat = ImageFormat.Jpeg; - else if (extension == "tiff") - { - // Convert tiff to gif. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "x-wmf") - { - extension = "wmf"; - imageFormat = ImageFormat.Wmf; - } - - // If the image format isn't one that we expect, ignore it, - // and don't return markup for the link. - if (imageFormat == null) - return null; - - string imageFileName = imageDirectoryName + "/image" + - imageCounter.ToString() + "." + extension; - try - { - imageInfo.Bitmap.Save(imageFileName, imageFormat); - } - catch (System.Runtime.InteropServices.ExternalException) - { - return null; - } - XElement img = new XElement(Xhtml.img, - new XAttribute(NoNamespace.src, imageFileName), - imageInfo.ImgStyleAttribute, - imageInfo.AltText != null ? - new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); - return img; - } - }; - XElement html = HtmlConverter.ConvertToHtml(wDoc, settings); - - // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type - // XEntity. PtOpenXmlUtil.cs define the XEntity class. See - // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx - // for detailed explanation. - // - // If you further transform the XML tree returned by ConvertToHtmlTransform, you - // must do it correctly, or entities will not be serialized properly. - - var htmlString = html.ToString(SaveOptions.DisableFormatting); - File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); - } - } - return destFileName; - } - - public static FileInfo SaveHtmlAsXml(FileInfo htmlFileName) - { - string baseName = htmlFileName.Name.Substring(0, htmlFileName.Name.Length - htmlFileName.Extension.Length); - FileInfo destFile = new FileInfo(Path.Combine(htmlFileName.DirectoryName, baseName + ".xml")); - - HtmlDocument hdoc = new HtmlDocument(); - hdoc.Load(htmlFileName.FullName, Encoding.UTF8); - hdoc.OptionOutputAsXml = true; - hdoc.Save(destFile.FullName, Encoding.UTF8); - StringBuilder sb = new StringBuilder(File.ReadAllText(destFile.FullName, Encoding.Default)); - sb.Replace("è", "è"); - sb.Replace("&", "&"); - sb.Replace(" ", "\xA0"); - sb.Replace(""", "\""); - sb.Replace("<", "~lt;"); - sb.Replace(">", "~gt;"); - sb.Replace("&#", "~#"); - sb.Replace("&", "&"); - sb.Replace("~lt;", "<"); - sb.Replace("~gt;", ">"); - sb.Replace("~#", "&#"); - File.WriteAllText(destFile.FullName, sb.ToString(), Encoding.UTF8); - return destFile; - } - } - -} diff --git a/OpenXmlPowerTools.Tests.OA/OpenXmlPowerTools.Tests.OA.csproj b/OpenXmlPowerTools.Tests.OA/OpenXmlPowerTools.Tests.OA.csproj deleted file mode 100644 index 37303a91..00000000 --- a/OpenXmlPowerTools.Tests.OA/OpenXmlPowerTools.Tests.OA.csproj +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - Debug - AnyCPU - {2C50A5AF-E34B-4FC4-983F-F30BB49A57AB} - Library - Properties - OpenXmlPowerTools.Tests.OA - OpenXmlPowerTools.Tests.OA - v4.5 - 512 - f5d754ea - - - true - full - false - bin\Debug\ - TRACE;DEBUG;USE_HTMLAGILITYPACK - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - False - ..\..\Open-Xml-SDK\DocumentFormat.OpenXml\bin\Debug\DocumentFormat.OpenXml.dll - - - False - ..\..\HtmlAgilityPack\HtmlAgilityPack.dll - - - True - - - - - - ..\..\Open-Xml-SDK\DocumentFormat.OpenXml\bin\Debug\System.IO.Packaging.dll - - - - - - - - ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll - True - - - ..\packages\xunit.assert.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll - True - - - ..\packages\xunit.extensibility.core.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll - True - - - - - HtmlConverterTests.cs - - - HtmlToWmlConverterTests.cs - - - HtmlToWmlReadAsXElement.cs - - - - - - - - - - - - {6f957ff3-afcc-4d69-8fbc-71ae21bc45c9} - OpenXmlPowerTools - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests.OA/WordAutomationUtilities.cs b/OpenXmlPowerTools.Tests.OA/WordAutomationUtilities.cs deleted file mode 100644 index ef154e13..00000000 --- a/OpenXmlPowerTools.Tests.OA/WordAutomationUtilities.cs +++ /dev/null @@ -1,158 +0,0 @@ -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2012-2015. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -Published at http://OpenXmlDeveloper.org -Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx - -Developer: Eric White -Blog: http://www.ericwhite.com -Twitter: @EricWhiteDev -Email: eric@ericwhite.com - -***************************************************************************/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using Word = Microsoft.Office.Interop.Word; - -namespace OxPt -{ - public class WordAutomationUtilities - { - public static void DoConversionViaWord(FileInfo newAltChunkBeforeFi, FileInfo newAltChunkAfterFi, XElement html) - { - var blankAltChunkFi = new DirectoryInfo(Path.Combine(TestUtil.SourceDir.FullName, "Blank-altchunk.docx")); - File.Copy(blankAltChunkFi.FullName, newAltChunkBeforeFi.FullName); - using (WordprocessingDocument myDoc = WordprocessingDocument.Open(newAltChunkBeforeFi.FullName, true)) - { - string altChunkId = "AltChunkId1"; - MainDocumentPart mainPart = myDoc.MainDocumentPart; - AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart( - "application/xhtml+xml", altChunkId); - using (Stream chunkStream = chunk.GetStream(FileMode.Create, FileAccess.Write)) - using (StreamWriter stringStream = new StreamWriter(chunkStream)) - stringStream.Write(html.ToString()); - XElement altChunk = new XElement(W.altChunk, - new XAttribute(R.id, altChunkId) - ); - XDocument mainDocumentXDoc = myDoc.MainDocumentPart.GetXDocument(); - mainDocumentXDoc.Root - .Element(W.body) - .AddFirst(altChunk); - myDoc.MainDocumentPart.PutXDocument(); - } - - WordAutomationUtilities.OpenAndSaveAs(newAltChunkBeforeFi.FullName, newAltChunkAfterFi.FullName); - - while (true) - { - try - { - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(newAltChunkAfterFi.FullName, true)) - { - SimplifyMarkupSettings settings2 = new SimplifyMarkupSettings - { - RemoveMarkupForDocumentComparison = true, - }; - MarkupSimplifier.SimplifyMarkup(wDoc, settings2); - XElement newRoot = (XElement)RemoveDivTransform(wDoc.MainDocumentPart.GetXDocument().Root); - wDoc.MainDocumentPart.GetXDocument().Root.ReplaceWith(newRoot); - wDoc.MainDocumentPart.PutXDocumentWithFormatting(); - } - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - continue; - } - } - } - - private static object RemoveDivTransform(XNode node) - { - XElement element = node as XElement; - if (element != null) - { - if (element.Name == W.divId) - return null; - return new XElement(element.Name, - element.Attributes(), - element.Nodes().Select(n => RemoveDivTransform(n))); - } - return node; - } - - public static void SaveAsHtmlUsingWord(FileInfo src, FileInfo dest) - { - Word.Application app = new Word.Application(); - app.Visible = false; - try - { - Word.Document doc = app.Documents.Open(src.FullName); - doc.SaveAs2(dest.FullName, Word.WdSaveFormat.wdFormatFilteredHTML); - } - catch (System.Runtime.InteropServices.COMException) - { - Console.WriteLine("Caught unexpected COM exception."); - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - Environment.Exit(0); - } - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - } - - public static void OpenAndSaveAs(string fromFileName, string toFileName) - { - Word.Application app = new Word.Application(); - app.Visible = false; - FileInfo fi = new FileInfo(fromFileName); - try - { - FileInfo ffi = new FileInfo(fromFileName); - Word.Document doc = app.Documents.Open(ffi.FullName); - object FileFormat = Word.WdSaveFormat.wdFormatDocument; - FileInfo tfi = new FileInfo(toFileName); - object FileName = tfi.FullName; - - doc.SaveAs(tfi.FullName, Word.WdSaveFormat.wdFormatDocumentDefault); - } - catch (System.Runtime.InteropServices.COMException) - { - Console.WriteLine("Caught unexpected COM exception."); - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - Environment.Exit(0); - } - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - } - - public static void OpenAndSaveAs(FileInfo src, FileInfo dest) - { - Word.Application app = new Word.Application(); - app.Visible = false; - try - { - Word.Document doc = app.Documents.Open(src.FullName); - doc.SaveAs2(dest.FullName, Word.WdSaveFormat.wdFormatDocument); - } - catch (System.Runtime.InteropServices.COMException) - { - Console.WriteLine("Caught unexpected COM exception."); - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - Environment.Exit(0); - } - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - } - } -} diff --git a/OpenXmlPowerTools.Tests/ChartUpdaterTests.cs b/OpenXmlPowerTools.Tests/ChartUpdaterTests.cs index 2a90cfc2..b4107fc9 100644 --- a/OpenXmlPowerTools.Tests/ChartUpdaterTests.cs +++ b/OpenXmlPowerTools.Tests/ChartUpdaterTests.cs @@ -1,22 +1,11 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.Chart; +using DocumentFormat.OpenXml.Packaging; using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class CuTests { @@ -29,7 +18,6 @@ public class CuTests [InlineData("CU006-Chart-Cached-Data-06.docx")] [InlineData("CU007-Chart-Cached-Data-07.docx")] [InlineData("CU008-Chart-Cached-Data-08.docx")] - [InlineData("CU009-Chart-Embedded-Xlsx-01.docx")] [InlineData("CU010-Chart-Embedded-Xlsx-02.docx")] [InlineData("CU011-Chart-Embedded-Xlsx-03.docx")] @@ -39,41 +27,38 @@ public class CuTests [InlineData("CU015-Chart-Embedded-Xlsx-07.docx")] [InlineData("CU016-Chart-Embedded-Xlsx-08.docx")] [InlineData("CU017-Chart-Embedded-Xlsx-10.docx")] - [InlineData("CU018-Chart-Cached-Data-41.pptx")] [InlineData("CU019-Chart-Embedded-Xlsx-41.pptx")] - public void CU001(string name) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo templateFile = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var templateFile = new FileInfo(Path.Combine(sourceDir.FullName, name)); if (templateFile.Extension.ToLower() == ".docx") { - WmlDocument wmlTemplate = new WmlDocument(templateFile.FullName); + var wmlTemplate = new WmlDocument(templateFile.FullName); var afterUpdatingDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, templateFile.Name.Replace(".docx", "-processed-by-ChartUpdater.docx"))); wmlTemplate.SaveAs(afterUpdatingDocx.FullName); - using (var wDoc = WordprocessingDocument.Open(afterUpdatingDocx.FullName, true)) + using var wDoc = WordprocessingDocument.Open(afterUpdatingDocx.FullName, true); + var chart1Data = new ChartData { - var chart1Data = new ChartData - { - SeriesNames = new[] { + SeriesNames = new[] { "Car", "Truck", "Van", "Bike", "Boat", }, - CategoryDataType = ChartDataType.String, - CategoryNames = new[] { + CategoryDataType = ChartDataType.String, + CategoryNames = new[] { "Q1", "Q2", "Q3", "Q4", }, - Values = new double[][] { + Values = new double[][] { new double[] { 100, 310, 220, 450, }, @@ -90,32 +75,32 @@ public void CU001(string name) 200, 210, 210, 480, }, }, - }; - ChartUpdater.UpdateChart(wDoc, "Chart1", chart1Data); + }; + ChartUpdater.UpdateChart(wDoc, "Chart1", chart1Data); - var chart2Data = new ChartData - { - SeriesNames = new[] { + var chart2Data = new ChartData + { + SeriesNames = new[] { "Series" }, - CategoryDataType = ChartDataType.String, - CategoryNames = new[] { + CategoryDataType = ChartDataType.String, + CategoryNames = new[] { "Cars", "Trucks", "Vans", "Boats", }, - Values = new double[][] { + Values = new double[][] { new double[] { 320, 112, 64, 80, }, }, - }; - ChartUpdater.UpdateChart(wDoc, "Chart2", chart2Data); + }; + ChartUpdater.UpdateChart(wDoc, "Chart2", chart2Data); - var chart3Data = new ChartData - { - SeriesNames = new[] { + var chart3Data = new ChartData + { + SeriesNames = new[] { "X1", "X2", "X3", @@ -123,8 +108,8 @@ public void CU001(string name) "X5", "X6", }, - CategoryDataType = ChartDataType.String, - CategoryNames = new[] { + CategoryDataType = ChartDataType.String, + CategoryNames = new[] { "Y1", "Y2", "Y3", @@ -132,7 +117,7 @@ public void CU001(string name) "Y5", "Y6", }, - Values = new double[][] { + Values = new double[][] { new double[] { 3.0, 2.1, .7, .7, 2.1, 3.0, }, new double[] { 3.0, 2.1, .8, .8, 2.1, 3.0, }, new double[] { 3.0, 2.4, 1.2, 1.2, 2.4, 3.0, }, @@ -140,19 +125,19 @@ public void CU001(string name) new double[] { 3.0, 2.9, 2.5, 2.5, 2.9, 3.0, }, new double[] { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, }, }, - }; - ChartUpdater.UpdateChart(wDoc, "Chart3", chart3Data); + }; + ChartUpdater.UpdateChart(wDoc, "Chart3", chart3Data); - var chart4Data = new ChartData - { - SeriesNames = new[] { + var chart4Data = new ChartData + { + SeriesNames = new[] { "Car", "Truck", "Van", }, - CategoryDataType = ChartDataType.DateTime, - CategoryFormatCode = 14, - CategoryNames = new[] { + CategoryDataType = ChartDataType.DateTime, + CategoryFormatCode = 14, + CategoryNames = new[] { ToExcelInteger(new DateTime(2013, 9, 1)), ToExcelInteger(new DateTime(2013, 9, 2)), ToExcelInteger(new DateTime(2013, 9, 3)), @@ -174,7 +159,7 @@ public void CU001(string name) ToExcelInteger(new DateTime(2013, 9, 19)), ToExcelInteger(new DateTime(2013, 9, 20)), }, - Values = new double[][] { + Values = new double[][] { new double[] { 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 5, 4, 5, 6, 7, 8, 7, 8, 8, 9, }, @@ -185,34 +170,32 @@ public void CU001(string name) 2, 3, 3, 3, 3, 2, 2, 2, 3, 2, 3, 3, 4, 4, 4, 3, 4, 5, 5, 4, }, }, - }; - ChartUpdater.UpdateChart(wDoc, "Chart4", chart4Data); - } + }; + ChartUpdater.UpdateChart(wDoc, "Chart4", chart4Data); } if (templateFile.Extension.ToLower() == ".pptx") { - PmlDocument pmlTemplate = new PmlDocument(templateFile.FullName); + var pmlTemplate = new PmlDocument(templateFile.FullName); var afterUpdatingPptx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, templateFile.Name.Replace(".pptx", "-processed-by-ChartUpdater.pptx"))); pmlTemplate.SaveAs(afterUpdatingPptx.FullName); - using (var pDoc = PresentationDocument.Open(afterUpdatingPptx.FullName, true)) + using var pDoc = PresentationDocument.Open(afterUpdatingPptx.FullName, true); + var chart1Data = new ChartData { - var chart1Data = new ChartData - { - SeriesNames = new[] { + SeriesNames = new[] { "Car", "Truck", "Van", }, - CategoryDataType = ChartDataType.String, - CategoryNames = new[] { + CategoryDataType = ChartDataType.String, + CategoryNames = new[] { "Q1", "Q2", "Q3", "Q4", }, - Values = new double[][] { + Values = new double[][] { new double[] { 320, 310, 320, 330, }, @@ -223,9 +206,8 @@ public void CU001(string name) 180, 200, 220, 230, }, }, - }; - ChartUpdater.UpdateChart(pDoc, 1, chart1Data); - } + }; + ChartUpdater.UpdateChart(pDoc, 1, chart1Data); } } @@ -234,6 +216,4 @@ private static string ToExcelInteger(DateTime dateTime) return dateTime.ToOADate().ToString(); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/ColorParserTests.cs b/OpenXmlPowerTools.Tests/ColorParserTests.cs new file mode 100644 index 00000000..c4cf50bd --- /dev/null +++ b/OpenXmlPowerTools.Tests/ColorParserTests.cs @@ -0,0 +1,56 @@ +using Codeuctivity.OpenXmlPowerTools; +using SkiaSharp; +using Xunit; + +namespace Codeuctivity.Tests +{ + public class ColorParserTests + { + [Theory] + [InlineData("red", 255, 0, 0)] + [InlineData("RED", 255, 0, 0)] + [InlineData("#00FF00", 0, 255, 0)] + [InlineData("#00ff00", 0, 255, 0)] + [InlineData("blue", 0, 0, 255)] + [InlineData("#0000FF", 0, 0, 255)] + [InlineData("#abc", 170, 187, 204)] + [InlineData("yellow", 255, 255, 0)] + [InlineData("black", 0, 0, 0)] + [InlineData("white", 255, 255, 255)] + public void ShouldParseColors(string input, byte r, byte g, byte b) + { + var result = ColorParser.FromName(input); + Assert.Equal(r, result.Red); + Assert.Equal(g, result.Green); + Assert.Equal(b, result.Blue); + } + + [Theory] + [InlineData("red", true)] + [InlineData("#123456", true)] + [InlineData("notacolor", false)] + [InlineData("", false)] + public void ShouldValidateColorNames(string input, bool valid) + { + Assert.Equal(valid, ColorParser.IsValidName(input)); + } + + [Fact] + public void FromNameShouldThrowOnInvalid() + { + Assert.Throws(() => ColorParser.FromName("bogus")); + } + + [Theory] + [InlineData("notacolor")] + [InlineData("#GGGGGG")] + [InlineData("")] + [InlineData(null)] + [InlineData(" ")] + public void ShouldRejectInvalidColors(string? input) + { + var success = ColorParser.TryFromName(input, out SKColor _); + Assert.False(success); + } + } +} diff --git a/OpenXmlPowerTools.Tests/CssPropertyValueTests.cs b/OpenXmlPowerTools.Tests/CssPropertyValueTests.cs new file mode 100644 index 00000000..8aaa38e5 --- /dev/null +++ b/OpenXmlPowerTools.Tests/CssPropertyValueTests.cs @@ -0,0 +1,45 @@ +using Codeuctivity.OpenXmlPowerTools; +using SkiaSharp; +using Xunit; + +namespace Codeuctivity.Tests +{ + public class CssPropertyValueTests + { + [Fact] + public void ShouldRecognizeNamedColor() + { + var value = new CssPropertyValue { Type = CssValueType.String, Value = "red" }; + Assert.True(value.IsColor); + var color = value.ToColor(); + Assert.Equal(SKColors.Red, color); + } + + [Fact] + public void ShouldRecognizeHexColor() + { + var value = new CssPropertyValue { Type = CssValueType.Hex, Value = "#0000FF" }; + Assert.True(value.IsColor); + var color = value.ToColor(); + Assert.Equal(SKColors.Blue, color); + } + + [Fact] + public void ShouldRejectNonColor() + { + var value = new CssPropertyValue { Type = CssValueType.String, Value = "1234" }; + Assert.False(value.IsColor); + } + + [Fact] + public void ShouldParseHexWithoutHash() + { + var value = new CssPropertyValue { Type = CssValueType.Hex, Value = "00FF00" }; + Assert.True(value.IsColor); + var color = value.ToColor(); + Assert.Equal(0u, color.Red); + Assert.Equal(255u, color.Green); + Assert.Equal(0u, color.Blue); + } + } +} diff --git a/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs b/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs index cf62d2f0..e69616a0 100644 --- a/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs +++ b/OpenXmlPowerTools.Tests/DocumentAssemblerTests.cs @@ -1,24 +1,16 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Validation; using System; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class DaTests { @@ -57,11 +49,9 @@ public class DaTests [InlineData("DA034-HeaderFooter.docx", "DA-Data.xml", false)] [InlineData("DA035-SchemaErrorInRepeat.docx", "DA-Data.xml", true)] [InlineData("DA036-SchemaErrorInConditional.docx", "DA-Data.xml", true)] - [InlineData("DA100-TemplateDocument.docx", "DA-Data.xml", false)] [InlineData("DA101-TemplateDocument.docx", "DA-Data.xml", true)] [InlineData("DA102-TemplateDocument.docx", "DA-Data.xml", true)] - [InlineData("DA201-TemplateDocument.docx", "DA-Data.xml", false)] [InlineData("DA202-TemplateDocument.docx", "DA-DataNotHighValueCust.xml", false)] [InlineData("DA203-Select-XPathFindsNoData.docx", "DA-Data.xml", true)] @@ -98,7 +88,6 @@ public class DaTests [InlineData("DA237-SchemaErrorInRepeat.docx", "DA-Data.xml", true)] [InlineData("DA238-SchemaErrorInConditional.docx", "DA-Data.xml", true)] [InlineData("DA239-RunLevelCC-Repeat.docx", "DA-Data.xml", false)] - [InlineData("DA250-ConditionalWithRichXPath.docx", "DA250-Address.xml", false)] [InlineData("DA251-EnhancedTables.docx", "DA-Data.xml", false)] [InlineData("DA252-Table-With-Sum.docx", "DA-Data.xml", false)] @@ -116,41 +105,35 @@ public class DaTests [InlineData("DA264-InvalidRunLevelRepeat.docx", "DA-Data.xml", true)] [InlineData("DA265-RunLevelRepeatWithWhiteSpaceBefore.docx", "DA-Data.xml", false)] [InlineData("DA266-RunLevelRepeat-NoData.docx", "DA-Data.xml", true)] - public void DA101(string name, string data, bool err) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo templateDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - FileInfo dataFile = new FileInfo(Path.Combine(sourceDir.FullName, data)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var templateDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var dataFile = new FileInfo(Path.Combine(sourceDir.FullName, data)); - WmlDocument wmlTemplate = new WmlDocument(templateDocx.FullName); - XElement xmldata = XElement.Load(dataFile.FullName); + var wmlTemplate = new WmlDocument(templateDocx.FullName); + var xmldata = XElement.Load(dataFile.FullName); - bool returnedTemplateError; - WmlDocument afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmldata, out returnedTemplateError); + var afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmldata, out var returnedTemplateError); var assembledDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, templateDocx.Name.Replace(".docx", "-processed-by-DocumentAssembler.docx"))); afterAssembling.SaveAs(assembledDocx.FullName); - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { ms.Write(afterAssembling.DocumentByteArray, 0, afterAssembling.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) + using var wDoc = WordprocessingDocument.Open(ms, true); + var v = new OpenXmlValidator(); + var valErrors = v.Validate(wDoc).Where(ve => !s_ExpectedErrors.Contains(ve.Description)); + + var sb = new StringBuilder(); + foreach (var item in valErrors.Select(r => r.Description).OrderBy(t => t).Distinct()) { - OpenXmlValidator v = new OpenXmlValidator(); - var valErrors = v.Validate(wDoc).Where(ve => !s_ExpectedErrors.Contains(ve.Description)); - -#if false - StringBuilder sb = new StringBuilder(); - foreach (var item in valErrors.Select(r => r.Description).OrderBy(t => t).Distinct()) - { - sb.Append(item).Append(Environment.NewLine); - } - string z = sb.ToString(); - Console.WriteLine(z); -#endif - - Assert.Empty(valErrors); + sb.Append(item).Append(Environment.NewLine); } + var z = sb.ToString(); + Console.WriteLine(z); + + Assert.Empty(valErrors); } Assert.Equal(err, returnedTemplateError); @@ -162,8 +145,8 @@ public void DA259(string name, string data, bool err) { DA101(name, data, err); var assembledDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".docx", "-processed-by-DocumentAssembler.docx"))); - WmlDocument afterAssembling = new WmlDocument(assembledDocx.FullName); - int brCount = afterAssembling.MainDocumentPart + var afterAssembling = new WmlDocument(assembledDocx.FullName); + var brCount = afterAssembling.MainDocumentPart .Element(W.body) .Elements(W.p).ElementAt(1) .Elements(W.r) @@ -175,18 +158,17 @@ public void DA259(string name, string data, bool err) [InlineData("DA024-TrackedRevisions.docx", "DA-Data.xml")] public void DA102_Throws(string name, string data) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo templateDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - FileInfo dataFile = new FileInfo(Path.Combine(sourceDir.FullName, data)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var templateDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var dataFile = new FileInfo(Path.Combine(sourceDir.FullName, data)); - WmlDocument wmlTemplate = new WmlDocument(templateDocx.FullName); - XElement xmldata = XElement.Load(dataFile.FullName); + var wmlTemplate = new WmlDocument(templateDocx.FullName); + var xmldata = XElement.Load(dataFile.FullName); - bool returnedTemplateError; WmlDocument afterAssembling; Assert.Throws(() => { - afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmldata, out returnedTemplateError); + afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmldata, out var returnedTemplateError); }); } @@ -194,34 +176,31 @@ public void DA102_Throws(string name, string data) [InlineData("DA025-TemplateDocument.docx", "DA-Data.xml", false)] public void DA103_UseXmlDocument(string name, string data, bool err) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo templateDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - FileInfo dataFile = new FileInfo(Path.Combine(sourceDir.FullName, data)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var templateDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var dataFile = new FileInfo(Path.Combine(sourceDir.FullName, data)); - WmlDocument wmlTemplate = new WmlDocument(templateDocx.FullName); - XmlDocument xmldata = new XmlDocument(); + var wmlTemplate = new WmlDocument(templateDocx.FullName); + var xmldata = new XmlDocument(); xmldata.Load(dataFile.FullName); - bool returnedTemplateError; - WmlDocument afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmldata, out returnedTemplateError); + var afterAssembling = DocumentAssembler.AssembleDocument(wmlTemplate, xmldata, out var returnedTemplateError); var assembledDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, templateDocx.Name.Replace(".docx", "-processed-by-DocumentAssembler.docx"))); afterAssembling.SaveAs(assembledDocx.FullName); - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { ms.Write(afterAssembling.DocumentByteArray, 0, afterAssembling.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator v = new OpenXmlValidator(); - var valErrors = v.Validate(wDoc).Where(ve => !s_ExpectedErrors.Contains(ve.Description)); - Assert.Empty(valErrors); - } + using var wDoc = WordprocessingDocument.Open(ms, true); + var v = new OpenXmlValidator(); + var valErrors = v.Validate(wDoc).Where(ve => !s_ExpectedErrors.Contains(ve.Description)); + Assert.Empty(valErrors); } Assert.Equal(err, returnedTemplateError); } - private static List s_ExpectedErrors = new List() + private static readonly List s_ExpectedErrors = new List() { "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenHBand' attribute is not declared.", "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenVBand' attribute is not declared.", @@ -239,6 +218,4 @@ public void DA103_UseXmlDocument(string name, string data, bool err) "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:oddVBand' attribute is not declared.", }; } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/DocumentBuilderTests.cs b/OpenXmlPowerTools.Tests/DocumentBuilderTests.cs index 5daacb76..55b69ca9 100644 --- a/OpenXmlPowerTools.Tests/DocumentBuilderTests.cs +++ b/OpenXmlPowerTools.Tests/DocumentBuilderTests.cs @@ -1,36 +1,26 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.DocumentBuilder; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Validation; using System; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using DocumentFormat.OpenXml.Wordprocessing; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class DbTests { [Fact] public void DB001_DocumentBuilderKeepSections() { - string name = "DB001-Sections.docx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - - List sources = null; - sources = new List() + var name = "DB001-Sections.docx"; + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sources = new List() { new Source(new WmlDocument(sourceDocx.FullName), true), }; @@ -41,12 +31,10 @@ public void DB001_DocumentBuilderKeepSections() [Fact] public void DB002_DocumentBuilderKeepSectionsDiscardHeaders() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Sections-With-Headers.docx")); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); - - List sources = null; - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Sections-With-Headers.docx")); + var source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); + var sources = new List() { new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true }, new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true }, @@ -59,12 +47,10 @@ public void DB002_DocumentBuilderKeepSectionsDiscardHeaders() [Fact] public void DB003_DocumentBuilderOnlyDefaultHeader() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB003-Only-Default-Header.docx")); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); - - List sources = null; - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB003-Only-Default-Header.docx")); + var source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); + var sources = new List() { new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true }, new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true }, @@ -77,12 +63,10 @@ public void DB003_DocumentBuilderOnlyDefaultHeader() [Fact] public void DB004_DocumentBuilderNoHeaders() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB004-No-Headers.docx")); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); - - List sources = null; - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB004-No-Headers.docx")); + var source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); + var sources = new List() { new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true }, new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true }, @@ -95,12 +79,10 @@ public void DB004_DocumentBuilderNoHeaders() [Fact] public void DB005_HeadersWithRefsToImages() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB005-Headers-With-Images.docx")); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); - - List sources = null; - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB005-Headers-With-Images.docx")); + var source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, "DB002-Landscape-Section.docx")); + var sources = new List() { new Source(new WmlDocument(source1Docx.FullName)) { KeepSections = true }, new Source(new WmlDocument(source2Docx.FullName)) { KeepSections = true, DiscardHeadersAndFootersInKeptSections = true }, @@ -113,14 +95,13 @@ public void DB005_HeadersWithRefsToImages() [Fact] public void DB006_Example_DocumentBuilder01() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1 = new FileInfo(Path.Combine(sourceDir.FullName, "DB006-Source1.docx")); - FileInfo source2 = new FileInfo(Path.Combine(sourceDir.FullName, "DB006-Source2.docx")); - FileInfo source3 = new FileInfo(Path.Combine(sourceDir.FullName, "DB006-Source3.docx")); - List sources = null; + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1 = new FileInfo(Path.Combine(sourceDir.FullName, "DB006-Source1.docx")); + var source2 = new FileInfo(Path.Combine(sourceDir.FullName, "DB006-Source2.docx")); + var source3 = new FileInfo(Path.Combine(sourceDir.FullName, "DB006-Source3.docx")); // Create new document from 10 paragraphs starting at paragraph 5 of Source1.docx - sources = new List() + var sources = new List() { new Source(new WmlDocument(source1.FullName), 5, 10, true), }; @@ -169,9 +150,9 @@ public void DB006_Example_DocumentBuilder01() new Source(new WmlDocument(source1.FullName), 0, 5, false), new Source(new WmlDocument(source2.FullName), 0, 5, true), }; - WmlDocument wmlOut5 = DocumentBuilder.BuildDocument(sources); + var wmlOut5 = DocumentBuilder.BuildDocument(sources); var out5 = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB006-Out5.docx")); - + wmlOut5.SaveAs(out5.FullName); // save it to the file system, but we could just as easily done something // else with it. Validate(out5); @@ -180,14 +161,11 @@ public void DB006_Example_DocumentBuilder01() [Fact] public void DB007_Example_DocumentBuilder02_WhitePaper() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo spec = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-Spec.docx")); - FileInfo whitePaper = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-WhitePaper.docx")); - FileInfo paperAbstract = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-Abstract.docx")); - FileInfo authorBio = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-AuthorBiography.docx")); - - List sources = null; - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var whitePaper = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-WhitePaper.docx")); + var paperAbstract = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-Abstract.docx")); + var authorBio = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-AuthorBiography.docx")); + var sources = new List() { new Source(new WmlDocument(whitePaper.FullName), 0, 1, true), new Source(new WmlDocument(paperAbstract.FullName), false), @@ -202,12 +180,12 @@ public void DB007_Example_DocumentBuilder02_WhitePaper() [Fact] public void DB008_DeleteParasWithGivenStyle() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo notes = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-Notes.docx")); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var notes = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-Notes.docx")); List sources = null; // Delete all paragraphs with a specific style. - using (WordprocessingDocument doc = WordprocessingDocument.Open(notes.FullName, false)) + using (var doc = WordprocessingDocument.Open(notes.FullName, false)) { sources = doc .MainDocumentPart @@ -253,42 +231,42 @@ public void DB008_DeleteParasWithGivenStyle() [InlineData("DB009-00160", "DB/HeadersFooters/Src/Watermark-2.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")] [InlineData("DB009-00170", "DB/HeadersFooters/Src/Disclaimer.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")] [InlineData("DB009-00180", "DB/HeadersFooters/Src/Footer.docx", "DB/HeadersFooters/Dest/Letter.docx", "Templafy")] - public void DB009_ImportIntoHeadersFooters(string testId, string src, string dest, string insertId) { - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Load the source document - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocxFi = new FileInfo(Path.Combine(sourceDir.FullName, src)); - WmlDocument wmlSourceDocument = new WmlDocument(sourceDocxFi.FullName); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocxFi = new FileInfo(Path.Combine(sourceDir.FullName, src)); + var wmlSourceDocument = new WmlDocument(sourceDocxFi.FullName); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Load the dest document - FileInfo destDocxFi = new FileInfo(Path.Combine(sourceDir.FullName, dest)); - WmlDocument wmlDestDocument = new WmlDocument(destDocxFi.FullName); + var destDocxFi = new FileInfo(Path.Combine(sourceDir.FullName, dest)); + var wmlDestDocument = new WmlDocument(destDocxFi.FullName); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Create the dir for the test var rootTempDir = TestUtil.TempDir; var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); if (thisTestTempDir.Exists) + { Assert.True(false, "Duplicate test id: " + testId); + } else + { thisTestTempDir.Create(); + } + var tempDirFullName = thisTestTempDir.FullName; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Copy src DOCX to temp directory, for ease of review while (true) { try { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// var sourceDocxCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, sourceDocxFi.Name)); if (!sourceDocxCopiedToDestFileName.Exists) + { wmlSourceDocument.SaveAs(sourceDocxCopiedToDestFileName.FullName); - ////////////////////////////////////////////////// + } break; } catch (IOException) @@ -297,18 +275,17 @@ public void DB009_ImportIntoHeadersFooters(string testId, string src, string des } } - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Copy dest DOCX to temp directory, for ease of review while (true) { try { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// var destDocxCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, destDocxFi.Name)); if (!destDocxCopiedToDestFileName.Exists) + { wmlDestDocument.SaveAs(destDocxCopiedToDestFileName.FullName); - ////////////////////////////////////////////////// + } break; } catch (IOException) @@ -317,7 +294,7 @@ public void DB009_ImportIntoHeadersFooters(string testId, string src, string des } } - List sources = new List() + var sources = new List() { new Source(wmlDestDocument), new Source(wmlSourceDocument, insertId), @@ -328,138 +305,6 @@ public void DB009_ImportIntoHeadersFooters(string testId, string src, string des Validate(outFi); } -#if false - [Theory] - [InlineData("DB999-00010", "DBTEMP/03DE57384B87AA6C2A3BDE87DDDD7F880DC55E.docx", true)] - [InlineData("DB999-00020", "DBTEMP/0D3DEB27ED036116466BED616B2056CDD2783A.docx", false)] - [InlineData("DB999-00030", "DBTEMP/421628B3F4B03B123CA8EDDA5009E449F5F47D.docx", false)] - [InlineData("DB999-00040", "DBTEMP/58D4E8661C7F44FE33392B89B0A3CB0AF1684F.docx", false)] - [InlineData("DB999-00050", "DBTEMP/67EBCA627D6D584CAB3EB1DF2E4C3982023DEE.docx", true)] - [InlineData("DB999-00060", "DBTEMP/A529643E2FC3E2C682FA86DEE0A1B3064DCEE0.docx", false)] - [InlineData("DB999-00070", "DBTEMP/E794032F0422B440D3C564F0E09E395519127D.docx", false)] - [InlineData("DB999-00080", "DBTEMP/1FF1ADF30B24978E9449754459C743D3BC67ED.docx", false)] - [InlineData("DB999-00090", "DBTEMP/5E685927DA2FECB88DE9CAF0BECEC88BC118A7.docx", false)] - [InlineData("DB999-00100", "DBTEMP/6427BCF5C18B55D627B95F3E14924050628C5B.docx", false)] - [InlineData("DB999-00110", "DBTEMP/91691E0D3AB89E9927A2BAC5D385BB6277648F.docx", false)] - [InlineData("DB999-00120", "DBTEMP/9533BC5710190EA01DA86D29CD06880395C4AF.docx", false)] - [InlineData("DB999-00130", "DBTEMP/E9CD8C556AA52CA7D31DADB51A201EEF580AA8.docx", false)] - [InlineData("DB999-00140", "DBTEMP/21D3CE149C30B791F9A8BE092828E1469A9047.docx", false)] - [InlineData("DB999-00150", "DBTEMP/AC0CB8CE43A7ECAE995BB542D4FB1060FB835B.docx", false)] - [InlineData("DB999-00160", "DBTEMP/C61F69B52EC8B0E2C784C932B26F3C613AE671.docx", false)] - [InlineData("DB999-00170", "DBTEMP/1DF04A9130B3EF858ACA6837A706A429904973.dotm", false)] - [InlineData("DB999-00180", "DBTEMP/6E9F26B708DE6076B2C731B97AAA5288D839AB.docm", false)] - [InlineData("DB999-00190", "DBTEMP/A6649726EA0BD7545932DDD51403D83E4D5917.docx", false)] - [InlineData("DB999-00200", "DBTEMP/C8AE8AD0A73F24B7CFCFD11918B337CF2B90C9.docx", false)] - [InlineData("DB999-00210", "DBTEMP/BC46A7FBB212EFD10878A39D91AE3ECAADDAB0.docx", false)] - [InlineData("DB999-00220", "DBTEMP/B6F0E938B508676B322C47F3E0E29C8D786DB2.docm", false)] - [InlineData("DB999-00230", "DBTEMP/D4D8694A51DECA243AF748B3232BE565EEE19D.docx", false)] - [InlineData("DB999-00240", "DBTEMP/F20B3CE72BF635462E22BA3CA81CA9D57F6FEB.docx", false)] - [InlineData("DB999-00250", "DBTEMP/74ED106FF88C1B195D97C466E00BECCB636A03.docx", false)] - [InlineData("DB999-00260", "DBTEMP/4421A4B7B6ECC2813070309AA2D86C4BCA4AEF.docx", false)] - [InlineData("DB999-00270", "DBTEMP/BC7D91B993807518F3D430B7C6592AFD6BD91C.docx", false)] - [InlineData("DB999-00280", "DBTEMP/3006E76FE65E8A25A91ED204EEBEE6D6D62A44.docx", false)] - [InlineData("DB999-00290", "DBTEMP/6254B74778BFFCD1799F4F2B3B01C2025AABB2.docx", false)] - [InlineData("DB999-00300", "DBTEMP/5AD0A0BD99676B268D8E7C1F69238FB9B6149E.docx", false)] - [InlineData("DB999-00310", "DBTEMP/2D58495ECCF30ED9507B707C689CA9C9D4B049.docx", false)] - - public void DB999_DocumentBuilder(string testId, string src, bool shouldThrow) - { - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Load the source document - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocxFi = new FileInfo(Path.Combine(sourceDir.FullName, src)); - WmlDocument wmlSourceDocument = new WmlDocument(sourceDocxFi.FullName); - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Create the dir for the test - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (thisTestTempDir.Exists) - Assert.True(false, "Duplicate test id: " + testId); - else - thisTestTempDir.Create(); - var tempDirFullName = thisTestTempDir.FullName; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Copy src DOCX to temp directory, for ease of review - - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var sourceDocxCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, sourceDocxFi.Name)); - if (!sourceDocxCopiedToDestFileName.Exists) - wmlSourceDocument.SaveAs(sourceDocxCopiedToDestFileName.FullName); - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - - List expectedErrors; - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(wmlSourceDocument.DocumentByteArray, 0, wmlSourceDocument.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - expectedErrors = validator.Validate(wDoc) - .Select(e => e.Description) - .Distinct() - .ToList(); - } - } - foreach (var item in s_ExpectedErrors) - expectedErrors.Add(item); - - List sources = new List() - { - new Source(wmlSourceDocument, true), - }; - - var outFi = new FileInfo(Path.Combine(tempDirFullName, "Output.docx")); - - if (shouldThrow) - { - Assert.Throws(() => DocumentBuilder.BuildDocument(sources, outFi.FullName)); - } - else - { - var outWml = DocumentBuilder.BuildDocument(sources); - outWml.SaveAs(outFi.FullName); - - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(outWml.DocumentByteArray, 0, outWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => - { - var str = e.Description; - foreach (var ee in expectedErrors) - { - if (str.Contains(ee)) - return false; - } - return true; - }); - if (errors.Count() != 0) - { - var message = errors.Select(e => e.Description + Environment.NewLine).StringConcatenate(); - Assert.True(false, message); - } - } - } - - } - } -#endif - private class DocumentInfo { public int DocumentNumber; @@ -470,11 +315,11 @@ private class DocumentInfo [Fact] public void DB009_ShredDocument() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo spec = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-Spec.docx")); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var spec = new FileInfo(Path.Combine(sourceDir.FullName, "DB007-Spec.docx")); // Shred a document into multiple parts for each section List documentList; - using (WordprocessingDocument doc = WordprocessingDocument.Open(spec.FullName, false)) + using (var doc = WordprocessingDocument.Open(spec.FullName, false)) { var sectionCounts = doc .MainDocumentPart @@ -498,10 +343,10 @@ public void DB009_ShredDocument() Paragraph = p, Index = i, }); - var zipped = PtExtensions.PtZip(beforeZipped, sectionCounts, (pi, sc) => new + var zipped = beforeZipped.PtZip(sectionCounts, (pi, sc) => new { - Paragraph = pi.Paragraph, - Index = pi.Index, + pi.Paragraph, + pi.Index, SectionIndex = sc, }); documentList = zipped @@ -516,9 +361,9 @@ public void DB009_ShredDocument() } foreach (var doc in documentList) { - string fileName = String.Format("DB009-Section{0:000}.docx", doc.DocumentNumber); + var fileName = string.Format("DB009-Section{0:000}.docx", doc.DocumentNumber); var fiSection = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, fileName)); - List documentSource = new List { + var documentSource = new List { new Source(new WmlDocument(spec.FullName), doc.Start, doc.Count, true) }; DocumentBuilder.BuildDocument(documentSource, fiSection.FullName); @@ -526,14 +371,14 @@ public void DB009_ShredDocument() } // Re-assemble the parts into a single document. - List sources = TestUtil.TempDir + var sources = TestUtil.TempDir .GetFiles("DB009-Section*.docx") .Select(d => new Source(new WmlDocument(d.FullName), true)) .ToList(); var fiReassembled = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB009-Reassembled.docx")); DocumentBuilder.BuildDocument(sources, fiReassembled.FullName); - using (WordprocessingDocument doc = WordprocessingDocument.Open(fiReassembled.FullName, true)) + using (var doc = WordprocessingDocument.Open(fiReassembled.FullName, true)) { ReferenceAdder.AddToc(doc, "/w:document/w:body/w:p[1]", @"TOC \o '1-3' \h \z \u", null, null); @@ -541,33 +386,32 @@ public void DB009_ShredDocument() Validate(fiReassembled); } - [Fact] public void DB010_InsertUsingInsertId() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo front = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-FrontMatter.docx")); - FileInfo insert01 = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-Insert-01.docx")); - FileInfo insert02 = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-Insert-02.docx")); - FileInfo template = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-Template.docx")); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var front = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-FrontMatter.docx")); + var insert01 = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-Insert-01.docx")); + var insert02 = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-Insert-02.docx")); + var template = new FileInfo(Path.Combine(sourceDir.FullName, "DB010-Template.docx")); - WmlDocument doc1 = new WmlDocument(template.FullName); - using (MemoryStream mem = new MemoryStream()) + var doc1 = new WmlDocument(template.FullName); + using (var mem = new MemoryStream()) { mem.Write(doc1.DocumentByteArray, 0, doc1.DocumentByteArray.Length); - using (WordprocessingDocument doc = WordprocessingDocument.Open(mem, true)) + using (var doc = WordprocessingDocument.Open(mem, true)) { - XDocument xDoc = doc.MainDocumentPart.GetXDocument(); - XElement frontMatterPara = xDoc.Root.Descendants(W.txbxContent).Elements(W.p).FirstOrDefault(); + var xDoc = doc.MainDocumentPart.GetXDocument(); + var frontMatterPara = xDoc.Root.Descendants(W.txbxContent).Elements(W.p).FirstOrDefault(); frontMatterPara.ReplaceWith( new XElement(PtOpenXml.Insert, new XAttribute("Id", "Front"))); - XElement tbl = xDoc.Root.Element(W.body).Elements(W.tbl).FirstOrDefault(); - XElement firstCell = tbl.Descendants(W.tr).First().Descendants(W.p).First(); + var tbl = xDoc.Root.Element(W.body).Elements(W.tbl).FirstOrDefault(); + var firstCell = tbl.Descendants(W.tr).First().Descendants(W.p).First(); firstCell.ReplaceWith( new XElement(PtOpenXml.Insert, new XAttribute("Id", "Liz"))); - XElement secondCell = tbl.Descendants(W.tr).Skip(1).First().Descendants(W.p).First(); + var secondCell = tbl.Descendants(W.tr).Skip(1).First().Descendants(W.p).First(); secondCell.ReplaceWith( new XElement(PtOpenXml.Insert, new XAttribute("Id", "Eric"))); @@ -576,7 +420,7 @@ public void DB010_InsertUsingInsertId() doc1.DocumentByteArray = mem.ToArray(); } - List sources = new List() + var sources = new List() { new Source(doc1, true), new Source(new WmlDocument(insert01.FullName), "Liz"), @@ -592,12 +436,10 @@ public void DB010_InsertUsingInsertId() public void DB011_BodyAndHeaderWithShapes() { // Both of theses documents have a shape with a DocProperties ID of 1. - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1 = new FileInfo(Path.Combine(sourceDir.FullName, "DB011-Header-With-Shape.docx")); - FileInfo source2 = new FileInfo(Path.Combine(sourceDir.FullName, "DB011-Body-With-Shape.docx")); - List sources = null; - - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1 = new FileInfo(Path.Combine(sourceDir.FullName, "DB011-Header-With-Shape.docx")); + var source2 = new FileInfo(Path.Combine(sourceDir.FullName, "DB011-Body-With-Shape.docx")); + var sources = new List() { new Source(new WmlDocument(source1.FullName)), new Source(new WmlDocument(source2.FullName)), @@ -610,17 +452,14 @@ public void DB011_BodyAndHeaderWithShapes() ValidateUniqueDocPrIds(processedDestDocx); } - [Fact] public void DB012_NumberingsWithSameAbstractNumbering() { // This document has three numbering definitions that use the same abstract numbering definition. - string name = "DB012-Lists-With-Different-Numberings.docx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - - List sources = null; - sources = new List() + var name = "DB012-Lists-With-Different-Numberings.docx"; + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sources = new List() { new Source(new WmlDocument(sourceDocx.FullName)), }; @@ -628,11 +467,9 @@ public void DB012_NumberingsWithSameAbstractNumbering() sourceDocx.Name.Replace(".docx", "-processed-by-DocumentBuilder.docx"))); DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false)) - { - var numberingRoot = wDoc.MainDocumentPart.NumberingDefinitionsPart.GetXDocument().Root; - Assert.Equal(3, numberingRoot.Elements(W.num).Count()); - } + using var wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false); + var numberingRoot = wDoc.MainDocumentPart.NumberingDefinitionsPart.GetXDocument().Root; + Assert.Equal(3, numberingRoot.Elements(W.num).Count()); } [Fact] @@ -640,10 +477,10 @@ public void DB013a_LocalizedStyleIds_Heading() { // Each of these documents have changed the font color of the Heading 1 style, one to red, the other to green. // One of the documents were created with English as the Word display language, the other with Danish as the language. - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1 = + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1 = new FileInfo(Path.Combine(sourceDir.FullName, "DB013a-Red-Heading1-English.docx")); - FileInfo source2 = new FileInfo(Path.Combine(sourceDir.FullName, + var source2 = new FileInfo(Path.Combine(sourceDir.FullName, "DB013a-Green-Heading1-Danish.docx")); List sources = null; @@ -656,17 +493,15 @@ public void DB013a_LocalizedStyleIds_Heading() new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB013a-Colored-Heading1.docx")); DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false)) - { - var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.style).ToArray(); - Assert.Equal(1, styles.Count(s => s.Element(W.name).Attribute(W.val).Value == "heading 1")); + using var wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false); + var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.style).ToArray(); + Assert.Equal(1, styles.Count(s => s.Element(W.name).Attribute(W.val).Value == "heading 1")); - var styleIds = new HashSet(styles.Select(s => s.Attribute(W.styleId).Value)); - var paragraphStylesIds = new HashSet(wDoc.MainDocumentPart.GetXDocument() - .Descendants(W.pStyle) - .Select(p => p.Attribute(W.val).Value)); - Assert.Subset(styleIds, paragraphStylesIds); - } + var styleIds = new HashSet(styles.Select(s => s.Attribute(W.styleId).Value)); + var paragraphStylesIds = new HashSet(wDoc.MainDocumentPart.GetXDocument() + .Descendants(W.pStyle) + .Select(p => p.Attribute(W.val).Value)); + Assert.Subset(styleIds, paragraphStylesIds); } [Fact] @@ -674,10 +509,10 @@ public void DB013b_LocalizedStyleIds_List() { // Each of these documents have changed the font color of the List Paragraph style, one to orange, the other to blue. // One of the documents were created with English as the Word display language, the other with Danish as the language. - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1 = + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1 = new FileInfo(Path.Combine(sourceDir.FullName, "DB013b-Orange-List-Danish.docx")); - FileInfo source2 = new FileInfo(Path.Combine(sourceDir.FullName, + var source2 = new FileInfo(Path.Combine(sourceDir.FullName, "DB013b-Blue-List-English.docx")); List sources = null; @@ -690,27 +525,23 @@ public void DB013b_LocalizedStyleIds_List() new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "DB013b-Colored-List.docx")); DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false)) - { - var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.style).ToArray(); - Assert.Equal(1, styles.Count(s => s.Element(W.name).Attribute(W.val).Value == "List Paragraph")); + using var wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false); + var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.style).ToArray(); + Assert.Equal(1, styles.Count(s => s.Element(W.name).Attribute(W.val).Value == "List Paragraph")); - var styleIds = new HashSet(styles.Select(s => s.Attribute(W.styleId).Value)); - var paragraphStylesIds = new HashSet(wDoc.MainDocumentPart.GetXDocument() - .Descendants(W.pStyle) - .Select(p => p.Attribute(W.val).Value)); - Assert.Subset(styleIds, paragraphStylesIds); - } + var styleIds = new HashSet(styles.Select(s => s.Attribute(W.styleId).Value)); + var paragraphStylesIds = new HashSet(wDoc.MainDocumentPart.GetXDocument() + .Descendants(W.pStyle) + .Select(p => p.Attribute(W.val).Value)); + Assert.Subset(styleIds, paragraphStylesIds); } [Fact] public void DB014_KeepWebExtensions() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source = new FileInfo(Path.Combine(sourceDir.FullName, "DB014-WebExtensions.docx")); - List sources = null; - - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source = new FileInfo(Path.Combine(sourceDir.FullName, "DB014-WebExtensions.docx")); + var sources = new List() { new Source(new WmlDocument(source.FullName)), }; @@ -718,22 +549,18 @@ public void DB014_KeepWebExtensions() DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName); Validate(processedDestDocx); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false)) - { - Assert.NotNull(wDoc.WebExTaskpanesPart); - Assert.Equal(2, wDoc.WebExTaskpanesPart.Taskpanes.ChildElements.Count); - Assert.Equal(2, wDoc.WebExTaskpanesPart.WebExtensionParts.Count()); - } + using var wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, false); + Assert.NotNull(wDoc.WebExTaskpanesPart); + Assert.Equal(2, wDoc.WebExTaskpanesPart.Taskpanes.ChildElements.Count); + Assert.Equal(2, wDoc.WebExTaskpanesPart.WebExtensionParts.Count()); } [Fact] public void DB015_LatentStyles() { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source = new FileInfo(Path.Combine(sourceDir.FullName, "DB015-LatentStyles.docx")); - List sources = null; - - sources = new List() + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source = new FileInfo(Path.Combine(sourceDir.FullName, "DB015-LatentStyles.docx")); + var sources = new List() { new Source(new WmlDocument(source.FullName)), }; @@ -752,12 +579,10 @@ public void DB015_LatentStyles() [Fact] public void DB0016_DocDefaultStyles() { - string name = "DB0016-DocDefaultStyles.docx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - - List sources = null; - sources = new List() + var name = "DB0016-DocDefaultStyles.docx"; + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sources = new List() { new Source(new WmlDocument(sourceDocx.FullName), true), }; @@ -765,11 +590,26 @@ public void DB0016_DocDefaultStyles() sourceDocx.Name.Replace(".docx", "-processed-by-DocumentBuilder.docx"))); DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, true)) + using var wDoc = WordprocessingDocument.Open(processedDestDocx.FullName, true); + var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.docDefaults).ToArray(); + Assert.Single(styles); + } + + [Fact] + public void DB017_ApplyHeaderAndFooterToAllDocs() + { + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var source1 = new FileInfo(Path.Combine(sourceDir.FullName, "DB017-ApplyHeaderAndFooterToAllDocs-Portrait-TwoColumns.docx")); + var source2 = new FileInfo(Path.Combine(sourceDir.FullName, "DB017-ApplyHeaderAndFooterToAllDocs-Landscape-SingleColumn.docx")); + + var sources = new List() { - var styles = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.docDefaults).ToArray(); - Assert.Single(styles); - } + new Source(new WmlDocument(source1.FullName)){KeepSections = true}, + new Source(new WmlDocument(source2.FullName)){KeepSections = true, DiscardHeadersAndFootersInKeptSections = true}, + }; + + var processedDestDocx = new FileInfo(Path.Combine(Path.Combine(TestUtil.TempDir.FullName), "DB017-ApplyHeaderAndFooterToAllDocs.docx")); + DocumentBuilder.BuildDocument(sources, processedDestDocx.FullName); } [Theory] @@ -785,20 +625,18 @@ public void DB0016_DocDefaultStyles() [InlineData("DB100-00100", "DB/GlossaryDocuments/FooterContent-built.docx", "DB/GlossaryDocuments/BaseDocument.docx,0,4", "DB/GlossaryDocuments/FooterContent.docx", "DB/GlossaryDocuments/BaseDocument.docx,4", null, null, null)] [InlineData("DB100-00110", "DB/GlossaryDocuments/HeaderContent-built.docx", "DB/GlossaryDocuments/BaseDocument.docx,0,4", "DB/GlossaryDocuments/HeaderContent.docx", "DB/GlossaryDocuments/BaseDocument.docx,4", null, null, null)] [InlineData("DB100-00200", null, "DB/GlossaryDocuments/BaseDocument.docx", "DB/GlossaryDocuments/CellLevelContentControl.docx", "DB/GlossaryDocuments/NestedContentControl.docx", null, null, null)] - public void WithGlossaryDocuments(string testId, string baseline, string src1, string src2, string src3, string src4, string src5, string src6) { var rawSources = new string[] { src1, src2, src3, src4, src5, src6, }; var sourcesStr = rawSources.Where(s => s != null).ToArray(); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Load the source documents - List sources = sourcesStr.Select(s => + var sources = sourcesStr.Select(s => { var spl = s.Split(','); if (spl.Length == 1) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); var sourceFi = new FileInfo(Path.Combine(sourceDir.FullName, s)); var wmlSource = new WmlDocument(sourceFi.FullName); return new Source(wmlSource); @@ -806,7 +644,7 @@ public void WithGlossaryDocuments(string testId, string baseline, string src1, s else if (spl.Length == 2) { var start = int.Parse(spl[1]); - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); var sourceFi = new FileInfo(Path.Combine(sourceDir.FullName, spl[0])); return new Source(sourceFi.FullName, start, true); } @@ -814,24 +652,27 @@ public void WithGlossaryDocuments(string testId, string baseline, string src1, s { var start = int.Parse(spl[1]); var count = int.Parse(spl[2]); - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); var sourceFi = new FileInfo(Path.Combine(sourceDir.FullName, spl[0])); return new Source(sourceFi.FullName, start, count, true); } }) .ToList(); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Create the dir for the test var rootTempDir = TestUtil.TempDir; var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); if (thisTestTempDir.Exists) + { Assert.True(false, "Duplicate test id: " + testId); + } else + { thisTestTempDir.Create(); + } + var tempDirFullName = thisTestTempDir.FullName; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Copy sources to temp directory, for ease of review foreach (var item in sources) @@ -839,51 +680,50 @@ public void WithGlossaryDocuments(string testId, string baseline, string src1, s var fi = new FileInfo(item.WmlDocument.FileName); var sourceCopiedToDestFi = new FileInfo(Path.Combine(tempDirFullName, fi.Name)); if (!sourceCopiedToDestFi.Exists) + { File.Copy(item.WmlDocument.FileName, sourceCopiedToDestFi.FullName); + } } if (baseline != null) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); var baselineFi = new FileInfo(Path.Combine(sourceDir.FullName, baseline)); var baselineCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, baselineFi.Name)); File.Copy(baselineFi.FullName, baselineCopiedToDestFileName.FullName); } - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Use DocumentBuilder to build the destination document var outFi = new FileInfo(Path.Combine(tempDirFullName, "Output.docx")); - DocumentBuilderSettings settings = new DocumentBuilderSettings(); + var settings = new DocumentBuilderSettings(); DocumentBuilder.BuildDocument(sources, outFi.FullName, settings); Validate(outFi); } private void Validate(FileInfo fi) { - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(fi.FullName, true)) + using var wDoc = WordprocessingDocument.Open(fi.FullName, true); + var v = new OpenXmlValidator(); + var errors = v.Validate(wDoc).Where(ve => { - OpenXmlValidator v = new OpenXmlValidator(); - var errors = v.Validate(wDoc).Where(ve => - { - var found = s_ExpectedErrors.Any(xe => ve.Description.Contains(xe)); - return !found; - }); + var found = s_ExpectedErrors.Any(xe => ve.Description.Contains(xe)); + return !found; + }); - if (errors.Count() != 0) + if (errors.Any()) + { + var sb = new StringBuilder(); + foreach (var item in errors) { - StringBuilder sb = new StringBuilder(); - foreach (var item in errors) - { - sb.Append(item.Description).Append(Environment.NewLine); - } - var s = sb.ToString(); - Assert.True(false, s); + sb.Append(item.Description).Append(Environment.NewLine); } + var s = sb.ToString(); + Assert.True(false, s); } } - private static List s_ExpectedErrors = new List() + private static readonly List s_ExpectedErrors = new List() { "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenHBand' attribute is not declared.", "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenVBand' attribute is not declared.", @@ -916,25 +756,44 @@ private void Validate(FileInfo fi) private void ValidateUniqueDocPrIds(FileInfo fi) { - using (WordprocessingDocument doc = WordprocessingDocument.Open(fi.FullName, false)) + using var doc = WordprocessingDocument.Open(fi.FullName, false); + var docPrIds = new HashSet(); + foreach (var item in doc.MainDocumentPart.GetXDocument().Descendants(WP.docPr)) + { + Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); + } + + foreach (var header in doc.MainDocumentPart.HeaderParts) { - var docPrIds = new HashSet(); - foreach (var item in doc.MainDocumentPart.GetXDocument().Descendants(WP.docPr)) - Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); - foreach (var header in doc.MainDocumentPart.HeaderParts) foreach (var item in header.GetXDocument().Descendants(WP.docPr)) + { Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); - foreach (var footer in doc.MainDocumentPart.FooterParts) + } + } + + foreach (var footer in doc.MainDocumentPart.FooterParts) + { foreach (var item in footer.GetXDocument().Descendants(WP.docPr)) + { Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); - if (doc.MainDocumentPart.FootnotesPart != null) - foreach (var item in doc.MainDocumentPart.FootnotesPart.GetXDocument().Descendants(WP.docPr)) - Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); - if (doc.MainDocumentPart.EndnotesPart != null) - foreach (var item in doc.MainDocumentPart.EndnotesPart.GetXDocument().Descendants(WP.docPr)) - Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); + } + } + + if (doc.MainDocumentPart.FootnotesPart != null) + { + foreach (var item in doc.MainDocumentPart.FootnotesPart.GetXDocument().Descendants(WP.docPr)) + { + Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); + } + } + + if (doc.MainDocumentPart.EndnotesPart != null) + { + foreach (var item in doc.MainDocumentPart.EndnotesPart.GetXDocument().Descendants(WP.docPr)) + { + Assert.True(docPrIds.Add(item.Attribute(NoNamespace.id).Value)); + } } } } -} -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/FormattingAssemblerTests.cs b/OpenXmlPowerTools.Tests/FormattingAssemblerTests.cs index 2ce730b1..4980dcbf 100644 --- a/OpenXmlPowerTools.Tests/FormattingAssemblerTests.cs +++ b/OpenXmlPowerTools.Tests/FormattingAssemblerTests.cs @@ -1,24 +1,14 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Validation; using System; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using DocumentFormat.OpenXml.Wordprocessing; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class FaTests { @@ -47,38 +37,41 @@ public class FaTests [InlineData("FA001-00220", "FA/RevTracking/022-TablePropertiesChange.docx")] [InlineData("FA001-00230", "FA/RevTracking/023-CellPropertiesChange.docx")] [InlineData("FA001-00240", "FA/RevTracking/024-RowPropertiesChange.docx")] - public void FA001_DocumentsWithRevTracking(string testId, string src) { - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Load the source document - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocxFi = new FileInfo(Path.Combine(sourceDir.FullName, src)); - WmlDocument wmlSourceDocument = new WmlDocument(sourceDocxFi.FullName); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocxFi = new FileInfo(Path.Combine(sourceDir.FullName, src)); + var wmlSourceDocument = new WmlDocument(sourceDocxFi.FullName); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Create the dir for the test var rootTempDir = TestUtil.TempDir; var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); if (thisTestTempDir.Exists) + { Assert.True(false, "Duplicate test id: " + testId); + } else + { thisTestTempDir.Create(); + } + var tempDirFullName = thisTestTempDir.FullName; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Copy src DOCX to temp directory, for ease of review var sourceDocxCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, sourceDocxFi.Name)); if (!sourceDocxCopiedToDestFileName.Exists) + { wmlSourceDocument.SaveAs(sourceDocxCopiedToDestFileName.FullName); + } var sourceDocxAcceptedCopiedToDestFileName = new FileInfo(Path.Combine(tempDirFullName, sourceDocxFi.Name.ToLower().Replace(".docx", "-accepted.docx"))); var wmlSourceAccepted = RevisionProcessor.AcceptRevisions(wmlSourceDocument); wmlSourceAccepted.SaveAs(sourceDocxAcceptedCopiedToDestFileName.FullName); var outFi = new FileInfo(Path.Combine(tempDirFullName, "Output.docx")); - FormattingAssemblerSettings settings = new FormattingAssemblerSettings(); + var settings = new FormattingAssemblerSettings(); var assembledWml = FormattingAssembler.AssembleFormatting(wmlSourceDocument, settings); assembledWml.SaveAs(outFi.FullName); @@ -91,29 +84,27 @@ public void FA001_DocumentsWithRevTracking(string testId, string src) private void Validate(FileInfo fi) { - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(fi.FullName, true)) + using var wDoc = WordprocessingDocument.Open(fi.FullName, true); + var v = new OpenXmlValidator(); + var errors = v.Validate(wDoc).Where(ve => { - OpenXmlValidator v = new OpenXmlValidator(); - var errors = v.Validate(wDoc).Where(ve => - { - var found = s_ExpectedErrors.Any(xe => ve.Description.Contains(xe)); - return !found; - }); + var found = s_ExpectedErrors.Any(xe => ve.Description.Contains(xe)); + return !found; + }); - if (errors.Count() != 0) + if (errors.Any()) + { + var sb = new StringBuilder(); + foreach (var item in errors) { - StringBuilder sb = new StringBuilder(); - foreach (var item in errors) - { - sb.Append(item.Description).Append(Environment.NewLine); - } - var s = sb.ToString(); - Assert.True(false, s); + sb.Append(item.Description).Append(Environment.NewLine); } + var s = sb.ToString(); + Assert.True(false, s); } } - private static List s_ExpectedErrors = new List() + private static readonly List s_ExpectedErrors = new List() { "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenHBand' attribute is not declared.", "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:evenVBand' attribute is not declared.", @@ -146,5 +137,4 @@ private void Validate(FileInfo fi) "The element has invalid child element 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:del'.", }; } -} -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/HtmlConverterTests.cs b/OpenXmlPowerTools.Tests/HtmlConverterTests.cs deleted file mode 100644 index 9bdb2165..00000000 --- a/OpenXmlPowerTools.Tests/HtmlConverterTests.cs +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#define COPY_FILES_FOR_DEBUGGING - -// DO_CONVERSION_VIA_WORD is defined in the project OpenXmlPowerTools.Tests.OA.csproj, but not in the OpenXmlPowerTools.Tests.csproj - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using Xunit; - -#if DO_CONVERSION_VIA_WORD -using Word = Microsoft.Office.Interop.Word; -#endif - -#if !ELIDE_XUNIT_TESTS - -namespace OxPt -{ - public class HcTests - { - public static bool s_CopySourceFiles = true; - public static bool s_CopyFormattingAssembledDocx = true; - public static bool s_ConvertUsingWord = true; - - // PowerShell oneliner that generates InlineData for all files in a directory - // dir | % { '[InlineData("' + $_.Name + '")]' } | clip - - [Theory] - [InlineData("HC001-5DayTourPlanTemplate.docx")] - [InlineData("HC002-Hebrew-01.docx")] - [InlineData("HC003-Hebrew-02.docx")] - [InlineData("HC004-ResumeTemplate.docx")] - [InlineData("HC005-TaskPlanTemplate.docx")] - [InlineData("HC006-Test-01.docx")] - [InlineData("HC007-Test-02.docx")] - [InlineData("HC008-Test-03.docx")] - [InlineData("HC009-Test-04.docx")] - [InlineData("HC010-Test-05.docx")] - [InlineData("HC011-Test-06.docx")] - [InlineData("HC012-Test-07.docx")] - [InlineData("HC013-Test-08.docx")] - [InlineData("HC014-RTL-Table-01.docx")] - [InlineData("HC015-Vertical-Spacing-atLeast.docx")] - [InlineData("HC016-Horizontal-Spacing-firstLine.docx")] - [InlineData("HC017-Vertical-Alignment-Cell-01.docx")] - [InlineData("HC018-Vertical-Alignment-Para-01.docx")] - [InlineData("HC019-Hidden-Run.docx")] - [InlineData("HC020-Small-Caps.docx")] - [InlineData("HC021-Symbols.docx")] - [InlineData("HC022-Table-Of-Contents.docx")] - [InlineData("HC023-Hyperlink.docx")] - [InlineData("HC024-Tabs-01.docx")] - [InlineData("HC025-Tabs-02.docx")] - [InlineData("HC026-Tabs-03.docx")] - [InlineData("HC027-Tabs-04.docx")] - [InlineData("HC028-No-Break-Hyphen.docx")] - [InlineData("HC029-Table-Merged-Cells.docx")] - [InlineData("HC030-Content-Controls.docx")] - [InlineData("HC031-Complicated-Document.docx")] - [InlineData("HC032-Named-Color.docx")] - [InlineData("HC033-Run-With-Border.docx")] - [InlineData("HC034-Run-With-Position.docx")] - [InlineData("HC035-Strike-Through.docx")] - [InlineData("HC036-Super-Script.docx")] - [InlineData("HC037-Sub-Script.docx")] - [InlineData("HC038-Conflicting-Border-Weight.docx")] - [InlineData("HC039-Bold.docx")] - [InlineData("HC040-Hyperlink-Fieldcode-01.docx")] - [InlineData("HC041-Hyperlink-Fieldcode-02.docx")] - [InlineData("HC042-Image-Png.docx")] - [InlineData("HC043-Chart.docx")] - [InlineData("HC044-Embedded-Workbook.docx")] - [InlineData("HC045-Italic.docx")] - [InlineData("HC046-BoldAndItalic.docx")] - [InlineData("HC047-No-Section.docx")] - [InlineData("HC048-Excerpt.docx")] - [InlineData("HC049-Borders.docx")] - [InlineData("HC050-Shaded-Text-01.docx")] - [InlineData("HC051-Shaded-Text-02.docx")] - [InlineData("HC060-Image-with-Hyperlink.docx")] - [InlineData("HC061-Hyperlink-in-Field.docx")] - - public void HC001(string name) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - -#if COPY_FILES_FOR_DEBUGGING - var sourceCopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-1-Source.docx"))); - if (!sourceCopiedToDestDocx.Exists) - File.Copy(sourceDocx.FullName, sourceCopiedToDestDocx.FullName); - - var assembledFormattingDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-2-FormattingAssembled.docx"))); - if (!assembledFormattingDestDocx.Exists) - CopyFormattingAssembledDocx(sourceDocx, assembledFormattingDestDocx); -#endif - - var oxPtConvertedDestHtml = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-3-OxPt.html"))); - ConvertToHtml(sourceDocx, oxPtConvertedDestHtml); - -#if DO_CONVERSION_VIA_WORD - var wordConvertedDocHtml = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-4-Word.html"))); - ConvertToHtmlUsingWord(sourceDocx, wordConvertedDocHtml); -#endif - - } - - [Theory] - [InlineData("HC006-Test-01.docx")] - public void HC002_NoCssClasses(string name) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - - var oxPtConvertedDestHtml = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-5-OxPt-No-CSS-Classes.html"))); - ConvertToHtmlNoCssClasses(sourceDocx, oxPtConvertedDestHtml); - } - - private static void CopyFormattingAssembledDocx(FileInfo source, FileInfo dest) - { - var ba = File.ReadAllBytes(source.FullName); - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(ba, 0, ba.Length); - using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(ms, true)) - { - - RevisionAccepter.AcceptRevisions(wordDoc); - SimplifyMarkupSettings simplifyMarkupSettings = new SimplifyMarkupSettings - { - RemoveComments = true, - RemoveContentControls = true, - RemoveEndAndFootNotes = true, - RemoveFieldCodes = false, - RemoveLastRenderedPageBreak = true, - RemovePermissions = true, - RemoveProof = true, - RemoveRsidInfo = true, - RemoveSmartTags = true, - RemoveSoftHyphens = true, - RemoveGoBackBookmark = true, - ReplaceTabsWithSpaces = false, - }; - MarkupSimplifier.SimplifyMarkup(wordDoc, simplifyMarkupSettings); - - FormattingAssemblerSettings formattingAssemblerSettings = new FormattingAssemblerSettings - { - RemoveStyleNamesFromParagraphAndRunProperties = false, - ClearStyles = false, - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - CreateHtmlConverterAnnotationAttributes = true, - OrderElementsPerStandard = false, - ListItemRetrieverSettings = - new ListItemRetrieverSettings() - { - ListItemTextImplementations = ListItemRetrieverSettings.DefaultListItemTextImplementations, - }, - }; - - FormattingAssembler.AssembleFormatting(wordDoc, formattingAssemblerSettings); - } - var newBa = ms.ToArray(); - File.WriteAllBytes(dest.FullName, newBa); - } - } - - private static void ConvertToHtml(FileInfo sourceDocx, FileInfo destFileName) - { - byte[] byteArray = File.ReadAllBytes(sourceDocx.FullName); - using (MemoryStream memoryStream = new MemoryStream()) - { - memoryStream.Write(byteArray, 0, byteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) - { - var outputDirectory = destFileName.Directory; - destFileName = new FileInfo(Path.Combine(outputDirectory.FullName, destFileName.Name)); - var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - int imageCounter = 0; - var pageTitle = (string)wDoc.CoreFilePropertiesPart.GetXDocument().Descendants(DC.title).FirstOrDefault(); - if (pageTitle == null) - pageTitle = sourceDocx.FullName; - - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings() - { - PageTitle = pageTitle, - FabricateCssClasses = true, - CssClassPrefix = "pt-", - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - ImageHandler = imageInfo => - { - DirectoryInfo localDirInfo = new DirectoryInfo(imageDirectoryName); - if (!localDirInfo.Exists) - localDirInfo.Create(); - ++imageCounter; - string extension = imageInfo.ContentType.Split('/')[1].ToLower(); - ImageFormat imageFormat = null; - if (extension == "png") - { - // Convert png to jpeg. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "gif") - imageFormat = ImageFormat.Gif; - else if (extension == "bmp") - imageFormat = ImageFormat.Bmp; - else if (extension == "jpeg") - imageFormat = ImageFormat.Jpeg; - else if (extension == "tiff") - { - // Convert tiff to gif. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "x-wmf") - { - extension = "wmf"; - imageFormat = ImageFormat.Wmf; - } - - // If the image format isn't one that we expect, ignore it, - // and don't return markup for the link. - if (imageFormat == null) - return null; - - string imageFileName = imageDirectoryName + "/image" + - imageCounter.ToString() + "." + extension; - try - { - imageInfo.Bitmap.Save(imageFileName, imageFormat); - } - catch (System.Runtime.InteropServices.ExternalException) - { - return null; - } - XElement img = new XElement(Xhtml.img, - new XAttribute(NoNamespace.src, imageFileName), - imageInfo.ImgStyleAttribute, - imageInfo.AltText != null ? - new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); - return img; - } - }; - XElement html = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); - - // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type - // XEntity. PtOpenXmlUtil.cs define the XEntity class. See - // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx - // for detailed explanation. - // - // If you further transform the XML tree returned by ConvertToHtmlTransform, you - // must do it correctly, or entities will not be serialized properly. - - var htmlString = html.ToString(SaveOptions.DisableFormatting); - File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); - } - } - } - - private static void ConvertToHtmlNoCssClasses(FileInfo sourceDocx, FileInfo destFileName) - { - byte[] byteArray = File.ReadAllBytes(sourceDocx.FullName); - using (MemoryStream memoryStream = new MemoryStream()) - { - memoryStream.Write(byteArray, 0, byteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) - { - var outputDirectory = destFileName.Directory; - destFileName = new FileInfo(Path.Combine(outputDirectory.FullName, destFileName.Name)); - var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - int imageCounter = 0; - var pageTitle = (string)wDoc.CoreFilePropertiesPart.GetXDocument().Descendants(DC.title).FirstOrDefault(); - if (pageTitle == null) - pageTitle = sourceDocx.FullName; - - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings() - { - PageTitle = pageTitle, - FabricateCssClasses = false, - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - ImageHandler = imageInfo => - { - DirectoryInfo localDirInfo = new DirectoryInfo(imageDirectoryName); - if (!localDirInfo.Exists) - localDirInfo.Create(); - ++imageCounter; - string extension = imageInfo.ContentType.Split('/')[1].ToLower(); - ImageFormat imageFormat = null; - if (extension == "png") - { - // Convert png to jpeg. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "gif") - imageFormat = ImageFormat.Gif; - else if (extension == "bmp") - imageFormat = ImageFormat.Bmp; - else if (extension == "jpeg") - imageFormat = ImageFormat.Jpeg; - else if (extension == "tiff") - { - // Convert tiff to gif. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "x-wmf") - { - extension = "wmf"; - imageFormat = ImageFormat.Wmf; - } - - // If the image format isn't one that we expect, ignore it, - // and don't return markup for the link. - if (imageFormat == null) - return null; - - string imageFileName = imageDirectoryName + "/image" + - imageCounter.ToString() + "." + extension; - try - { - imageInfo.Bitmap.Save(imageFileName, imageFormat); - } - catch (System.Runtime.InteropServices.ExternalException) - { - return null; - } - XElement img = new XElement(Xhtml.img, - new XAttribute(NoNamespace.src, imageFileName), - imageInfo.ImgStyleAttribute, - imageInfo.AltText != null ? - new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); - return img; - } - }; - XElement html = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); - - // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type - // XEntity. PtOpenXmlUtil.cs define the XEntity class. See - // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx - // for detailed explanation. - // - // If you further transform the XML tree returned by ConvertToHtmlTransform, you - // must do it correctly, or entities will not be serialized properly. - - var htmlString = html.ToString(SaveOptions.DisableFormatting); - File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); - } - } - } - -#if DO_CONVERSION_VIA_WORD - public static void ConvertToHtmlUsingWord(FileInfo sourceFileName, FileInfo destFileName) - { - Word.Application app = new Word.Application(); - app.Visible = false; - try - { - Word.Document doc = app.Documents.Open(sourceFileName.FullName); - doc.SaveAs2(destFileName.FullName, Word.WdSaveFormat.wdFormatFilteredHTML); - } - catch (System.Runtime.InteropServices.COMException) - { - Console.WriteLine("Caught unexpected COM exception."); - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - Environment.Exit(0); - } - ((Microsoft.Office.Interop.Word._Application)app).Quit(); - } -#endif - } -} - -#endif diff --git a/OpenXmlPowerTools.Tests/HtmlToWmlConverterTests.cs b/OpenXmlPowerTools.Tests/HtmlToWmlConverterTests.cs index 3bec4ffe..56630fba 100644 --- a/OpenXmlPowerTools.Tests/HtmlToWmlConverterTests.cs +++ b/OpenXmlPowerTools.Tests/HtmlToWmlConverterTests.cs @@ -1,21 +1,10 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Validation; using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; using Xunit; -using System.Text.RegularExpressions; /******************************************************************************************* * HtmlToWmlConverter expects the HTML to be passed as an XElement, i.e. as XML. While the HTML test files that @@ -23,30 +12,24 @@ * The best solution is to use the HtmlAgilityPack, which can parse HTML and save as XML. The HtmlAgilityPack * is licensed under the Ms-PL (same as Open-Xml-PowerTools) so it is convenient to include it in your solution, * and thereby you can convert HTML to XML that can be processed by the HtmlToWmlConverter. - * + * * A convenient way to get the DLL that has been checked out with HtmlToWmlConverter is to clone the repo at * https://github.com/EricWhiteDev/HtmlAgilityPack - * + * * That repo contains only the DLL that has been checked out with HtmlToWmlConverter. - * + * * Of course, you can also get the HtmlAgilityPack source and compile it to get the DLL. You can find it at * http://codeplex.com/HtmlAgilityPack - * + * * We don't include the HtmlAgilityPack in Open-Xml-PowerTools, to simplify installation. The XUnit tests in * this module do not require the HtmlAgilityPack to run. *******************************************************************************************/ -#if DO_CONVERSION_VIA_WORD -using Word = Microsoft.Office.Interop.Word; -#endif - -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class HwTests { - static bool s_ProduceAnnotatedHtml = true; + private static readonly bool s_ProduceAnnotatedHtml = true; // PowerShell oneliner that generates InlineData for all files in a directory // dir | % { '[InlineData("' + $_.Name + '")]' } | clip @@ -282,108 +265,26 @@ public class HwTests [InlineData("T1830.html")] [InlineData("T1840.html")] [InlineData("T1850.html")] - public void HW001(string name) { -#if false - string[] cssFilter = new[] { - "text-indent", - "margin-left", - "margin-right", - "padding-left", - "padding-right", - }; -#else - string[] cssFilter = null; -#endif - -#if false - string[] htmlFilter = new[] { - "img", - }; -#else - string[] htmlFilter = null; -#endif - - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); var sourceHtmlFi = new FileInfo(Path.Combine(sourceDir.FullName, name)); var sourceImageDi = new DirectoryInfo(Path.Combine(sourceDir.FullName, sourceHtmlFi.Name.Replace(".html", "_files"))); - - var destImageDi = new DirectoryInfo(Path.Combine(TestUtil.TempDir.FullName, sourceImageDi.Name)); - var sourceCopiedToDestHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceHtmlFi.Name.Replace(".html", "-1-Source.html"))); var destCssFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceHtmlFi.Name.Replace(".html", "-2.css"))); var destDocxFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceHtmlFi.Name.Replace(".html", "-3-ConvertedByHtmlToWml.docx"))); var annotatedHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceHtmlFi.Name.Replace(".html", "-4-Annotated.txt"))); - if (!sourceCopiedToDestHtmlFi.Exists) - File.Copy(sourceHtmlFi.FullName, sourceCopiedToDestHtmlFi.FullName); - XElement html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceCopiedToDestHtmlFi); - - string htmlString = html.ToString(); - if (htmlFilter != null && htmlFilter.Any()) - { - bool found = false; - foreach (var item in htmlFilter) - { - if (htmlString.Contains(item)) - { - found = true; - break; - } - } - if (!found) - { - sourceCopiedToDestHtmlFi.Delete(); - return; - } - } - - string usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); + var html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceHtmlFi); + var usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); File.WriteAllText(destCssFi.FullName, usedAuthorCss); - if (cssFilter != null && cssFilter.Any()) - { - bool found = false; - foreach (var item in cssFilter) - { - if (usedAuthorCss.Contains(item)) - { - found = true; - break; - } - } - if (!found) - { - sourceCopiedToDestHtmlFi.Delete(); - destCssFi.Delete(); - return; - } - } - - if (sourceImageDi.Exists) - { - destImageDi.Create(); - foreach (var file in sourceImageDi.GetFiles()) - { - File.Copy(file.FullName, destImageDi.FullName + "/" + file.Name); - } - } - - HtmlToWmlConverterSettings settings = HtmlToWmlConverter.GetDefaultSettings(); - // image references in HTML files contain the path to the subdir that contains the images, so base URI is the name of the directory - // that contains the HTML files - settings.BaseUriForImages = Path.Combine(TestUtil.TempDir.FullName); + var settings = HtmlToWmlConverter.GetDefaultSettings(); + // image references in HTML files contain the path to the subdir that contains the images, so base URI is the name of the directory that contains the HTML files + settings.BaseUriForImages = sourceDir.FullName; - WmlDocument doc = HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings, null, s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null); + var doc = HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings, null, s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null); Assert.NotNull(doc); - if (doc != null) - SaveValidateAndFormatMainDocPart(destDocxFi, doc); - -#if DO_CONVERSION_VIA_WORD - var newAltChunkBeforeFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".html", "-5-AltChunkBefore.docx"))); - var newAltChunkAfterFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, name.Replace(".html", "-6-ConvertedViaWord.docx"))); - WordAutomationUtilities.DoConversionViaWord(newAltChunkBeforeFi, newAltChunkAfterFi, html); -#endif + SaveValidateAndFormatMainDocPart(destDocxFi, doc); } [Theory] @@ -391,8 +292,7 @@ public void HW001(string name) [InlineData("E0020.html")] public void HW004(string name) { - - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); var sourceHtmlFi = new FileInfo(Path.Combine(sourceDir.FullName, name)); var sourceImageDi = new DirectoryInfo(Path.Combine(sourceDir.FullName, sourceHtmlFi.Name.Replace(".html", "_files"))); @@ -403,12 +303,12 @@ public void HW004(string name) var annotatedHtmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceHtmlFi.Name.Replace(".html", "-4-Annotated.txt"))); File.Copy(sourceHtmlFi.FullName, sourceCopiedToDestHtmlFi.FullName); - XElement html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceCopiedToDestHtmlFi); + var html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceCopiedToDestHtmlFi); - string usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); + var usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); File.WriteAllText(destCssFi.FullName, usedAuthorCss); - HtmlToWmlConverterSettings settings = HtmlToWmlConverter.GetDefaultSettings(); + var settings = HtmlToWmlConverter.GetDefaultSettings(); settings.BaseUriForImages = Path.Combine(TestUtil.TempDir.FullName); Assert.Throws(() => HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings, null, s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null)); @@ -418,29 +318,31 @@ private static void SaveValidateAndFormatMainDocPart(FileInfo destDocxFi, WmlDoc { WmlDocument formattedDoc; + if (File.Exists(destDocxFi.FullName)) + { + File.Delete(destDocxFi.FullName); + } + doc.SaveAs(destDocxFi.FullName); - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { ms.Write(doc.DocumentByteArray, 0, doc.DocumentByteArray.Length); - using (WordprocessingDocument document = WordprocessingDocument.Open(ms, true)) + using (var document = WordprocessingDocument.Open(ms, true)) { - XDocument xDoc = document.MainDocumentPart.GetXDocument(); + var xDoc = document.MainDocumentPart.GetXDocument(); document.MainDocumentPart.PutXDocumentWithFormatting(); - OpenXmlValidator validator = new OpenXmlValidator(); + var validator = new OpenXmlValidator(); var errors = validator.Validate(document); - var errorsString = errors - .Select(e => e.Description + Environment.NewLine) - .StringConcatenate(); + var errorsString = errors.Select(e => e.Description + Environment.NewLine).StringConcatenate(); - // Assert that there were no errors in the generated document. - Assert.Equal("", errorsString); + Assert.True(errorsString.Length == 0, $"Error in {destDocxFi.FullName}\n{errorsString}"); } formattedDoc = new WmlDocument(destDocxFi.FullName, ms.ToArray()); } formattedDoc.SaveAs(destDocxFi.FullName); } - static string defaultCss = + private const string defaultCss = @"html, address, blockquote, body, dd, div, @@ -514,8 +416,6 @@ private static void SaveValidateAndFormatMainDocPart(FileInfo destDocxFi, WmlDoc "; - static string userCss = @""; + private const string userCss = @""; } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/HtmlToWmlReadAsXElement.cs b/OpenXmlPowerTools.Tests/HtmlToWmlReadAsXElement.cs index 99998d4f..8defdb03 100644 --- a/OpenXmlPowerTools.Tests/HtmlToWmlReadAsXElement.cs +++ b/OpenXmlPowerTools.Tests/HtmlToWmlReadAsXElement.cs @@ -1,15 +1,7 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO; +using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -using OpenXmlPowerTools; /******************************************************************************************* * HtmlToWmlConverter expects the HTML to be passed as an XElement, i.e. as XML. While the HTML test files that @@ -17,30 +9,30 @@ * The best solution is to use the HtmlAgilityPack, which can parse HTML and save as XML. The HtmlAgilityPack * is licensed under the Ms-PL (same as Open-Xml-PowerTools) so it is convenient to include it in your solution, * and thereby you can convert HTML to XML that can be processed by the HtmlToWmlConverter. - * + * * A convenient way to get the DLL that has been checked out with HtmlToWmlConverter is to clone the repo at * https://github.com/EricWhiteDev/HtmlAgilityPack - * + * * That repo contains only the DLL that has been checked out with HtmlToWmlConverter. - * + * * Of course, you can also get the HtmlAgilityPack source and compile it to get the DLL. You can find it at * http://codeplex.com/HtmlAgilityPack - * + * * We don't include the HtmlAgilityPack in Open-Xml-PowerTools, to simplify installation. The XUnit tests in * this module do not require the HtmlAgilityPack to run. -*******************************************************************************************/ +*******************************************************************************************/ #if USE_HTMLAGILITYPACK using HtmlAgilityPack; #endif -namespace OpenXmlPowerTools +namespace Codeuctivity.Tests { public class HtmlToWmlReadAsXElement { public static XElement ReadAsXElement(FileInfo sourceHtmlFi) { - string htmlString = File.ReadAllText(sourceHtmlFi.FullName); + var htmlString = File.ReadAllText(sourceHtmlFi.FullName); XElement html = null; try { @@ -68,9 +60,9 @@ public static XElement ReadAsXElement(FileInfo sourceHtmlFi) html = XElement.Parse(sb.ToString()); } #else - catch (XmlException e) + catch (XmlException) { - throw e; + throw; } #endif html = (XElement)ConvertToNoNamespace(html); @@ -79,8 +71,7 @@ public static XElement ReadAsXElement(FileInfo sourceHtmlFi) private static object ConvertToNoNamespace(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { return new XElement(element.Name.LocalName, element.Attributes().Where(a => !a.IsNamespaceDeclaration), @@ -89,4 +80,4 @@ private static object ConvertToNoNamespace(XNode node) return node; } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/ImageHandlerTests.cs b/OpenXmlPowerTools.Tests/ImageHandlerTests.cs new file mode 100644 index 00000000..4e8b3d8d --- /dev/null +++ b/OpenXmlPowerTools.Tests/ImageHandlerTests.cs @@ -0,0 +1,93 @@ +using System.IO; +using System.Xml.Linq; +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using SkiaSharp; +using Xunit; + +namespace Codeuctivity.Tests +{ + public class ImageHandlerTests + { + [Theory] + [InlineData(SKEncodedImageFormat.Png, "image/png")] + [InlineData(SKEncodedImageFormat.Jpeg, "image/jpeg")] + [InlineData(SKEncodedImageFormat.Webp, "image/webp")] + public void ShouldTransformImagesToDataUri(SKEncodedImageFormat format, string mime) + { + using var surface = SKSurface.Create(new SKImageInfo(10, 10)); + surface.Canvas.Clear(SKColors.Red); + using var image = surface.Snapshot(); + using var ms = new MemoryStream(); + using (var data = image.Encode(format, 100)) + { + data.SaveTo(ms); + } + ms.Position = 0; + var info = new ImageInfo { Image = ms, AltText = "alt", ImgStyleAttribute = new XAttribute(NoNamespace.style, "width:10px") }; + var handler = new ImageHandler(); + var result = handler.TransformImage(info); + var srcAttr = result.Attribute(NoNamespace.src); + Assert.NotNull(srcAttr); + Assert.StartsWith($"data:{mime};base64,", srcAttr.Value); + Assert.Equal("width:10px", result.Attribute(NoNamespace.style)?.Value); + } + + [Fact] + public void ShouldTransformGifToDataUri() + { + var gif = System.Convert.FromBase64String("R0lGODlhAQABAPAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=="); + using var ms = new MemoryStream(gif); + var info = new ImageInfo { Image = ms }; + var handler = new ImageHandler(); + var result = handler.TransformImage(info); + var srcAttr = result.Attribute(NoNamespace.src); + Assert.NotNull(srcAttr); + Assert.StartsWith("data:image/gif;base64,", srcAttr.Value); + } + + [Fact] + public void ShouldThrowOnInvalidImage() + { + using var ms = new MemoryStream(new byte[] { 1, 2, 3, 4 }); + var handler = new ImageHandler(); + Assert.ThrowsAny(() => handler.TransformImage(new ImageInfo { Image = ms })); + } + + [Fact] + public void ShouldIncludeAltText() + { + using var surface = SKSurface.Create(new SKImageInfo(5, 5)); + surface.Canvas.Clear(SKColors.Blue); + using var image = surface.Snapshot(); + using var ms = new MemoryStream(); + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + { + data.SaveTo(ms); + } + ms.Position = 0; + var info = new ImageInfo { Image = ms, AltText = "demo" }; + var handler = new ImageHandler(); + var result = handler.TransformImage(info); + Assert.Equal("demo", result.Attribute(NoNamespace.alt)?.Value); + } + + [Fact] + public void ShouldOmitAltTextWhenNotProvided() + { + using var surface = SKSurface.Create(new SKImageInfo(5, 5)); + surface.Canvas.Clear(SKColors.Blue); + using var image = surface.Snapshot(); + using var ms = new MemoryStream(); + using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) + { + data.SaveTo(ms); + } + ms.Position = 0; + var info = new ImageInfo { Image = ms }; + var handler = new ImageHandler(); + var result = handler.TransformImage(info); + Assert.Null(result.Attribute(NoNamespace.alt)); + } + } +} diff --git a/OpenXmlPowerTools.Tests/MarkupSimplifierTests.cs b/OpenXmlPowerTools.Tests/MarkupSimplifierTests.cs index c5f6bc79..5bc7ab7b 100644 --- a/OpenXmlPowerTools.Tests/MarkupSimplifierTests.cs +++ b/OpenXmlPowerTools.Tests/MarkupSimplifierTests.cs @@ -1,22 +1,19 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; using System.IO; using System.Linq; using System.Xml.Linq; -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Packaging; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OpenXmlPowerTools.Tests +namespace Codeuctivity.Tests { public class MarkupSimplifierTests { private const WordprocessingDocumentType DocumentType = WordprocessingDocumentType.Document; private const string SmartTagDocumentTextValue = "The countries include Algeria, Botswana, and Sri Lanka."; + private const string SmartTagDocumentXmlString = @" @@ -86,56 +83,52 @@ public class MarkupSimplifierTests [Fact] public void CanRemoveSmartTags() { - XDocument partDocument = XDocument.Parse(SmartTagDocumentXmlString); + var partDocument = XDocument.Parse(SmartTagDocumentXmlString); Assert.True(partDocument.Descendants(W.smartTag).Any()); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); - var settings = new SimplifyMarkupSettings { RemoveSmartTags = true }; - MarkupSimplifier.SimplifyMarkup(wordDocument, settings); + var settings = new SimplifyMarkupSettings { RemoveSmartTags = true }; + MarkupSimplifier.SimplifyMarkup(wordDocument, settings); - partDocument = part.GetXDocument(); - XElement t = partDocument.Descendants(W.t).First(); + partDocument = part.GetXDocument(); + var t = partDocument.Descendants(W.t).First(); - Assert.False(partDocument.Descendants(W.smartTag).Any()); - Assert.Equal(SmartTagDocumentTextValue, t.Value); - } + Assert.False(partDocument.Descendants(W.smartTag).Any()); + Assert.Equal(SmartTagDocumentTextValue, t.Value); } [Fact] public void CanRemoveContentControls() { - XDocument partDocument = XDocument.Parse(SdtDocumentXmlString); + var partDocument = XDocument.Parse(SdtDocumentXmlString); Assert.True(partDocument.Descendants(W.sdt).Any()); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); - var settings = new SimplifyMarkupSettings { RemoveContentControls = true }; - MarkupSimplifier.SimplifyMarkup(wordDocument, settings); + var settings = new SimplifyMarkupSettings { RemoveContentControls = true }; + MarkupSimplifier.SimplifyMarkup(wordDocument, settings); - partDocument = part.GetXDocument(); - XElement element = partDocument - .Descendants(W.body) - .Descendants() - .First(); + partDocument = part.GetXDocument(); + var element = partDocument + .Descendants(W.body) + .Descendants() + .First(); - Assert.False(partDocument.Descendants(W.sdt).Any()); - Assert.Equal(W.p, element.Name); - } + Assert.False(partDocument.Descendants(W.sdt).Any()); + Assert.Equal(W.p, element.Name); } [Fact] public void CanRemoveGoBackBookmarks() { - XDocument partDocument = XDocument.Parse(GoBackBookmarkDocumentXmlString); + var partDocument = XDocument.Parse(GoBackBookmarkDocumentXmlString); Assert.Contains(partDocument .Descendants(W.bookmarkStart) , e => e.Attribute(W.name).Value == "_GoBack" && e.Attribute(W.id).Value == "0"); @@ -143,21 +136,17 @@ public void CanRemoveGoBackBookmarks() .Descendants(W.bookmarkEnd) , e => e.Attribute(W.id).Value == "0"); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); - var settings = new SimplifyMarkupSettings { RemoveGoBackBookmark = true }; - MarkupSimplifier.SimplifyMarkup(wordDocument, settings); + var settings = new SimplifyMarkupSettings { RemoveGoBackBookmark = true }; + MarkupSimplifier.SimplifyMarkup(wordDocument, settings); - partDocument = part.GetXDocument(); - Assert.False(partDocument.Descendants(W.bookmarkStart).Any()); - Assert.False(partDocument.Descendants(W.bookmarkEnd).Any()); - } + partDocument = part.GetXDocument(); + Assert.False(partDocument.Descendants(W.bookmarkStart).Any()); + Assert.False(partDocument.Descendants(W.bookmarkEnd).Any()); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/MetricsGetterTests.cs b/OpenXmlPowerTools.Tests/MetricsGetterTests.cs index dffcd14f..e450bc23 100644 --- a/OpenXmlPowerTools.Tests/MetricsGetterTests.cs +++ b/OpenXmlPowerTools.Tests/MetricsGetterTests.cs @@ -1,19 +1,9 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; +using Codeuctivity.OpenXmlPowerTools; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Xml.Linq; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class MgTests { @@ -28,10 +18,10 @@ public class MgTests [InlineData("DA006-SelectTestValue-NoData.docx")] public void MG001(string name) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo fi = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var fi = new FileInfo(Path.Combine(sourceDir.FullName, name)); - MetricsGetterSettings settings = new MetricsGetterSettings() + var settings = new MetricsGetterSettings() { IncludeTextInContentControls = false, IncludeXlsxTableCellData = false, @@ -43,23 +33,21 @@ public void MG001(string name) XElement metrics = null; if (Util.IsWordprocessingML(extension)) { - WmlDocument wmlDocument = new WmlDocument(fi.FullName); + var wmlDocument = new WmlDocument(fi.FullName); metrics = MetricsGetter.GetDocxMetrics(wmlDocument, settings); } else if (Util.IsSpreadsheetML(extension)) { - SmlDocument smlDocument = new SmlDocument(fi.FullName); + var smlDocument = new SmlDocument(fi.FullName); metrics = MetricsGetter.GetXlsxMetrics(smlDocument, settings); } else if (Util.IsPresentationML(extension)) { - PmlDocument pmlDocument = new PmlDocument(fi.FullName); + var pmlDocument = new PmlDocument(fi.FullName); metrics = MetricsGetter.GetPptxMetrics(pmlDocument, settings); } Assert.NotNull(metrics); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/AllowedDiffInfo.cs b/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/AllowedDiffInfo.cs new file mode 100644 index 00000000..59411961 --- /dev/null +++ b/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/AllowedDiffInfo.cs @@ -0,0 +1,16 @@ +namespace Codeuctivity.Tests.OpenXMLWordProcessingMLToHtmlConverter +{ + internal class AllowedDiffInfo + { + public bool DiffFileExists; + public string NewDiffImageFileName; + public string[] ExistingDiffImageFilename; + + public AllowedDiffInfo(bool diffFileExists, string newDiffImageFileName, string[] matchingFiles) + { + DiffFileExists = diffFileExists; + NewDiffImageFileName = newDiffImageFileName; + ExistingDiffImageFilename = matchingFiles; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterHandlerTests.cs b/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterHandlerTests.cs new file mode 100644 index 00000000..fa78d8b5 --- /dev/null +++ b/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterHandlerTests.cs @@ -0,0 +1,129 @@ +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using Codeuctivity.SkiaSharpCompare; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Xunit; + +namespace Codeuctivity.Tests.OpenXMLWordProcessingMLToHtmlConverter +{ + public class WmlToHtmlConverterHandlerTests + { + private const string minimalPng = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAMSURBVBhXY0ACDAwAAA4AAXqxuTAAAAAASUVORK5CYII="; + private const string minimalBmp = "Qk1CAAAAAAAAADoAAAAoAAAAAgAAAAIAAAABAAQAAAAAAAAAAAAiLgAAIi4AAAEAAAABAAAAAAAA/wAAAAAAAAAA"; + private const string minimalJpg = "/9j/4AAQSkZJRgABAQEBLAEsAAD/4QBoRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAARAAAATgAAAAAABJPfAAAD6AAEk98AAAPocGFpbnQubmV0IDQuMi4xNAAA/9sAQwACAQEBAQECAQEBAgICAgIEAwICAgIFBAQDBAYFBgYGBQYGBgcJCAYHCQcGBggLCAkKCgoKCgYICwwLCgwJCgoK/9sAQwECAgICAgIFAwMFCgcGBwoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK/8AAEQgAAgACAwEhAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/n/ooA//2Q=="; + + [Fact] + public void ShouldTranslateWithWordprocessingTextDummyHandler() + { + var expected = "someValue"; + Dictionary fontFamily = default!; + var wordprocessingTextDummyHandler = new TextDummyHandler(); + + var actual = wordprocessingTextDummyHandler.TransformText(expected, fontFamily); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData("png", minimalPng)] + [InlineData("bmp", minimalBmp)] + [InlineData("jpeg", minimalJpg)] + public void ShouldTranslateWithDefaultImageHandler(string imageType, string minimalImage) + { + var expectedStart = $""; + var binaryBitmap = Convert.FromBase64String(minimalImage); + + using var expectedImage = new MemoryStream(binaryBitmap); + + var imageInfo = new ImageInfo + { + Image = expectedImage + }; + + var defaultImageHandler = new ImageHandler(); + + var actual = defaultImageHandler.TransformImage(imageInfo).ToString(); + + Assert.StartsWith(expectedStart, actual); + Assert.EndsWith(expectedEnd, actual); + + var actualBase64Part = actual.Substring(expectedStart.Length, actual.Length - expectedEnd.Length - expectedStart.Length); + var binaryActualBitmap = Convert.FromBase64String(actualBase64Part); + using var actualImage = new MemoryStream(binaryActualBitmap); + + expectedImage.Position = 0; + actualImage.Position = 0; + + Assert.True(Compare.ImagesAreEqual(expectedImage, actualImage, transparencyOptions: TransparencyOptions.CompareAlphaChannel)); + } + + [Fact] + public void ShouldTranslateSymbolsToUnicodeWithDefaultSymbolHandler() + { + Dictionary fontFamily = default!; + var defaultSymbolHandler = new SymbolHandler(); + + var element = new XElement("symbol", new XAttribute(W._char, "A")); + + var actual = defaultSymbolHandler.TransformSymbol(element, fontFamily); + + Assert.Equal(" ", actual.ToString()); + } + + [Fact] + public void ShouldTranslatePageBreaksWithBreakHandler() + { + var breakHandler = new BreakHandler(); + + var element = new XElement("br"); + + var actual = breakHandler.TransformBreak(element); + + Assert.Equal(3, actual.Count()); + Assert.Equal("
", actual.ElementAt(0).ToString()); + Assert.Equal("‎", actual.ElementAt(1).ToString()); + Assert.Null(actual.ElementAt(2)); + } + + [Fact] + public void ShouldTranslateFontInRunSymbolWithFontHandler() + { + var fontHandler = new FontHandler(); + + var element = new XElement("run", new XElement(W.sym, new XAttribute(W.font, "SomeSymbolFont")), new XAttribute(PtOpenXml.FontName, "SomeRunFont")); + + var actual = fontHandler.TranslateRunStyleFont(element); + + Assert.Equal("SomeSymbolFont", actual); + } + + [Fact] + public void ShouldTranslateFontInRunWithFontHandler() + { + var fontHandler = new FontHandler(); + + var element = new XElement("run", new XAttribute(PtOpenXml.FontName, "SomeRunFont")); + + var actual = fontHandler.TranslateRunStyleFont(element); + + Assert.Equal("SomeRunFont", actual); + } + + [Fact] + public void ShouldTranslateFontInParagraphWithFontHandler() + { + var fontHandler = new FontHandler(); + + var element = new XElement("run", new XAttribute(PtOpenXml.FontName, "SomeRunFont")); + + var actual = fontHandler.TranslateParagraphStyleFont(element); + + Assert.Equal("SomeRunFont", actual); + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterTests.cs b/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterTests.cs new file mode 100644 index 00000000..3329b53c --- /dev/null +++ b/OpenXmlPowerTools.Tests/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterTests.cs @@ -0,0 +1,293 @@ +using Codeuctivity.HtmlRenderer; +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using Codeuctivity.SkiaSharpCompare; +using DocumentFormat.OpenXml.Packaging; +using PuppeteerSharp; +using SkiaSharp; +using System; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Xunit; + +namespace Codeuctivity.Tests.OpenXMLWordProcessingMLToHtmlConverter +{ + public class WmlToHtmlConverterTests + { + // PowerShell one liner that generates InlineData for all files in a directory + // dir | % { '[InlineData("' + $_.Name + '")]' } | clip + + [Theory] + [InlineData("HC001-5DayTourPlanTemplate.docx", 0, false)] + [InlineData("HC002-Hebrew-01.docx", 0, true)] + [InlineData("HC003-Hebrew-02.docx", 0, true)] + [InlineData("HC004-ResumeTemplate.docx", 0, false)] + [InlineData("HC005-TaskPlanTemplate.docx", 0, false)] + [InlineData("HC006-Test-01.docx", 0, true)] + [InlineData("HC007-Test-02.docx", 0, true)] + [InlineData("HC008-Test-03.docx", 0, false)] + [InlineData("HC009-Test-04.docx", 0, true)] + [InlineData("HC010-Test-05.docx", 0, true)] + [InlineData("HC011-Test-06.docx", 0, false)] + [InlineData("HC012-Test-07.docx", 0, true)] + [InlineData("HC013-Test-08.docx", 0, false)] + [InlineData("HC014-RTL-Table-01.docx", 0, true)] + [InlineData("HC015-Vertical-Spacing-atLeast.docx", 0, false)] + [InlineData("HC016-Horizontal-Spacing-firstLine.docx", 0, false)] + [InlineData("HC017-Vertical-Alignment-Cell-01.docx", 0, false)] + [InlineData("HC018-Vertical-Alignment-Para-01.docx", 0, false)] + [InlineData("HC019-Hidden-Run.docx", 0, false)] + [InlineData("HC020-Small-Caps.docx", 0, false)] + [InlineData("HC021-Symbols.docx", 0, false)] + [InlineData("HC022-Table-Of-Contents.docx", 0, false)] + [InlineData("HC023-Hyperlink.docx", 0, false)] + [InlineData("HC024-Tabs-01.docx", 0, false)] + [InlineData("HC025-Tabs-02.docx", 0, false)] + [InlineData("HC026-Tabs-03.docx", 0, false)] + [InlineData("HC027-Tabs-04.docx", 0, false)] + [InlineData("HC028-No-Break-Hyphen.docx", 0, false)] + [InlineData("HC029-Table-Merged-Cells.docx", 0, false)] + [InlineData("HC030-Content-Controls.docx", 0, false)] + [InlineData("HC031-Complicated-Document.docx", 0, true)] + [InlineData("HC032-Named-Color.docx", 0, false)] + [InlineData("HC033-Run-With-Border.docx", 0, false)] + [InlineData("HC034-Run-With-Position.docx", 0, false)] + [InlineData("HC035-Strike-Through.docx", 0, false)] + [InlineData("HC036-Super-Script.docx", 0, false)] + [InlineData("HC037-Sub-Script.docx", 0, false)] + [InlineData("HC038-Conflicting-Border-Weight.docx", 0, false)] + [InlineData("HC039-Bold.docx", 0, false)] + [InlineData("HC040-Hyperlink-Fieldcode-01.docx", 0, false)] + [InlineData("HC041-Hyperlink-Fieldcode-02.docx", 0, false)] + [InlineData("HC042-Image-Png.docx", 0, false)] + [InlineData("HC043-Chart.docx", 0, false)] + [InlineData("HC044-Embedded-Workbook.docx", 0, false)] + [InlineData("HC045-Italic.docx", 0, false)] + [InlineData("HC046-BoldAndItalic.docx", 0, false)] + [InlineData("HC047-No-Section.docx", 0, false)] + [InlineData("HC048-Excerpt.docx", 0, true)] + [InlineData("HC049-Borders.docx", 0, false)] + [InlineData("HC050-Shaded-Text-01.docx", 0, false)] + [InlineData("HC051-Shaded-Text-02.docx", 0, false)] + [InlineData("HC052-SmartArt.docx", 0, false)] + [InlineData("HC053-Headings.docx", 0, false)] + [InlineData("HC055-GoogleDocsExport.docx", 0, false)] + [InlineData("HC060-Image-with-Hyperlink.docx", 0, false)] + [InlineData("HC061-Hyperlink-in-Field.docx", 0, false)] + [InlineData("Tabs.docx", 0, false)] + public async Task HC001(string name, int expectedPixelNoise, bool imageSizeMayDiffer) + { + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var settings = new WmlToHtmlConverterSettings(sourceDocx.FullName); + + var oxPtConvertedDestHtml = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-3-OxPt.html"))); + await ConvertToHtml(sourceDocx, oxPtConvertedDestHtml, settings, expectedPixelNoise, imageSizeMayDiffer); + } + + [Theory] + [InlineData("HC006-Test-01.docx", 0)] + public async Task HC002_NoCssClasses(string name, int expectedPixelNoise) + { + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var settings = new WmlToHtmlConverterSettings(sourceDocx.FullName, new ImageHandler(), new TextDummyHandler(), new SymbolHandler(), new BreakHandler(), new FontHandler(), false); + + var oxPtConvertedDestHtml = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-5-OxPt-No-CSS-Classes.html"))); + await ConvertToHtml(sourceDocx, oxPtConvertedDestHtml, settings, expectedPixelNoise, true); + } + + [Theory] + [InlineData("HC023-Hyperlink.docx", "href=\"http://example.com/#anchor\"")] + [InlineData("HC003-Hebrew-02.docx", "
    allowedPixelErrorCount; + + if (!pixelErrorCountAboveExpectedWithDiff) + { + return; + } + resultWithAllowedDiffPixelErrorCount = Math.Min(resultWithAllowedDiffPixelErrorCount, resultWithAllowedDiff.PixelErrorCount); + } + + SaveToGithubActionsPickupTestresultsDirectory(actualFullPath, expectFullPath, allowedDiffInfo.NewDiffImageFileName); + Assert.Fail($"Expected PixelErrorCount beyond {allowedPixelErrorCount} but was {resultWithAllowedDiffPixelErrorCount}\nExpected {expectFullPath}\ndiffers to actual {actualFullPath}\n diff is {allowedDiffInfo.NewDiffImageFileName}\n"); + } + + var result = Compare.CalcDiff(actualFullPath, expectFullPath, resizeOption, transparencyOptions: TransparencyOptions.CompareAlphaChannel); + + var pixelErrorCountAboveExpected = result.PixelErrorCount > allowedPixelErrorCount; + if (pixelErrorCountAboveExpected) + { + SaveToGithubActionsPickupTestresultsDirectory(actualFullPath, expectFullPath, allowedDiffInfo.NewDiffImageFileName); + + Assert.Fail($"Expected PixelErrorCount less or equal {allowedPixelErrorCount} but was {result.PixelErrorCount}\nExpected {expectFullPath}\ndiffers to actual {actualFullPath}\n Diff is {allowedDiffInfo.NewDiffImageFileName} \nReplace {actualFullPath} with the new value or store the diff as {allowedDiffInfo.ExistingDiffImageFilename.First()}."); + } + } + catch (System.Exception ex) when (!(ex is Xunit.Sdk.FailException)) + { + SaveToGithubActionsPickupTestresultsDirectory(actualFullPath, expectFullPath, allowedDiffInfo.NewDiffImageFileName); + } + } + + private static AllowedDiffInfo CalcAllowedDiffInfo(string actualFullPath, string expectFullPath, int allowedPixelErrorCount, bool imageSizeMayDiffer) + { + var osSpecificDiffFileSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : "win"; + var newDiffImageFileName = $"{Path.GetFileName(expectFullPath)}.diff.{osSpecificDiffFileSuffix}{DateTime.Now:yyyyMMddHHmmss}.png"; + + var directoryPath = Path.GetDirectoryName(expectFullPath); + var fileNamePattern = $"{Path.GetFileName(expectFullPath)}.diff.{osSpecificDiffFileSuffix}*.png"; + var matchingFiles = Directory.GetFiles(directoryPath, fileNamePattern); + + return new AllowedDiffInfo(matchingFiles.Length > 0, newDiffImageFileName, matchingFiles); + } + + private static void SaveToGithubActionsPickupTestresultsDirectory(string actualFullPath, string expectFullPath, string newDiffImageFileName) + { + var fileName = Path.GetFileName(actualFullPath); + var expectFullDirectory = Path.GetDirectoryName(expectFullPath); + var expectFullDirectoryFullPath = Path.GetFullPath(expectFullDirectory); + + var testResultDirectoryActual = Path.Combine(expectFullDirectoryFullPath, "../TestResult/Actual"); + var testResultDirectoryExpected = Path.Combine(expectFullDirectoryFullPath, "../TestResult/Expected"); + var testResultDirectoryDiff = Path.Combine(expectFullDirectoryFullPath, "../TestResult/Diff"); + CreateDirectory(Path.Combine(expectFullDirectoryFullPath, "../TestResult")); + CreateDirectory(testResultDirectoryActual); + CreateDirectory(testResultDirectoryExpected); + CreateDirectory(testResultDirectoryDiff); + + File.Copy(actualFullPath, Path.Combine(testResultDirectoryActual, fileName), true); + File.Copy(expectFullPath, Path.Combine(testResultDirectoryExpected, fileName), true); + var newDiffImageFullPath = Path.GetFullPath(newDiffImageFileName); + var destFileName = Path.Combine(testResultDirectoryDiff, Path.GetFileName(newDiffImageFullPath)); + File.Copy(newDiffImageFullPath, destFileName, true); + + static void CreateDirectory(string testResultDirectory) + { + if (!Directory.Exists(testResultDirectory)) + { + Directory.CreateDirectory(testResultDirectory); + } + } + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/OpenXmlPowerTools.Tests.csproj b/OpenXmlPowerTools.Tests/OpenXmlPowerTools.Tests.csproj index 6bb94044..9b8f7ac4 100644 --- a/OpenXmlPowerTools.Tests/OpenXmlPowerTools.Tests.csproj +++ b/OpenXmlPowerTools.Tests/OpenXmlPowerTools.Tests.csproj @@ -1,30 +1,31 @@  - net452;net461;netcoreapp2.0 - true - true - true + 8.0 + net8.0 + true - - - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - - - - - - diff --git a/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs b/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs index 1921db11..334277b4 100644 --- a/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs +++ b/OpenXmlPowerTools.Tests/OpenXmlRegexTests.cs @@ -1,18 +1,13 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml.Linq; -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Packaging; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OpenXmlPowerTools.Tests +namespace Codeuctivity.Tests { public class OpenXmlRegexTests { @@ -220,167 +215,155 @@ private static string InnerDelText(XContainer e) [Fact] public void CanReplaceTextWithQuotationMarks() { - XDocument partDocument = XDocument.Parse(QuotationMarksDocumentXmlString); - XElement p = partDocument.Descendants(W.p).First(); - string innerText = InnerText(p); + var partDocument = XDocument.Parse(QuotationMarksDocumentXmlString); + var p = partDocument.Descendants(W.p).First(); + var innerText = InnerText(p); Assert.Equal( "Text can be enclosed in “normal double quotes” and in «double angle quotation marks».", innerText); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); - - IEnumerable content = partDocument.Descendants(W.p); - var regex = new Regex(string.Format("{0}(?{1}){2}", LeftDoubleQuotationMarks, Words, - RightDoubleQuotationMarks)); - int count = OpenXmlRegex.Replace(content, regex, "‘changed ${words}’", null); - - p = partDocument.Descendants(W.p).First(); - innerText = InnerText(p); - - Assert.Equal(2, count); - Assert.Equal( - "Text can be enclosed in ‘changed normal double quotes’ and in ‘changed double angle quotation marks’.", - innerText); - } + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); + + var content = partDocument.Descendants(W.p); + var regex = new Regex(string.Format("{0}(?{1}){2}", LeftDoubleQuotationMarks, Words, + RightDoubleQuotationMarks)); + var count = OpenXmlRegex.Replace(content, regex, "‘changed ${words}’", null); + + p = partDocument.Descendants(W.p).First(); + innerText = InnerText(p); + + Assert.Equal(2, count); + Assert.Equal( + "Text can be enclosed in ‘changed normal double quotes’ and in ‘changed double angle quotation marks’.", + innerText); } [Fact] public void CanReplaceTextWithQuotationMarksAndAddTrackedChangesWhenReplacing() { - XDocument partDocument = XDocument.Parse(QuotationMarksDocumentXmlString); - XElement p = partDocument.Descendants(W.p).First(); - string innerText = InnerText(p); + var partDocument = XDocument.Parse(QuotationMarksDocumentXmlString); + var p = partDocument.Descendants(W.p).First(); + var innerText = InnerText(p); Assert.Equal( "Text can be enclosed in “normal double quotes” and in «double angle quotation marks».", innerText); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); - IEnumerable content = partDocument.Descendants(W.p); - var regex = new Regex(string.Format("{0}(?{1}){2}", LeftDoubleQuotationMarks, Words, - RightDoubleQuotationMarks)); - int count = OpenXmlRegex.Replace(content, regex, "‘changed ${words}’", null, true, "John Doe"); + var content = partDocument.Descendants(W.p); + var regex = new Regex(string.Format("{0}(?{1}){2}", LeftDoubleQuotationMarks, Words, + RightDoubleQuotationMarks)); + var count = OpenXmlRegex.Replace(content, regex, "‘changed ${words}’", null, true, "John Doe"); - p = partDocument.Descendants(W.p).First(); - innerText = InnerText(p); + p = partDocument.Descendants(W.p).First(); + innerText = InnerText(p); - Assert.Equal(2, count); - Assert.Equal( - "Text can be enclosed in ‘changed normal double quotes’ and in ‘changed double angle quotation marks’.", - innerText); + Assert.Equal(2, count); + Assert.Equal( + "Text can be enclosed in ‘changed normal double quotes’ and in ‘changed double angle quotation marks’.", + innerText); - Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed normal double quotes’"); - Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed double angle quotation marks’"); + Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed normal double quotes’"); + Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed double angle quotation marks’"); - Assert.Contains(p.Elements(W.del), e => InnerDelText(e) == "“normal double quotes”"); - Assert.Contains(p.Elements(W.del), e => InnerDelText(e) == "«double angle quotation marks»"); - } + Assert.Contains(p.Elements(W.del), e => InnerDelText(e) == "“normal double quotes”"); + Assert.Contains(p.Elements(W.del), e => InnerDelText(e) == "«double angle quotation marks»"); } [Fact] public void CanReplaceTextWithQuotationMarksAndTrackedChanges() { - XDocument partDocument = XDocument.Parse(QuotationMarksAndTrackedChangesDocumentXmlString); - XElement p = partDocument.Descendants(W.p).First(); - string innerText = InnerText(p); + var partDocument = XDocument.Parse(QuotationMarksAndTrackedChangesDocumentXmlString); + var p = partDocument.Descendants(W.p).First(); + var innerText = InnerText(p); Assert.Equal( "Text can be enclosed in “normal double quotes” and in «double angle quotation marks».", innerText); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); - IEnumerable content = partDocument.Descendants(W.p); - var regex = new Regex(string.Format("{0}(?{1}){2}", LeftDoubleQuotationMarks, Words, - RightDoubleQuotationMarks)); - int count = OpenXmlRegex.Replace(content, regex, "‘changed ${words}’", null, true, "John Doe"); + var content = partDocument.Descendants(W.p); + var regex = new Regex(string.Format("{0}(?{1}){2}", LeftDoubleQuotationMarks, Words, + RightDoubleQuotationMarks)); + var count = OpenXmlRegex.Replace(content, regex, "‘changed ${words}’", null, true, "John Doe"); - p = partDocument.Descendants(W.p).First(); - innerText = InnerText(p); + p = partDocument.Descendants(W.p).First(); + innerText = InnerText(p); - Assert.Equal(2, count); - Assert.Equal( - "Text can be enclosed in ‘changed normal double quotes’ and in ‘changed double angle quotation marks’.", - innerText); + Assert.Equal(2, count); + Assert.Equal( + "Text can be enclosed in ‘changed normal double quotes’ and in ‘changed double angle quotation marks’.", + innerText); - Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed normal double quotes’"); - Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed double angle quotation marks’"); - } + Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed normal double quotes’"); + Assert.Contains(p.Elements(W.ins), e => InnerText(e) == "‘changed double angle quotation marks’"); } [Fact] public void CanReplaceTextWithSymbolsAndTrackedChanges() { - XDocument partDocument = XDocument.Parse(SymbolsAndTrackedChangesDocumentXmlString); - XElement p = partDocument.Descendants(W.p).First(); - string innerText = InnerText(p); + var partDocument = XDocument.Parse(SymbolsAndTrackedChangesDocumentXmlString); + var p = partDocument.Descendants(W.p).First(); + var innerText = InnerText(p); Assert.Equal("We can also use symbols such as \uF021 or \uF028.", innerText); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); - IEnumerable content = partDocument.Descendants(W.p); - var regex = new Regex(@"[\uF021]"); - int count = OpenXmlRegex.Replace(content, regex, "\uF028", null, true, "John Doe"); + var content = partDocument.Descendants(W.p); + var regex = new Regex(@"[\uF021]"); + var count = OpenXmlRegex.Replace(content, regex, "\uF028", null, true, "John Doe"); - p = partDocument.Descendants(W.p).First(); - innerText = InnerText(p); + p = partDocument.Descendants(W.p).First(); + innerText = InnerText(p); - Assert.Equal(1, count); - Assert.Equal("We can also use symbols such as \uF028 or \uF028.", innerText); + Assert.Equal(1, count); + Assert.Equal("We can also use symbols such as \uF028 or \uF028.", innerText); - Assert.Contains(p.Descendants(W.ins), ins => ins.Descendants(W.sym).Any( - sym => sym.Attribute(W.font).Value == "Wingdings" && - sym.Attribute(W._char).Value == "F028")); - } + Assert.Contains(p.Descendants(W.ins), ins => ins.Descendants(W.sym).Any( + sym => sym.Attribute(W.font).Value == "Wingdings" && + sym.Attribute(W._char).Value == "F028")); } [Fact] public void CanReplaceTextWithFields() { - XDocument partDocument = XDocument.Parse(FieldsDocumentXmlString); - XElement p = partDocument.Descendants(W.p).Last(); - string innerText = InnerText(p); + var partDocument = XDocument.Parse(FieldsDocumentXmlString); + var p = partDocument.Descendants(W.p).Last(); + var innerText = InnerText(p); Assert.Equal("As stated in Article {__1} and this Section {__1.1}, this is described in Schedule C (Performance Framework).", innerText); - using (var stream = new MemoryStream()) - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.PutXDocument(partDocument); + using var stream = new MemoryStream(); + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.PutXDocument(partDocument); - IEnumerable content = partDocument.Descendants(W.p); - var regex = new Regex(@"Schedule C \(Performance Framework\)"); - int count = OpenXmlRegex.Replace(content, regex, "Exhibit 4", null, true, "John Doe"); + var content = partDocument.Descendants(W.p); + var regex = new Regex(@"Schedule C \(Performance Framework\)"); + var count = OpenXmlRegex.Replace(content, regex, "Exhibit 4", null, true, "John Doe"); - p = partDocument.Descendants(W.p).Last(); - innerText = InnerText(p); + p = partDocument.Descendants(W.p).Last(); + innerText = InnerText(p); - Assert.Equal(1, count); - Assert.Equal("As stated in Article {__1} and this Section {__1.1}, this is described in Exhibit 4.", innerText); - } + Assert.Equal(1, count); + Assert.Equal("As stated in Article {__1} and this Section {__1.1}, this is described in Exhibit 4.", innerText); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/PowerToolsBlockExtensionsTests.cs b/OpenXmlPowerTools.Tests/PowerToolsBlockExtensionsTests.cs index fccb6d73..b107aafe 100644 --- a/OpenXmlPowerTools.Tests/PowerToolsBlockExtensionsTests.cs +++ b/OpenXmlPowerTools.Tests/PowerToolsBlockExtensionsTests.cs @@ -1,136 +1,121 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; using System.IO; using System.Linq; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OpenXmlPowerTools.Tests +namespace Codeuctivity.Tests { public class PowerToolsBlockExtensionsTests : TestsBase { [Fact] public void MustBeginPowerToolsBlockToUsePowerTools() { - using (var stream = new MemoryStream()) - { - CreateEmptyWordprocessingDocument(stream); - - using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) - { - MainDocumentPart part = wordDocument.MainDocumentPart; - - // Add a first paragraph through the SDK. - Body body = part.Document.Body; - body.AppendChild(new Paragraph(new Run(new Text("First")))); - - // This demonstrates the usage of the BeginPowerToolsBlock method to - // demarcate blocks or regions of code where the PowerTools are used - // in between usages of the strongly typed classes. - wordDocument.BeginPowerToolsBlock(); - - // Get content through the PowerTools. We will see the one paragraph added - // by using the strongly typed SDK classes. - XDocument content = part.GetXDocument(); - List paragraphElements = content.Descendants(W.p).ToList(); - Assert.Single(paragraphElements); - Assert.Equal("First", paragraphElements[0].Value); - - // This demonstrates the usage of the EndPowerToolsBlock method to - // demarcate blocks or regions of code where the PowerTools are used - // in between usages of the strongly typed classes. - wordDocument.EndPowerToolsBlock(); - - // Add a second paragraph through the SDK in the exact same way as above. - body = part.Document.Body; - body.AppendChild(new Paragraph(new Run(new Text("Second")))); - part.Document.Save(); - - // Get content through the PowerTools in the exact same way as above, - // noting that we have not used the BeginPowerToolsBlock method to - // mark the beginning of the next PowerTools Block. - // What we will see in this case is that we still only get the first - // paragraph. This is caused by the GetXDocument method using the cached - // XDocument, i.e., the annotation, rather reading the part's stream again. - content = part.GetXDocument(); - paragraphElements = content.Descendants(W.p).ToList(); - Assert.Single(paragraphElements); - Assert.Equal("First", paragraphElements[0].Value); - - // To make the GetXDocument read the parts' streams, we need to begin - // the next PowerTools Block. This will remove the annotations from the - // parts and make the PowerTools read the part's stream instead of - // using the outdated annotation. - wordDocument.BeginPowerToolsBlock(); - - // Get content through the PowerTools in the exact same way as above. - // We should now see both paragraphs. - content = part.GetXDocument(); - paragraphElements = content.Descendants(W.p).ToList(); - Assert.Equal(2, paragraphElements.Count); - Assert.Equal("First", paragraphElements[0].Value); - Assert.Equal("Second", paragraphElements[1].Value); - } - } + using var stream = new MemoryStream(); + CreateEmptyWordprocessingDocument(stream); + + using var wordDocument = WordprocessingDocument.Open(stream, true); + var part = wordDocument.MainDocumentPart; + + // Add a first paragraph through the SDK. + var body = part.Document.Body; + body.AppendChild(new Paragraph(new Run(new Text("First")))); + + // This demonstrates the usage of the BeginPowerToolsBlock method to + // demarcate blocks or regions of code where the PowerTools are used + // in between usages of the strongly typed classes. + wordDocument.BeginPowerToolsBlock(); + + // Get content through the PowerTools. We will see the one paragraph added + // by using the strongly typed SDK classes. + var content = part.GetXDocument(); + var paragraphElements = content.Descendants(W.p).ToList(); + Assert.Single(paragraphElements); + Assert.Equal("First", paragraphElements[0].Value); + + // This demonstrates the usage of the EndPowerToolsBlock method to + // demarcate blocks or regions of code where the PowerTools are used + // in between usages of the strongly typed classes. + wordDocument.EndPowerToolsBlock(); + + // Add a second paragraph through the SDK in the exact same way as above. + body = part.Document.Body; + body.AppendChild(new Paragraph(new Run(new Text("Second")))); + part.Document.Save(); + + // Get content through the PowerTools in the exact same way as above, + // noting that we have not used the BeginPowerToolsBlock method to + // mark the beginning of the next PowerTools Block. + // What we will see in this case is that we still only get the first + // paragraph. This is caused by the GetXDocument method using the cached + // XDocument, i.e., the annotation, rather reading the part's stream again. + content = part.GetXDocument(); + paragraphElements = content.Descendants(W.p).ToList(); + Assert.Single(paragraphElements); + Assert.Equal("First", paragraphElements[0].Value); + + // To make the GetXDocument read the parts' streams, we need to begin + // the next PowerTools Block. This will remove the annotations from the + // parts and make the PowerTools read the part's stream instead of + // using the outdated annotation. + wordDocument.BeginPowerToolsBlock(); + + // Get content through the PowerTools in the exact same way as above. + // We should now see both paragraphs. + content = part.GetXDocument(); + paragraphElements = content.Descendants(W.p).ToList(); + Assert.Equal(2, paragraphElements.Count); + Assert.Equal("First", paragraphElements[0].Value); + Assert.Equal("Second", paragraphElements[1].Value); } [Fact] public void MustEndPowerToolsBlockToUseStronglyTypedClasses() { - using (var stream = new MemoryStream()) - { - CreateEmptyWordprocessingDocument(stream); - - using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) - { - MainDocumentPart part = wordDocument.MainDocumentPart; - - // Add a paragraph through the SDK. - Body body = part.Document.Body; - body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); - - // Begin the PowerTools Block, which saves any changes made through the strongly - // typed SDK classes to the parts of the WordprocessingDocument. - // In this case, this could also be done by invoking the Save method on the - // WordprocessingDocument, which will save all parts that had changes, or by - // invoking part.RootElement.Save() for the one part that was changed. - wordDocument.BeginPowerToolsBlock(); - - // Add a paragraph through the PowerTools. - XDocument content = part.GetXDocument(); - XElement bodyElement = content.Descendants(W.body).First(); - bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); - part.PutXDocument(); - - // Get the part's content through the SDK. However, we will only see what we - // added through the SDK, not what we added through the PowerTools functionality. - body = part.Document.Body; - List paragraphs = body.Elements().ToList(); - Assert.Single(paragraphs); - Assert.Equal("Added through SDK", paragraphs[0].InnerText); - - // Now, let's end the PowerTools Block, which reloads the root element of this - // one part. Reloading those root elements this way is fine if you know exactly - // which parts had their content changed by the Open XML PowerTools. - wordDocument.EndPowerToolsBlock(); - - // Get the part's content through the SDK. Having reloaded the root element, - // we should now see both paragraphs. - body = part.Document.Body; - paragraphs = body.Elements().ToList(); - Assert.Equal(2, paragraphs.Count); - Assert.Equal("Added through SDK", paragraphs[0].InnerText); - Assert.Equal("Added through PowerTools", paragraphs[1].InnerText); - } - } + using var stream = new MemoryStream(); + CreateEmptyWordprocessingDocument(stream); + + using var wordDocument = WordprocessingDocument.Open(stream, true); + var part = wordDocument.MainDocumentPart; + + // Add a paragraph through the SDK. + var body = part.Document.Body; + body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); + + // Begin the PowerTools Block, which saves any changes made through the strongly + // typed SDK classes to the parts of the WordprocessingDocument. + // In this case, this could also be done by invoking the Save method on the + // WordprocessingDocument, which will save all parts that had changes, or by + // invoking part.RootElement.Save() for the one part that was changed. + wordDocument.BeginPowerToolsBlock(); + + // Add a paragraph through the PowerTools. + var content = part.GetXDocument(); + var bodyElement = content.Descendants(W.body).First(); + bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); + part.PutXDocument(); + + // Get the part's content through the SDK. However, we will only see what we + // added through the SDK, not what we added through the PowerTools functionality. + body = part.Document.Body; + var paragraphs = body.Elements().ToList(); + Assert.Single(paragraphs); + Assert.Equal("Added through SDK", paragraphs[0].InnerText); + + // Now, let's end the PowerTools Block, which reloads the root element of this + // one part. Reloading those root elements this way is fine if you know exactly + // which parts had their content changed by the Open XML PowerTools. + wordDocument.EndPowerToolsBlock(); + + // Get the part's content through the SDK. Having reloaded the root element, + // we should now see both paragraphs. + body = part.Document.Body; + paragraphs = body.Elements().ToList(); + Assert.Equal(2, paragraphs.Count); + Assert.Equal("Added through SDK", paragraphs[0].InnerText); + Assert.Equal("Added through PowerTools", paragraphs[1].InnerText); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/PowerToolsBlockTests.cs b/OpenXmlPowerTools.Tests/PowerToolsBlockTests.cs index 12b4a89d..955bf41a 100644 --- a/OpenXmlPowerTools.Tests/PowerToolsBlockTests.cs +++ b/OpenXmlPowerTools.Tests/PowerToolsBlockTests.cs @@ -1,61 +1,52 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OpenXmlPowerTools.Tests +namespace Codeuctivity.Tests { public class PowerToolsBlockTests : TestsBase { [Fact] public void CanUsePowerToolsBlockToDemarcateApis() { - using (var stream = new MemoryStream()) - { - CreateEmptyWordprocessingDocument(stream); - - using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) - { - MainDocumentPart part = wordDocument.MainDocumentPart; - - // Add a paragraph through the SDK. - Body body = part.Document.Body; - body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); + using var stream = new MemoryStream(); + CreateEmptyWordprocessingDocument(stream); - // This demonstrates the use of the PowerToolsBlock in a using statement to - // demarcate the intermittent use of the PowerTools. - using (new PowerToolsBlock(wordDocument)) - { - // Assert that we can see the paragraph added through the strongly typed classes. - XDocument content = part.GetXDocument(); - List paragraphElements = content.Descendants(W.p).ToList(); - Assert.Single(paragraphElements); - Assert.Equal("Added through SDK", paragraphElements[0].Value); + using var wordDocument = WordprocessingDocument.Open(stream, true); + var part = wordDocument.MainDocumentPart; - // Add a paragraph through the PowerTools. - XElement bodyElement = content.Descendants(W.body).First(); - bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); - part.PutXDocument(); - } + // Add a paragraph through the SDK. + var body = part.Document.Body; + body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); - // Get the part's content through the SDK. Having used the PowerToolsBlock, - // we should see both paragraphs. - body = part.Document.Body; - List paragraphs = body.Elements().ToList(); - Assert.Equal(2, paragraphs.Count); - Assert.Equal("Added through SDK", paragraphs[0].InnerText); - Assert.Equal("Added through PowerTools", paragraphs[1].InnerText); - } + // This demonstrates the use of the PowerToolsBlock in a using statement to + // demarcate the intermittent use of the PowerTools. + using (new PowerToolsBlock(wordDocument)) + { + // Assert that we can see the paragraph added through the strongly typed classes. + var content = part.GetXDocument(); + var paragraphElements = content.Descendants(W.p).ToList(); + Assert.Single(paragraphElements); + Assert.Equal("Added through SDK", paragraphElements[0].Value); + + // Add a paragraph through the PowerTools. + var bodyElement = content.Descendants(W.body).First(); + bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); + part.PutXDocument(); } + + // Get the part's content through the SDK. Having used the PowerToolsBlock, + // we should see both paragraphs. + body = part.Document.Body; + var paragraphs = body.Elements().ToList(); + Assert.Equal(2, paragraphs.Count); + Assert.Equal("Added through SDK", paragraphs[0].InnerText); + Assert.Equal("Added through PowerTools", paragraphs[1].InnerText); } [Fact] @@ -64,6 +55,4 @@ public void ConstructorThrowsWhenPassingNull() Assert.Throws(() => new PowerToolsBlock(null)); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/PresentationBuilderTests.cs b/OpenXmlPowerTools.Tests/PresentationBuilderTests.cs deleted file mode 100644 index 95a0dc6b..00000000 --- a/OpenXmlPowerTools.Tests/PresentationBuilderTests.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; -using Xunit; - -#if !ELIDE_XUNIT_TESTS - -namespace OxPt -{ - public class PbTests - { - [Fact] - public void PB001_Formatting() - { - string name1 = "PB001-Input1.pptx"; - string name2 = "PB001-Input2.pptx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - List sources = null; - sources = new List() - { - new SlideSource(new PmlDocument(source1Pptx.FullName), 1, true), - new SlideSource(new PmlDocument(source2Pptx.FullName), 0, true), - }; - var processedDestPptx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "PB001-Formatting.pptx")); - PresentationBuilder.BuildPresentation(sources, processedDestPptx.FullName); - } - - [Fact] - public void PB002_Formatting() - { - string name2 = "PB001-Input2.pptx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source2Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - List sources = null; - sources = new List() - { - new SlideSource(new PmlDocument(source2Pptx.FullName), 0, true), - }; - var processedDestPptx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "PB002-Formatting.pptx")); - PresentationBuilder.BuildPresentation(sources, processedDestPptx.FullName); - } - - [Fact] - public void PB003_Formatting() - { - string name1 = "PB001-Input1.pptx"; - string name2 = "PB001-Input3.pptx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - List sources = null; - sources = new List() - { - new SlideSource(new PmlDocument(source1Pptx.FullName), 1, true), - new SlideSource(new PmlDocument(source2Pptx.FullName), 0, true), - }; - var processedDestPptx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "PB003-Formatting.pptx")); - PresentationBuilder.BuildPresentation(sources, processedDestPptx.FullName); - } - - [Fact] - public void PB004_Formatting() - { - string name1 = "PB001-Input1.pptx"; - string name2 = "PB001-Input3.pptx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - List sources = null; - sources = new List() - { - new SlideSource(new PmlDocument(source2Pptx.FullName), 0, true), - new SlideSource(new PmlDocument(source1Pptx.FullName), 1, true), - }; - var processedDestPptx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "PB004-Formatting.pptx")); - PresentationBuilder.BuildPresentation(sources, processedDestPptx.FullName); - } - - [Fact] - public void PB005_Formatting() - { - string name1 = "PB001-Input1.pptx"; - string name2 = "PB001-Input3.pptx"; - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Pptx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - List sources = null; - sources = new List() - { - new SlideSource(new PmlDocument(source2Pptx.FullName), 0, 0, true), - new SlideSource(new PmlDocument(source1Pptx.FullName), 1, true), - new SlideSource(new PmlDocument(source2Pptx.FullName), 0, true), - }; - var processedDestPptx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "PB005-Formatting.pptx")); - PresentationBuilder.BuildPresentation(sources, processedDestPptx.FullName); - } - -#if NETCOREAPP2_0 - [Fact(Skip="Bug in netcore 2.0 : https://github.com/OfficeDev/Open-Xml-PowerTools/pull/238#issuecomment-412375570")] -#else - [Fact] -#endif - public void PB006_VideoFormats() - { - // This presentation contains videos with content types video/mp4, video/quicktime, video/unknown, video/x-ms-asf, and video/x-msvideo. - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourcePptx = new FileInfo(Path.Combine(sourceDir.FullName, "PP006-Videos.pptx")); - - var oldMediaDataContentTypes = GetMediaDataContentTypes(sourcePptx); - - List sources = null; - sources = new List() - { - new SlideSource(new PmlDocument(sourcePptx.FullName), true), - }; - var processedDestPptx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "PB006-Videos.pptx")); - PresentationBuilder.BuildPresentation(sources, processedDestPptx.FullName); - - var newMediaDataContentTypes = GetMediaDataContentTypes(processedDestPptx); - - Assert.Equal(oldMediaDataContentTypes, newMediaDataContentTypes); - } - - private static string[] GetMediaDataContentTypes(FileInfo fi) - { - using (PresentationDocument ptDoc = PresentationDocument.Open(fi.FullName, false)) - { - return ptDoc.PresentationPart.SlideParts.SelectMany( - p => p.DataPartReferenceRelationships.Select(d => d.DataPart.ContentType)) - .Distinct() - .OrderBy(m => m) - .ToArray(); - } - } - } -} - -#endif diff --git a/OpenXmlPowerTools.Tests/PtUtilTests.cs b/OpenXmlPowerTools.Tests/PtUtilTests.cs index 4515e5d9..7b209f93 100644 --- a/OpenXmlPowerTools.Tests/PtUtilTests.cs +++ b/OpenXmlPowerTools.Tests/PtUtilTests.cs @@ -1,22 +1,8 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; +using Codeuctivity.OpenXmlPowerTools; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class PtUtilTests { @@ -24,8 +10,8 @@ public class PtUtilTests [InlineData("PU/PU001-Test001.mht")] public void PU001(string name) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceMht = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceMht = new FileInfo(Path.Combine(sourceDir.FullName, name)); var src = File.ReadAllText(sourceMht.FullName); var p = MhtParser.Parse(src); Assert.True(p.ContentType != null); @@ -33,8 +19,5 @@ public void PU001(string name) Assert.True(p.Parts.Length != 0); Assert.DoesNotContain(p.Parts, part => part.ContentType == null || part.ContentLocation == null); } - } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/RevisionAccepterTests.cs b/OpenXmlPowerTools.Tests/RevisionAccepterTests.cs index 2797e478..c98a3934 100644 --- a/OpenXmlPowerTools.Tests/RevisionAccepterTests.cs +++ b/OpenXmlPowerTools.Tests/RevisionAccepterTests.cs @@ -1,41 +1,23 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; +using Codeuctivity.OpenXmlPowerTools; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class RaTests { [Theory] [InlineData("RA001-Tracked-Revisions-01.docx")] [InlineData("RA001-Tracked-Revisions-02.docx")] - public void RA001(string name) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - WmlDocument notAccepted = new WmlDocument(sourceDocx.FullName); - WmlDocument afterAccepting = RevisionAccepter.AcceptRevisions(notAccepted); + var notAccepted = new WmlDocument(sourceDocx.FullName); + var afterAccepting = RevisionAccepter.AcceptRevisions(notAccepted); var processedDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-processed-by-RevisionAccepter.docx"))); afterAccepting.SaveAs(processedDestDocx.FullName); } - } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/RevisionProcessorTests.cs b/OpenXmlPowerTools.Tests/RevisionProcessorTests.cs index 88730fb2..f4b92234 100644 --- a/OpenXmlPowerTools.Tests/RevisionProcessorTests.cs +++ b/OpenXmlPowerTools.Tests/RevisionProcessorTests.cs @@ -1,34 +1,13 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class RpTests { - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // perf settings - public static bool m_CopySourceFilesToTempDir = true; - public static bool m_OpenTempDirInExplorer = false; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - [Theory] - //[InlineData("RP/RP001-Tracked-Revisions-01.docx")] - //[InlineData("RP/RP001-Tracked-Revisions-02.docx")] [InlineData("RP/RP002-Deleted-Text.docx")] [InlineData("RP/RP003-Inserted-Text.docx")] [InlineData("RP/RP004-Deleted-Text-in-CC.docx")] @@ -81,14 +60,14 @@ public class RpTests [InlineData("RP/RP052-Deleted-Para-Mark.docx")] public void RP001(string name) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); var sourceFi = new FileInfo(Path.Combine(sourceDir.FullName, name)); var baselineAcceptedFi = new FileInfo(Path.Combine(sourceDir.FullName, name.Replace(".docx", "-Accepted.docx"))); var baselineRejectedFi = new FileInfo(Path.Combine(sourceDir.FullName, name.Replace(".docx", "-Rejected.docx"))); - WmlDocument sourceWml = new WmlDocument(sourceFi.FullName); - WmlDocument afterRejectingWml = RevisionProcessor.RejectRevisions(sourceWml); - WmlDocument afterAcceptingWml = RevisionProcessor.AcceptRevisions(sourceWml); + var sourceWml = new WmlDocument(sourceFi.FullName); + var afterRejectingWml = RevisionProcessor.RejectRevisions(sourceWml); + var afterAcceptingWml = RevisionProcessor.AcceptRevisions(sourceWml); var processedAcceptedFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceFi.Name.Replace(".docx", "-Accepted.docx"))); afterAcceptingWml.SaveAs(processedAcceptedFi.FullName); @@ -96,45 +75,24 @@ public void RP001(string name) var processedRejectedFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceFi.Name.Replace(".docx", "-Rejected.docx"))); afterRejectingWml.SaveAs(processedRejectedFi.FullName); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Copy source files to temp dir - if (m_CopySourceFilesToTempDir) - { - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var sourceDocxCopiedToDestFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceFi.Name)); - if (!sourceDocxCopiedToDestFi.Exists) - sourceWml.SaveAs(sourceDocxCopiedToDestFi.FullName); - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // create batch file to copy properly processed documents to the TestFiles directory. while (true) { try { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// var batchFileName = "Copy-Gen-Files-To-TestFiles.bat"; var batchFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, batchFileName)); var batch = ""; batch += "copy " + processedAcceptedFi.FullName + " " + baselineAcceptedFi.FullName + Environment.NewLine; batch += "copy " + processedRejectedFi.FullName + " " + baselineRejectedFi.FullName + Environment.NewLine; if (batchFi.Exists) + { File.AppendAllText(batchFi.FullName, batch); + } else + { File.WriteAllText(batchFi.FullName, batch); - ////////////////////////////////////////////////// + } break; } catch (IOException) @@ -142,69 +100,6 @@ public void RP001(string name) System.Threading.Thread.Sleep(50); } } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Open Windows Explorer - if (m_OpenTempDirInExplorer) - { - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var semaphorFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "z_ExplorerOpenedSemaphore.txt")); - if (!semaphorFi.Exists) - { - File.WriteAllText(semaphorFi.FullName, ""); - TestUtil.Explorer(TestUtil.TempDir); - } - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Use WmlComparer to see if accepted baseline is same as processed - if (baselineAcceptedFi.Exists) - { - var baselineAcceptedWml = new WmlDocument(baselineAcceptedFi.FullName); - WmlComparerSettings wmlComparerSettings = new WmlComparerSettings(); - WmlDocument result = WmlComparer.Compare(baselineAcceptedWml, afterAcceptingWml, wmlComparerSettings); - var revisions = WmlComparer.GetRevisions(result, wmlComparerSettings); - if (revisions.Any()) - { - Assert.True(false, "Regression Error: Accepted baseline document did not match processed document"); - } - } - else - { - Assert.True(false, "No Accepted baseline document"); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Use WmlComparer to see if rejected baseline is same as processed - if (baselineRejectedFi.Exists) - { - var baselineRejectedWml = new WmlDocument(baselineRejectedFi.FullName); - WmlComparerSettings wmlComparerSettings = new WmlComparerSettings(); - WmlDocument result = WmlComparer.Compare(baselineRejectedWml, afterRejectingWml, wmlComparerSettings); - var revisions = WmlComparer.GetRevisions(result, wmlComparerSettings); - if (revisions.Any()) - { - Assert.True(false, "Regression Error: Rejected baseline document did not match processed document"); - } - } - else - { - Assert.True(false, "No Rejected baseline document"); - } } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/SmlCellFormatterTests.cs b/OpenXmlPowerTools.Tests/SmlCellFormatterTests.cs index 0c215625..abc3026a 100644 --- a/OpenXmlPowerTools.Tests/SmlCellFormatterTests.cs +++ b/OpenXmlPowerTools.Tests/SmlCellFormatterTests.cs @@ -1,22 +1,10 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class CfTests { @@ -82,11 +70,9 @@ public class CfTests [InlineData("mm:ss.0", "42344.295445092591", "05:26:456", null)] [InlineData("##0.0E+0", "100.0", "100.0E+0", null)] [InlineData("##0.0E+0", "543.210", "543.2E+0", null)] - public void CF001(string formatCode, string value, string expected, string expectedColor) { - string color; - string r = SmlCellFormatter.FormatCell(formatCode, value, out color); + var r = SmlCellFormatter.FormatCell(formatCode, value, out var color); Assert.Equal(expected, r); Assert.Equal(expectedColor, color); } @@ -116,38 +102,29 @@ public void CF001(string formatCode, string value, string expected, string expec [InlineData("SH151-Custom-Cell-Format-Currency.xlsx", "Sheet1", "H1:H1", "£ 123.45", null)] [InlineData("SH151-Custom-Cell-Format-Currency.xlsx", "Sheet1", "H2:H2", "-£ 123.45", "Red")] [InlineData("SH151-Custom-Cell-Format-Currency.xlsx", "Sheet1", "H3:H3", "£ -", null)] - [InlineData("SH152-Custom-Cell-Format.xlsx", "Sheet1", "A1:A1", "1,234,567.0000", null)] [InlineData("SH152-Custom-Cell-Format.xlsx", "Sheet1", "B1:B1", "This is the value: abc", null)] - [InlineData("SH201-Cell-C1-Without-R-Attr.xlsx", "Sheet1", "C1:C1", "3", null)] [InlineData("SH202-Cell-C1-D1-Without-R-Attr.xlsx", "Sheet1", "C1:C1", "3", null)] [InlineData("SH203-Cell-C1-D1-E1-Without-R-Attr.xlsx", "Sheet1", "C1:C1", "3", null)] [InlineData("SH204-Cell-A1-B1-C1-Without-R-Attr.xlsx", "Sheet1", "A1:A1", "1", null)] - public void CF002(string name, string sheetName, string range, string expected, string expectedColor) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); var sourceCopiedToDestXlsx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", "-1-Source.xlsx"))); if (!sourceCopiedToDestXlsx.Exists) - File.Copy(sourceXlsx.FullName, sourceCopiedToDestXlsx.FullName); - - var dataTemplateFileNameSuffix = string.Format("-2-Generated-XmlData-{0}.xml", range.Replace(":", "")); - var dataXmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", dataTemplateFileNameSuffix))); - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, true)) { - var rangeXml = SmlDataRetriever.RetrieveRange(sDoc, sheetName, range); - string displayValue = (string)rangeXml.Descendants("DisplayValue").FirstOrDefault(); - string displayColor = (string)rangeXml.Descendants("DisplayColor").FirstOrDefault(); - Assert.Equal(expected, displayValue); - Assert.Equal(expectedColor, displayColor); + File.Copy(sourceXlsx.FullName, sourceCopiedToDestXlsx.FullName); } - } - + using var sDoc = SpreadsheetDocument.Open(sourceCopiedToDestXlsx.FullName, true); + var rangeXml = SmlDataRetriever.RetrieveRange(sDoc, sheetName, range); + var displayValue = (string)rangeXml.Descendants("DisplayValue").FirstOrDefault(); + var displayColor = (string)rangeXml.Descendants("DisplayColor").FirstOrDefault(); + Assert.Equal(expected, displayValue); + Assert.Equal(expectedColor, displayColor); + } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/SmlToHtmlConverterTests.cs b/OpenXmlPowerTools.Tests/SmlToHtmlConverterTests.cs index a0ad0393..5cb0b5ea 100644 --- a/OpenXmlPowerTools.Tests/SmlToHtmlConverterTests.cs +++ b/OpenXmlPowerTools.Tests/SmlToHtmlConverterTests.cs @@ -1,22 +1,9 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; +using Codeuctivity.OpenXmlPowerTools; using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; +using System.IO; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class ShTests { @@ -54,24 +41,23 @@ public class ShTests [InlineData("SH129-FmtNumId-20.xlsx", "Sheet1")] [InlineData("SH130-FmtNumId-21.xlsx", "Sheet1")] [InlineData("SH131-FmtNumId-22.xlsx", "Sheet1")] - public void SH005_ConvertSheet(string name, string sheetName) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); var sourceCopiedToDestXlsx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", "-1-Source.xlsx"))); if (!sourceCopiedToDestXlsx.Exists) + { File.Copy(sourceXlsx.FullName, sourceCopiedToDestXlsx.FullName); + } var dataTemplateFileNameSuffix = "-2-Generated-XmlData-Entire-Sheet.xml"; - var dataXmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", dataTemplateFileNameSuffix))); - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, true)) - { - var settings = new SmlToHtmlConverterSettings(); - var rangeXml = SmlDataRetriever.RetrieveSheet(sDoc, sheetName); - rangeXml.Save(dataXmlFi.FullName); - } + var dataXmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceCopiedToDestXlsx.Name.Replace(".xlsx", dataTemplateFileNameSuffix))); + using var sDoc = SpreadsheetDocument.Open(sourceCopiedToDestXlsx.FullName, true); + var settings = new SmlToHtmlConverterSettings(); + var rangeXml = SmlDataRetriever.RetrieveSheet(sDoc, sheetName); + rangeXml.Save(dataXmlFi.FullName); } [Theory] @@ -113,26 +99,24 @@ public void SH005_ConvertSheet(string name, string sheetName) [InlineData("SH129-FmtNumId-20.xlsx", "Sheet1", "A1:A10")] [InlineData("SH130-FmtNumId-21.xlsx", "Sheet1", "A1:A10")] [InlineData("SH131-FmtNumId-22.xlsx", "Sheet1", "A1:A10")] - public void SH004_ConvertRange(string name, string sheetName, string range) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); var sourceCopiedToDestXlsx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", "-1-Source.xlsx"))); if (!sourceCopiedToDestXlsx.Exists) + { File.Copy(sourceXlsx.FullName, sourceCopiedToDestXlsx.FullName); + } var dataTemplateFileNameSuffix = string.Format("-2-Generated-XmlData-{0}.xml", range.Replace(":", "")); - var dataXmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", dataTemplateFileNameSuffix))); - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, true)) - { - var settings = new SmlToHtmlConverterSettings(); - var rangeXml = SmlDataRetriever.RetrieveRange(sDoc, sheetName, range); - rangeXml.Save(dataXmlFi.FullName); - } + var dataXmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceCopiedToDestXlsx.Name.Replace(".xlsx", dataTemplateFileNameSuffix))); + using var sDoc = SpreadsheetDocument.Open(sourceCopiedToDestXlsx.FullName, true); + var settings = new SmlToHtmlConverterSettings(); + var rangeXml = SmlDataRetriever.RetrieveRange(sDoc, sheetName, range); + rangeXml.Save(dataXmlFi.FullName); } - [Theory] [InlineData("SH001-Table.xlsx", "MyTable")] @@ -144,36 +128,33 @@ public void SH004_ConvertRange(string name, string sheetName, string range) [InlineData("SH007-One-Cell-Table.xlsx", "Table1")] [InlineData("SH008-Table-With-Tall-Row.xlsx", "Table1")] [InlineData("SH009-Table-With-Wide-Column.xlsx", "Table1")] - public void SH003_ConvertTable(string name, string tableName) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); var sourceCopiedToDestXlsx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", "-1-Source.xlsx"))); if (!sourceCopiedToDestXlsx.Exists) - File.Copy(sourceXlsx.FullName, sourceCopiedToDestXlsx.FullName); - - var dataXmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceXlsx.Name.Replace(".xlsx", "-2-Generated-XmlData.xml"))); - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, true)) { - var settings = new SmlToHtmlConverterSettings(); - var rangeXml = SmlDataRetriever.RetrieveTable(sDoc, tableName); - rangeXml.Save(dataXmlFi.FullName); + File.Copy(sourceXlsx.FullName, sourceCopiedToDestXlsx.FullName); } + + var dataXmlFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceCopiedToDestXlsx.Name.Replace(".xlsx", "-2-Generated-XmlData.xml"))); + using var sDoc = SpreadsheetDocument.Open(sourceCopiedToDestXlsx.FullName, true); + var settings = new SmlToHtmlConverterSettings(); + var rangeXml = SmlDataRetriever.RetrieveTable(sDoc, tableName); + rangeXml.Save(dataXmlFi.FullName); } [Theory] [InlineData("Spreadsheet.xlsx", 2)] public void SH002_SheetNames(string name, int numberOfSheets) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, true)) - { - var sheetNames = SmlDataRetriever.SheetNames(sDoc); - Assert.Equal(numberOfSheets, sheetNames.Length); - } + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + using var sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, false); + var sheetNames = SmlDataRetriever.SheetNames(sDoc); + Assert.Equal(numberOfSheets, sheetNames.Length); } [Theory] @@ -181,15 +162,11 @@ public void SH002_SheetNames(string name, int numberOfSheets) [InlineData("SH002-TwoTablesTwoSheets.xlsx", 2)] public void SH001_TableNames(string name, int numberOfTables) { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, true)) - { - var table = SmlDataRetriever.TableNames(sDoc); - Assert.Equal(numberOfTables, table.Length); - } + var sourceDir = new DirectoryInfo("../../../../TestFiles/"); + var sourceXlsx = new FileInfo(Path.Combine(sourceDir.FullName, name)); + using var sDoc = SpreadsheetDocument.Open(sourceXlsx.FullName, false); + var table = SmlDataRetriever.TableNames(sDoc); + Assert.Equal(numberOfTables, table.Length); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs b/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs index 5deccaa2..e17ae745 100644 --- a/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs +++ b/OpenXmlPowerTools.Tests/SpreadsheetWriterTests.cs @@ -1,92 +1,83 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Validation; using System; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using Sw = OpenXmlPowerTools; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OxPt +namespace Codeuctivity.Tests { public class SwTests { [Fact] public void SW001_Simple() { - Sw.WorkbookDfn wb = new Sw.WorkbookDfn + var wb = new WorkbookDfn { - Worksheets = new Sw.WorksheetDfn[] + Worksheets = new WorksheetDfn[] { - new Sw.WorksheetDfn + new WorksheetDfn { Name = "MyFirstSheet", TableName = "NamesAndRates", - ColumnHeadings = new Sw.CellDfn[] + ColumnHeadings = new CellDfn[] { - new Sw.CellDfn + new CellDfn { Value = "Name", Bold = true, }, - new Sw.CellDfn + new CellDfn { Value = "Age", Bold = true, - HorizontalCellAlignment = Sw.HorizontalCellAlignment.Left, + HorizontalCellAlignment = HorizontalCellAlignment.Left, }, - new Sw.CellDfn + new CellDfn { Value = "Rate", Bold = true, - HorizontalCellAlignment = Sw.HorizontalCellAlignment.Left, + HorizontalCellAlignment = HorizontalCellAlignment.Left, } }, - Rows = new Sw.RowDfn[] + Rows = new RowDfn[] { - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "Eric", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, + new CellDfn { + CellDataType = CellDataType.Number, Value = 50, }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, + new CellDfn { + CellDataType = CellDataType.Number, Value = (decimal)45.00, FormatCode = "0.00", }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "Bob", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, + new CellDfn { + CellDataType = CellDataType.Number, Value = 42, }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, + new CellDfn { + CellDataType = CellDataType.Number, Value = (decimal)78.00, FormatCode = "0.00", }, @@ -96,207 +87,235 @@ public void SW001_Simple() } } }; - var outXlsx = new FileInfo(Path.Combine(Sw.TestUtil.TempDir.FullName, "SW001-Simple.xlsx")); - Sw.SpreadsheetWriter.Write(outXlsx.FullName, wb); + var outXlsx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "SW001-Simple.xlsx")); + SpreadsheetWriter.Write(outXlsx.FullName, wb); Validate(outXlsx); } + // Breaks with DocumentFormat.OpenXml 2.12 but works till 2.11.3 [Fact] public void SW002_AllDataTypes() { - Sw.WorkbookDfn wb = new Sw.WorkbookDfn + var wb = new WorkbookDfn { - Worksheets = new Sw.WorksheetDfn[] + Worksheets = new WorksheetDfn[] { - new Sw.WorksheetDfn + new WorksheetDfn { Name = "MyFirstSheet", - ColumnHeadings = new Sw.CellDfn[] + ColumnHeadings = new CellDfn[] { - new Sw.CellDfn + new CellDfn { Value = "DataType", Bold = true, }, - new Sw.CellDfn + new CellDfn { Value = "Value", Bold = true, - HorizontalCellAlignment = Sw.HorizontalCellAlignment.Right, + HorizontalCellAlignment = HorizontalCellAlignment.Right, }, }, - Rows = new Sw.RowDfn[] + Rows = new RowDfn[] { - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "Boolean", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Boolean, + new CellDfn { + CellDataType = CellDataType.Boolean, Value = true, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "Boolean", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Boolean, + new CellDfn { + CellDataType = CellDataType.Boolean, Value = false, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "String", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "A String", - HorizontalCellAlignment = Sw.HorizontalCellAlignment.Right, + HorizontalCellAlignment = HorizontalCellAlignment.Right, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "int", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, - Value = (int)100, + new CellDfn { + CellDataType = CellDataType.Number, + Value = 100, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "int?", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, - Value = (int?)100, + new CellDfn { + CellDataType = CellDataType.Number, + Value = 100, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "int? (is null)", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, + new CellDfn { + CellDataType = CellDataType.Number, Value = null, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "uint", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, - Value = (uint)101, + new CellDfn { + CellDataType = CellDataType.Number, + Value = 101, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "long", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, - Value = Int64.MaxValue, + new CellDfn { + CellDataType = CellDataType.Number, + Value = long.MaxValue, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "float", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, + new CellDfn { + CellDataType = CellDataType.Number, Value = (float)123.45, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "double", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, - Value = (double)123.45, + new CellDfn { + CellDataType = CellDataType.Number, + Value = 123.45, }, } }, - new Sw.RowDfn + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.String, + new CellDfn { + CellDataType = CellDataType.String, Value = "decimal", }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Number, + new CellDfn { + CellDataType = CellDataType.Number, Value = (decimal)123.45, }, } }, - new Sw.RowDfn + new RowDfn + { + Cells = new CellDfn[] + { + new CellDfn { + CellDataType = CellDataType.String, + Value = "date (t:str)", + }, + new CellDfn { + Value = new DateTime(2012, 1, 8).ToOADate(), + FormatCode= "mm-dd-yy", + Bold = true, + }, + } + }, + new RowDfn { - Cells = new Sw.CellDfn[] + Cells = new CellDfn[] { - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Date, - Value = new DateTime(2012, 1, 8), - FormatCode = "mm-dd-yy", - }, - new Sw.CellDfn { - CellDataType = Sw.CellDataType.Date, - Value = new DateTime(2012, 1, 9), - FormatCode = "mm-dd-yy", + new CellDfn { + CellDataType = CellDataType.String, + Value = "date (t:str)", + }, + new CellDfn { + Value = new DateTime(2012, 1, 9).ToOADate(), + FormatCode= "mm-dd-yy", Bold = true, - HorizontalCellAlignment = Sw.HorizontalCellAlignment.Center, + HorizontalCellAlignment = HorizontalCellAlignment.Center, + }, + } + }, + new RowDfn + { + Cells = new CellDfn[] + { + new CellDfn { + CellDataType = CellDataType.String, + Value = "date (t:d)", + }, + new CellDfn { + CellDataType = CellDataType.Date, + Value = new DateTime(2012, 1, 11).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff"), }, } }, @@ -304,43 +323,36 @@ public void SW002_AllDataTypes() } } }; - var outXlsx = new FileInfo(Path.Combine(Sw.TestUtil.TempDir.FullName, "SW002-DataTypes.xlsx")); - Sw.SpreadsheetWriter.Write(outXlsx.FullName, wb); + var outXlsx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "SW002-DataTypes.xlsx")); + SpreadsheetWriter.Write(outXlsx.FullName, wb); Validate(outXlsx); } private void Validate(FileInfo fi) { - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(fi.FullName, true)) - { - OpenXmlValidator v = new OpenXmlValidator(); - var errors = v.Validate(sDoc).Where(ve => !s_ExpectedErrors.Contains(ve.Description)); + using var sDoc = SpreadsheetDocument.Open(fi.FullName, true); + var v = new OpenXmlValidator(); + var errors = v.Validate(sDoc).Where(ve => !s_ExpectedErrors.Contains(ve.Description)); -#if false - // if a test fails validation post-processing, then can use this code to determine the SDK - // validation error(s). + // if a test fails validation post-processing, then can use this code to determine the SDK validation error(s). - if (errors.Count() != 0) + if (errors.Any()) + { + var sb = new StringBuilder(); + foreach (var item in errors) { - StringBuilder sb = new StringBuilder(); - foreach (var item in errors) - { - sb.Append(item.Description).Append(Environment.NewLine); - } - var s = sb.ToString(); - Console.WriteLine(s); + sb.Append(item.Description).Append(Environment.NewLine); } -#endif - - Assert.Empty(errors); + var s = sb.ToString(); + Console.WriteLine(s); } + + Assert.Empty(errors); } - private static List s_ExpectedErrors = new List() + private static readonly List s_ExpectedErrors = new List() { "The attribute 't' has invalid value 'd'. The Enumeration constraint failed.", }; } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/StronglyTypedBlockTests.cs b/OpenXmlPowerTools.Tests/StronglyTypedBlockTests.cs index ac0c5715..18edaba4 100644 --- a/OpenXmlPowerTools.Tests/StronglyTypedBlockTests.cs +++ b/OpenXmlPowerTools.Tests/StronglyTypedBlockTests.cs @@ -1,60 +1,51 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OpenXmlPowerTools.Tests +namespace Codeuctivity.Tests { public class StronglyTypedBlockTests : TestsBase { [Fact] public void CanUseStronglyTypedBlockToDemarcateApis() { - using (var stream = new MemoryStream()) - { - CreateEmptyWordprocessingDocument(stream); - - using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(stream, true)) - { - MainDocumentPart part = wordDocument.MainDocumentPart; - - // Add a paragraph through the PowerTools. - XDocument content = part.GetXDocument(); - XElement bodyElement = content.Descendants(W.body).First(); - bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); - part.PutXDocument(); + using var stream = new MemoryStream(); + CreateEmptyWordprocessingDocument(stream); - // This demonstrates the use of the StronglyTypedBlock in a using statement to - // demarcate the intermittent use of the strongly typed classes. - using (new StronglyTypedBlock(wordDocument)) - { - // Assert that we can see the paragraph added through the PowerTools. - Body body = part.Document.Body; - List paragraphs = body.Elements().ToList(); - Assert.Single(paragraphs); - Assert.Equal("Added through PowerTools", paragraphs[0].InnerText); + using var wordDocument = WordprocessingDocument.Open(stream, true); + var part = wordDocument.MainDocumentPart; - // Add a paragraph through the SDK. - body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); - } + // Add a paragraph through the PowerTools. + var content = part.GetXDocument(); + var bodyElement = content.Descendants(W.body).First(); + bodyElement.Add(new XElement(W.p, new XElement(W.r, new XElement(W.t, "Added through PowerTools")))); + part.PutXDocument(); - // Assert that we can see the paragraphs added through the PowerTools and the SDK. - content = part.GetXDocument(); - List paragraphElements = content.Descendants(W.p).ToList(); - Assert.Equal(2, paragraphElements.Count); - Assert.Equal("Added through PowerTools", paragraphElements[0].Value); - Assert.Equal("Added through SDK", paragraphElements[1].Value); - } + // This demonstrates the use of the StronglyTypedBlock in a using statement to + // demarcate the intermittent use of the strongly typed classes. + using (new StronglyTypedBlock(wordDocument)) + { + // Assert that we can see the paragraph added through the PowerTools. + var body = part.Document.Body; + var paragraphs = body.Elements().ToList(); + Assert.Single(paragraphs); + Assert.Equal("Added through PowerTools", paragraphs[0].InnerText); + + // Add a paragraph through the SDK. + body.AppendChild(new Paragraph(new Run(new Text("Added through SDK")))); } + + // Assert that we can see the paragraphs added through the PowerTools and the SDK. + content = part.GetXDocument(); + var paragraphElements = content.Descendants(W.p).ToList(); + Assert.Equal(2, paragraphElements.Count); + Assert.Equal("Added through PowerTools", paragraphElements[0].Value); + Assert.Equal("Added through SDK", paragraphElements[1].Value); } [Fact] @@ -63,6 +54,4 @@ public void ConstructorThrowsWhenPassingNull() Assert.Throws(() => new StronglyTypedBlock(null)); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/TestUtil.cs b/OpenXmlPowerTools.Tests/TestUtil.cs new file mode 100644 index 00000000..4ef92264 --- /dev/null +++ b/OpenXmlPowerTools.Tests/TestUtil.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Codeuctivity.Tests +{ + public class TestUtil + { + static TestUtil() + { + var now = DateTime.Now; + var tempDirName = $"Test-{now.Year - 2000:00}-{now.Month:00}-{now.Day:00}-{now.Hour:00}{now.Minute:00}{now.Second:00}-{Guid.NewGuid()}"; + var tempDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), tempDirName)); + tempDir.Create(); + TempDir = tempDir; + } + + /// + /// Lookin into /tmp or %temp% for test output + /// + public static DirectoryInfo TempDir { get; private set; } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/TestsBase.cs b/OpenXmlPowerTools.Tests/TestsBase.cs index a5df4f51..03c62f91 100644 --- a/OpenXmlPowerTools.Tests/TestsBase.cs +++ b/OpenXmlPowerTools.Tests/TestsBase.cs @@ -1,12 +1,9 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.IO; using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; +using System.IO; -namespace OpenXmlPowerTools.Tests +namespace Codeuctivity.Tests { /// /// Base class for unit tests providing utility methods. @@ -17,11 +14,9 @@ public class TestsBase protected static void CreateEmptyWordprocessingDocument(Stream stream) { - using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(stream, DocumentType)) - { - MainDocumentPart part = wordDocument.AddMainDocumentPart(); - part.Document = new Document(new Body()); - } + using var wordDocument = WordprocessingDocument.Create(stream, DocumentType); + var part = wordDocument.AddMainDocumentPart(); + part.Document = new Document(new Body()); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs b/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs index 562a6682..b54e8f69 100644 --- a/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs +++ b/OpenXmlPowerTools.Tests/UnicodeMapperTests.cs @@ -1,27 +1,9 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2016. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -Developer: Thomas Barnekow -Email: thomas@barnekow.info - -***************************************************************************/ - -using System.Collections.Generic; +using Codeuctivity.OpenXmlPowerTools; using System.Linq; using System.Xml.Linq; using Xunit; -#if !ELIDE_XUNIT_TESTS - -namespace OpenXmlPowerTools.Tests +namespace Codeuctivity.Tests { public class UnicodeMapperTests { @@ -63,7 +45,7 @@ public void CanCreateRunChildElementsFromSpecialCharacters() Assert.Equal(W.softHyphen, UnicodeMapper.CharToRunChild(UnicodeMapper.SoftHyphen).Name); Assert.Equal(W.tab, UnicodeMapper.CharToRunChild(UnicodeMapper.HorizontalTabulation).Name); - XElement element = UnicodeMapper.CharToRunChild(UnicodeMapper.FormFeed); + var element = UnicodeMapper.CharToRunChild(UnicodeMapper.FormFeed); Assert.Equal(W.br, element.Name); Assert.Equal("page", element.Attribute(W.type).Value); @@ -76,8 +58,8 @@ public void CanCreateCoalescedRuns() const string textString = "This is only text."; const string mixedString = "First\tSecond\tThird"; - List textRuns = UnicodeMapper.StringToCoalescedRunList(textString, null); - List mixedRuns = UnicodeMapper.StringToCoalescedRunList(mixedString, null); + var textRuns = UnicodeMapper.StringToCoalescedRunList(textString, null); + var mixedRuns = UnicodeMapper.StringToCoalescedRunList(mixedString, null); Assert.Single(textRuns); Assert.Equal(5, mixedRuns.Count); @@ -93,26 +75,26 @@ public void CanMapSymbols() var sym1 = new XElement(W.sym, new XAttribute(W.font, "Wingdings"), new XAttribute(W._char, "F028")); - char charFromSym1 = UnicodeMapper.SymToChar(sym1); - XElement symFromChar1 = UnicodeMapper.CharToRunChild(charFromSym1); + var charFromSym1 = UnicodeMapper.SymToChar(sym1); + var symFromChar1 = UnicodeMapper.CharToRunChild(charFromSym1); var sym2 = new XElement(W.sym, new XAttribute(W._char, "F028"), new XAttribute(W.font, "Wingdings")); - char charFromSym2 = UnicodeMapper.SymToChar(sym2); + var charFromSym2 = UnicodeMapper.SymToChar(sym2); var sym3 = new XElement(W.sym, new XAttribute(XNamespace.Xmlns + "w", W.w), new XAttribute(W.font, "Wingdings"), new XAttribute(W._char, "F028")); - char charFromSym3 = UnicodeMapper.SymToChar(sym3); + var charFromSym3 = UnicodeMapper.SymToChar(sym3); var sym4 = new XElement(W.sym, new XAttribute(XNamespace.Xmlns + "w", W.w), new XAttribute(W.font, "Webdings"), new XAttribute(W._char, "F028")); - char charFromSym4 = UnicodeMapper.SymToChar(sym4); - XElement symFromChar4 = UnicodeMapper.CharToRunChild(charFromSym4); + var charFromSym4 = UnicodeMapper.SymToChar(sym4); + var symFromChar4 = UnicodeMapper.CharToRunChild(charFromSym4); Assert.Equal(charFromSym1, charFromSym2); Assert.Equal(charFromSym1, charFromSym3); @@ -128,13 +110,13 @@ public void CanMapSymbols() [Fact] public void CanStringifySymbols() { - char charFromSym1 = UnicodeMapper.SymToChar("Wingdings", '\uF028'); - char charFromSym2 = UnicodeMapper.SymToChar("Wingdings", 0xF028); - char charFromSym3 = UnicodeMapper.SymToChar("Wingdings", "F028"); + var charFromSym1 = UnicodeMapper.SymToChar("Wingdings", '\uF028'); + var charFromSym2 = UnicodeMapper.SymToChar("Wingdings", 0xF028); + var charFromSym3 = UnicodeMapper.SymToChar("Wingdings", "F028"); - XElement symFromChar1 = UnicodeMapper.CharToRunChild(charFromSym1); - XElement symFromChar2 = UnicodeMapper.CharToRunChild(charFromSym2); - XElement symFromChar3 = UnicodeMapper.CharToRunChild(charFromSym3); + var symFromChar1 = UnicodeMapper.CharToRunChild(charFromSym1); + var symFromChar2 = UnicodeMapper.CharToRunChild(charFromSym2); + var symFromChar3 = UnicodeMapper.CharToRunChild(charFromSym3); Assert.Equal(charFromSym1, charFromSym2); Assert.Equal(charFromSym1, charFromSym3); @@ -143,6 +125,4 @@ public void CanStringifySymbols() Assert.Equal(symFromChar1.ToString(SaveOptions.None), symFromChar3.ToString(SaveOptions.None)); } } -} - -#endif +} \ No newline at end of file diff --git a/OpenXmlPowerTools.Tests/WmlComparerTests.cs b/OpenXmlPowerTools.Tests/WmlComparerTests.cs deleted file mode 100644 index a882b422..00000000 --- a/OpenXmlPowerTools.Tests/WmlComparerTests.cs +++ /dev/null @@ -1,1114 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; -using Xunit; -using System.Diagnostics; - -/****************************************************************************************************************/ -// Large tests have been commented out below. If and when there is an effort to improve performance for WmlComparer, -// then uncomment. Performance isn't bad, but certainly is possible to improve. -/****************************************************************************************************************/ - -#if !ELIDE_XUNIT_TESTS - -namespace OxPt -{ - public class WcTests - { - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - public static bool s_OpenWord = false; - public static bool m_OpenTempDirInExplorer = false; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - [Theory] - [InlineData("RC-0010", "RC/RC001-Before.docx", - @" - - RC/RC001-After1.docx - LightYellow - From Bob - - - RC/RC001-After2.docx - LightPink - From Fred - - ")] - [InlineData("RC-0020", "RC/RC002-Image.docx", - @" - - RC/RC002-Image-After1.docx - LightBlue - From Bob - - ")] - [InlineData("RC-0030", "RC/RC002-Image-After1.docx", - @" - - RC/RC002-Image.docx - LightBlue - From Bob - - ")] - [InlineData("RC-0040", "WC/WC027-Twenty-Paras-Before.docx", - @" - - WC/WC027-Twenty-Paras-After-1.docx - LightBlue - From Bob - - ")] - [InlineData("RC-0050", "WC/WC027-Twenty-Paras-Before.docx", - @" - - WC/WC027-Twenty-Paras-After-3.docx - LightBlue - From Bob - - ")] - [InlineData("RC-0060", "RC/RC003-Multi-Paras.docx", - @" - - RC/RC003-Multi-Paras-After.docx - LightBlue - From Bob - - ")] - [InlineData("RC-0070", "RC/RC004-Before.docx", - @" - - RC/RC004-After1.docx - LightYellow - From Bob - - - RC/RC004-After2.docx - LightPink - From Fred - - ")] - [InlineData("RC-0080", "RC/RC005-Before.docx", - @" - - RC/RC005-After1.docx - LightYellow - From Bob - - ")] - [InlineData("RC-0090", "RC/RC006-Before.docx", - @" - - RC/RC006-After1.docx - LightYellow - From Bob - - ")] - [InlineData("RC-0100", "RC/RC007-Endnotes-Before.docx", - @" - - RC/RC007-Endnotes-After.docx - LightYellow - From Bob - - ")] - - public void WC001_Consolidate(string testId, string originalName, string revisedDocumentsXml) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo originalDocx = new FileInfo(Path.Combine(sourceDir.FullName, originalName)); - - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (thisTestTempDir.Exists) - Assert.True(false, "Duplicate test id: " + testId); - else - thisTestTempDir.Create(); - - var originalCopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, originalDocx.Name)); - if (!originalCopiedToDestDocx.Exists) - { - var wml1 = new WmlDocument(originalDocx.FullName); - var wml2 = WordprocessingMLUtil.BreakLinkToTemplate(wml1); - wml2.SaveAs(originalCopiedToDestDocx.FullName); - } - - var revisedDocumentsXElement = XElement.Parse(revisedDocumentsXml); - var revisedDocumentsArray = revisedDocumentsXElement - .Elements() - .Select(z => - { - FileInfo revisedDocx = new FileInfo(Path.Combine(sourceDir.FullName, z.Element("DocName").Value)); - var revisedCopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, revisedDocx.Name)); - var wml1 = new WmlDocument(revisedDocx.FullName); - var wml2 = WordprocessingMLUtil.BreakLinkToTemplate(wml1); - wml2.SaveAs(revisedCopiedToDestDocx.FullName); - return new WmlRevisedDocumentInfo() - { - RevisedDocument = new WmlDocument(revisedCopiedToDestDocx.FullName), - Color = ColorParser.FromName(z.Element("Color")?.Value), - Revisor = z.Element("Revisor")?.Value, - }; - }) - .ToList(); - - var consolidatedDocxName = originalCopiedToDestDocx.Name.Replace(".docx", "-Consolidated.docx"); - var consolidatedDocumentFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, consolidatedDocxName)); - - WmlDocument source1Wml = new WmlDocument(originalCopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - settings.DebugTempFileDi = thisTestTempDir; - WmlDocument consolidatedWml = WmlComparer.Consolidate( - source1Wml, - revisedDocumentsArray, - settings); - var wml3 = WordprocessingMLUtil.BreakLinkToTemplate(consolidatedWml); - wml3.SaveAs(consolidatedDocumentFi.FullName); - - var validationErrors = ""; - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(consolidatedWml.DocumentByteArray, 0, consolidatedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { -#if true - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); -#else - sb.Append(" \"" + err.Description + "\"," + Environment.NewLine); -#endif - } - validationErrors = sb.ToString(); - } - } - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, consolidatedDocumentFi); - WordRunner.RunWord(wordExe, originalCopiedToDestDocx); - - var revisedList = revisedDocumentsXElement - .Elements() - .Select(z => - { - FileInfo revisedDocx = new FileInfo(Path.Combine(sourceDir.FullName, z.Element("DocName").Value)); - var revisedCopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, revisedDocx.Name)); - return revisedCopiedToDestDocx; - }) - .ToList(); - foreach (var item in revisedList) - WordRunner.RunWord(wordExe, item); - } - - /************************************************************************************************************************/ - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Open Windows Explorer - if (m_OpenTempDirInExplorer) - { - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var semaphorFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "z_ExplorerOpenedSemaphore.txt")); - if (!semaphorFi.Exists) - { - File.WriteAllText(semaphorFi.FullName, ""); - TestUtil.Explorer(thisTestTempDir); - } - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - } - - if (validationErrors != "") - Assert.True(false, validationErrors); - } - - [Theory] - [InlineData("WCB-1000", "CA/CA001-Plain.docx", "CA/CA001-Plain-Mod.docx")] - [InlineData("WCB-1010", "WC/WC001-Digits.docx", "WC/WC001-Digits-Mod.docx")] - [InlineData("WCB-1020", "WC/WC001-Digits.docx", "WC/WC001-Digits-Deleted-Paragraph.docx")] - [InlineData("WCB-1030", "WC/WC001-Digits-Deleted-Paragraph.docx", "WC/WC001-Digits.docx")] - [InlineData("WCB-1040", "WC/WC002-Unmodified.docx", "WC/WC002-DiffInMiddle.docx")] - [InlineData("WCB-1050", "WC/WC002-Unmodified.docx", "WC/WC002-DiffAtBeginning.docx")] - [InlineData("WCB-1060", "WC/WC002-Unmodified.docx", "WC/WC002-DeleteAtBeginning.docx")] - [InlineData("WCB-1070", "WC/WC002-Unmodified.docx", "WC/WC002-InsertAtBeginning.docx")] - [InlineData("WCB-1080", "WC/WC002-Unmodified.docx", "WC/WC002-InsertAtEnd.docx")] - [InlineData("WCB-1090", "WC/WC002-Unmodified.docx", "WC/WC002-DeleteAtEnd.docx")] - [InlineData("WCB-1100", "WC/WC002-Unmodified.docx", "WC/WC002-DeleteInMiddle.docx")] - [InlineData("WCB-1110", "WC/WC002-Unmodified.docx", "WC/WC002-InsertInMiddle.docx")] - [InlineData("WCB-1120", "WC/WC002-DeleteInMiddle.docx", "WC/WC002-Unmodified.docx")] - //[InlineData("WCB-1130", "WC/WC004-Large.docx", "WC/WC004-Large-Mod.docx")] - [InlineData("WCB-1140", "WC/WC006-Table.docx", "WC/WC006-Table-Delete-Row.docx")] - [InlineData("WCB-1150", "WC/WC006-Table-Delete-Row.docx", "WC/WC006-Table.docx")] - [InlineData("WCB-1160", "WC/WC006-Table.docx", "WC/WC006-Table-Delete-Contests-of-Row.docx")] - [InlineData("WCB-1170", "WC/WC007-Unmodified.docx", "WC/WC007-Longest-At-End.docx")] - [InlineData("WCB-1180", "WC/WC007-Unmodified.docx", "WC/WC007-Deleted-at-Beginning-of-Para.docx")] - [InlineData("WCB-1190", "WC/WC007-Unmodified.docx", "WC/WC007-Moved-into-Table.docx")] - [InlineData("WCB-1200", "WC/WC009-Table-Unmodified.docx", "WC/WC009-Table-Cell-1-1-Mod.docx")] - [InlineData("WCB-1210", "WC/WC010-Para-Before-Table-Unmodified.docx", "WC/WC010-Para-Before-Table-Mod.docx")] - [InlineData("WCB-1220", "WC/WC011-Before.docx", "WC/WC011-After.docx")] - [InlineData("WCB-1230", "WC/WC012-Math-Before.docx", "WC/WC012-Math-After.docx")] - [InlineData("WCB-1240", "WC/WC013-Image-Before.docx", "WC/WC013-Image-After.docx")] - [InlineData("WCB-1250", "WC/WC013-Image-Before.docx", "WC/WC013-Image-After2.docx")] - [InlineData("WCB-1260", "WC/WC013-Image-Before2.docx", "WC/WC013-Image-After2.docx")] - [InlineData("WCB-1270", "WC/WC014-SmartArt-Before.docx", "WC/WC014-SmartArt-After.docx")] - [InlineData("WCB-1280", "WC/WC014-SmartArt-With-Image-Before.docx", "WC/WC014-SmartArt-With-Image-After.docx")] - [InlineData("WCB-1290", "WC/WC014-SmartArt-With-Image-Before.docx", "WC/WC014-SmartArt-With-Image-Deleted-After.docx")] - [InlineData("WCB-1300", "WC/WC014-SmartArt-With-Image-Before.docx", "WC/WC014-SmartArt-With-Image-Deleted-After2.docx")] - [InlineData("WCB-1310", "WC/WC015-Three-Paragraphs.docx", "WC/WC015-Three-Paragraphs-After.docx")] - [InlineData("WCB-1320", "WC/WC016-Para-Image-Para.docx", "WC/WC016-Para-Image-Para-w-Deleted-Image.docx")] - [InlineData("WCB-1330", "WC/WC017-Image.docx", "WC/WC017-Image-After.docx")] - [InlineData("WCB-1340", "WC/WC018-Field-Simple-Before.docx", "WC/WC018-Field-Simple-After-1.docx")] - [InlineData("WCB-1350", "WC/WC018-Field-Simple-Before.docx", "WC/WC018-Field-Simple-After-2.docx")] - [InlineData("WCB-1360", "WC/WC019-Hyperlink-Before.docx", "WC/WC019-Hyperlink-After-1.docx")] - [InlineData("WCB-1370", "WC/WC019-Hyperlink-Before.docx", "WC/WC019-Hyperlink-After-2.docx")] - [InlineData("WCB-1380", "WC/WC020-FootNote-Before.docx", "WC/WC020-FootNote-After-1.docx")] - [InlineData("WCB-1390", "WC/WC020-FootNote-Before.docx", "WC/WC020-FootNote-After-2.docx")] - [InlineData("WCB-1400", "WC/WC021-Math-Before-1.docx", "WC/WC021-Math-After-1.docx")] - [InlineData("WCB-1410", "WC/WC021-Math-Before-2.docx", "WC/WC021-Math-After-2.docx")] - [InlineData("WCB-1420", "WC/WC022-Image-Math-Para-Before.docx", "WC/WC022-Image-Math-Para-After.docx")] - [InlineData("WCB-1430", "WC/WC023-Table-4-Row-Image-Before.docx", "WC/WC023-Table-4-Row-Image-After-Delete-1-Row.docx")] - [InlineData("WCB-1440", "WC/WC024-Table-Before.docx", "WC/WC024-Table-After.docx")] - [InlineData("WCB-1450", "WC/WC024-Table-Before.docx", "WC/WC024-Table-After2.docx")] - [InlineData("WCB-1460", "WC/WC025-Simple-Table-Before.docx", "WC/WC025-Simple-Table-After.docx")] - [InlineData("WCB-1470", "WC/WC026-Long-Table-Before.docx", "WC/WC026-Long-Table-After-1.docx")] - [InlineData("WCB-1480", "WC/WC027-Twenty-Paras-Before.docx", "WC/WC027-Twenty-Paras-After-1.docx")] - [InlineData("WCB-1490", "WC/WC027-Twenty-Paras-After-1.docx", "WC/WC027-Twenty-Paras-Before.docx")] - [InlineData("WCB-1500", "WC/WC027-Twenty-Paras-Before.docx", "WC/WC027-Twenty-Paras-After-2.docx")] - [InlineData("WCB-1510", "WC/WC030-Image-Math-Before.docx", "WC/WC030-Image-Math-After.docx")] - [InlineData("WCB-1520", "WC/WC031-Two-Maths-Before.docx", "WC/WC031-Two-Maths-After.docx")] - [InlineData("WCB-1530", "WC/WC032-Para-with-Para-Props.docx", "WC/WC032-Para-with-Para-Props-After.docx")] - [InlineData("WCB-1540", "WC/WC033-Merged-Cells-Before.docx", "WC/WC033-Merged-Cells-After1.docx")] - [InlineData("WCB-1550", "WC/WC033-Merged-Cells-Before.docx", "WC/WC033-Merged-Cells-After2.docx")] - [InlineData("WCB-1560", "WC/WC034-Footnotes-Before.docx", "WC/WC034-Footnotes-After1.docx")] - [InlineData("WCB-1570", "WC/WC034-Footnotes-Before.docx", "WC/WC034-Footnotes-After2.docx")] - [InlineData("WCB-1580", "WC/WC034-Footnotes-Before.docx", "WC/WC034-Footnotes-After3.docx")] - [InlineData("WCB-1590", "WC/WC034-Footnotes-After3.docx", "WC/WC034-Footnotes-Before.docx")] - [InlineData("WCB-1600", "WC/WC035-Footnote-Before.docx", "WC/WC035-Footnote-After.docx")] - [InlineData("WCB-1610", "WC/WC035-Footnote-After.docx", "WC/WC035-Footnote-Before.docx")] - [InlineData("WCB-1620", "WC/WC036-Footnote-With-Table-Before.docx", "WC/WC036-Footnote-With-Table-After.docx")] - [InlineData("WCB-1630", "WC/WC036-Footnote-With-Table-After.docx", "WC/WC036-Footnote-With-Table-Before.docx")] - [InlineData("WCB-1640", "WC/WC034-Endnotes-Before.docx", "WC/WC034-Endnotes-After1.docx")] - [InlineData("WCB-1650", "WC/WC034-Endnotes-Before.docx", "WC/WC034-Endnotes-After2.docx")] - [InlineData("WCB-1660", "WC/WC034-Endnotes-Before.docx", "WC/WC034-Endnotes-After3.docx")] - [InlineData("WCB-1670", "WC/WC034-Endnotes-After3.docx", "WC/WC034-Endnotes-Before.docx")] - [InlineData("WCB-1680", "WC/WC035-Endnote-Before.docx", "WC/WC035-Endnote-After.docx")] - [InlineData("WCB-1690", "WC/WC035-Endnote-After.docx", "WC/WC035-Endnote-Before.docx")] - [InlineData("WCB-1700", "WC/WC036-Endnote-With-Table-Before.docx", "WC/WC036-Endnote-With-Table-After.docx")] - [InlineData("WCB-1710", "WC/WC036-Endnote-With-Table-After.docx", "WC/WC036-Endnote-With-Table-Before.docx")] - [InlineData("WCB-1720", "WC/WC038-Document-With-BR-Before.docx", "WC/WC038-Document-With-BR-After.docx")] - [InlineData("WCB-1730", "RC/RC001-Before.docx", "RC/RC001-After1.docx")] - [InlineData("WCB-1740", "RC/RC002-Image.docx", "RC/RC002-Image-After1.docx")] - //[InlineData("WCB-1000", "", "")] - //[InlineData("WCB-1000", "", "")] - //[InlineData("WCB-1000", "", "")] - //[InlineData("WCB-1000", "", "")] - //[InlineData("WCB-1000", "", "")] - //[InlineData("WCB-1000", "", "")] - //[InlineData("WCB-1000", "", "")] - - - public void WC002_Consolidate_Bulk_Test(string testId, string name1, string name2) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (thisTestTempDir.Exists) - Assert.True(false, "Duplicate test id: " + testId); - else - thisTestTempDir.Create(); - - var source1CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name)); - if (!source1CopiedToDestDocx.Exists) - { - var wml1 = new WmlDocument(source1Docx.FullName); - var wml2 = WordprocessingMLUtil.BreakLinkToTemplate(wml1); - wml2.SaveAs(source1CopiedToDestDocx.FullName); - } - if (!source2CopiedToDestDocx.Exists) - { - var wml1 = new WmlDocument(source2Docx.FullName); - var wml2 = WordprocessingMLUtil.BreakLinkToTemplate(wml1); - wml2.SaveAs(source2CopiedToDestDocx.FullName); - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo source1DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name.Replace(".docx", "-For-Word.docx"))); - var source2CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name.Replace(".docx", "-For-Word.docx"))); - if (!source1CopiedToDestDocxForWord.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocxForWord.FullName); - if (!source2CopiedToDestDocxForWord.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocxForWord.FullName); - - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - var path = new DirectoryInfo(@"C:\Users\Eric\Documents\WindowsPowerShellModules\Open-Xml-PowerTools\TestFiles"); - WordRunner.RunWord(wordExe, source2CopiedToDestDocxForWord); - WordRunner.RunWord(wordExe, source1CopiedToDestDocxForWord); - } - - /************************************************************************************************************************/ - - var before = source1CopiedToDestDocx.Name.Replace(".docx", ""); - var after = source2CopiedToDestDocx.Name.Replace(".docx", ""); - var docxWithRevisionsFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-COMPARE-" + after + ".docx")); - var docxConsolidatedFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-CONSOLIDATED-" + after + ".docx")); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - WordprocessingMLUtil.BreakLinkToTemplate(comparedWml).SaveAs(docxWithRevisionsFi.FullName); - - List revisedDocInfo = new List() - { - new WmlRevisedDocumentInfo() - { - RevisedDocument = source2Wml, - Color = Color.LightBlue, - Revisor = "Revised by Eric White", - } - }; - WmlDocument consolidatedWml = WmlComparer.Consolidate( - source1Wml, - revisedDocInfo, - settings); - WordprocessingMLUtil.BreakLinkToTemplate(consolidatedWml).SaveAs(docxConsolidatedFi.FullName); - - string validationErrors = ""; - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(consolidatedWml.DocumentByteArray, 0, consolidatedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { -#if true - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); -#else - sb.Append(" \"" + err.Description + "\"," + Environment.NewLine); -#endif - } - validationErrors = sb.ToString(); - } - } - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, docxConsolidatedFi); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Open Windows Explorer - if (m_OpenTempDirInExplorer) - { - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var semaphorFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "z_ExplorerOpenedSemaphore.txt")); - if (!semaphorFi.Exists) - { - File.WriteAllText(semaphorFi.FullName, ""); - TestUtil.Explorer(thisTestTempDir); - } - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (validationErrors != "") - Assert.True(false, validationErrors); - } - - [Theory] - [InlineData("WC-1000", "CA/CA001-Plain.docx", "CA/CA001-Plain-Mod.docx", 1)] - [InlineData("WC-1010", "WC/WC001-Digits.docx", "WC/WC001-Digits-Mod.docx", 4)] - [InlineData("WC-1020", "WC/WC001-Digits.docx", "WC/WC001-Digits-Deleted-Paragraph.docx", 1)] - [InlineData("WC-1030", "WC/WC001-Digits-Deleted-Paragraph.docx", "WC/WC001-Digits.docx", 1)] - [InlineData("WC-1040", "WC/WC002-Unmodified.docx", "WC/WC002-DiffInMiddle.docx", 2)] - [InlineData("WC-1050", "WC/WC002-Unmodified.docx", "WC/WC002-DiffAtBeginning.docx", 2)] - [InlineData("WC-1060", "WC/WC002-Unmodified.docx", "WC/WC002-DeleteAtBeginning.docx", 1)] - [InlineData("WC-1070", "WC/WC002-Unmodified.docx", "WC/WC002-InsertAtBeginning.docx", 1)] - [InlineData("WC-1080", "WC/WC002-Unmodified.docx", "WC/WC002-InsertAtEnd.docx", 1)] - [InlineData("WC-1090", "WC/WC002-Unmodified.docx", "WC/WC002-DeleteAtEnd.docx", 1)] - [InlineData("WC-1100", "WC/WC002-Unmodified.docx", "WC/WC002-DeleteInMiddle.docx", 1)] - [InlineData("WC-1110", "WC/WC002-Unmodified.docx", "WC/WC002-InsertInMiddle.docx", 1)] - [InlineData("WC-1120", "WC/WC002-DeleteInMiddle.docx", "WC/WC002-Unmodified.docx", 1)] - //[InlineData("WC-1130", "WC/WC004-Large.docx", "WC/WC004-Large-Mod.docx", 2)] - [InlineData("WC-1140", "WC/WC006-Table.docx", "WC/WC006-Table-Delete-Row.docx", 1)] - [InlineData("WC-1150", "WC/WC006-Table-Delete-Row.docx", "WC/WC006-Table.docx", 1)] - [InlineData("WC-1160", "WC/WC006-Table.docx", "WC/WC006-Table-Delete-Contests-of-Row.docx", 2)] - [InlineData("WC-1170", "WC/WC007-Unmodified.docx", "WC/WC007-Longest-At-End.docx", 2)] - [InlineData("WC-1180", "WC/WC007-Unmodified.docx", "WC/WC007-Deleted-at-Beginning-of-Para.docx", 1)] - [InlineData("WC-1190", "WC/WC007-Unmodified.docx", "WC/WC007-Moved-into-Table.docx", 2)] - [InlineData("WC-1200", "WC/WC009-Table-Unmodified.docx", "WC/WC009-Table-Cell-1-1-Mod.docx", 1)] - [InlineData("WC-1210", "WC/WC010-Para-Before-Table-Unmodified.docx", "WC/WC010-Para-Before-Table-Mod.docx", 3)] - [InlineData("WC-1220", "WC/WC011-Before.docx", "WC/WC011-After.docx", 2)] - [InlineData("WC-1230", "WC/WC012-Math-Before.docx", "WC/WC012-Math-After.docx", 2)] - [InlineData("WC-1240", "WC/WC013-Image-Before.docx", "WC/WC013-Image-After.docx", 2)] - [InlineData("WC-1250", "WC/WC013-Image-Before.docx", "WC/WC013-Image-After2.docx", 2)] - [InlineData("WC-1260", "WC/WC013-Image-Before2.docx", "WC/WC013-Image-After2.docx", 2)] - [InlineData("WC-1270", "WC/WC014-SmartArt-Before.docx", "WC/WC014-SmartArt-After.docx", 2)] - [InlineData("WC-1280", "WC/WC014-SmartArt-With-Image-Before.docx", "WC/WC014-SmartArt-With-Image-After.docx", 2)] - [InlineData("WC-1310", "WC/WC014-SmartArt-With-Image-Before.docx", "WC/WC014-SmartArt-With-Image-Deleted-After.docx", 3)] - [InlineData("WC-1320", "WC/WC014-SmartArt-With-Image-Before.docx", "WC/WC014-SmartArt-With-Image-Deleted-After2.docx", 1)] - [InlineData("WC-1330", "WC/WC015-Three-Paragraphs.docx", "WC/WC015-Three-Paragraphs-After.docx", 3)] - [InlineData("WC-1340", "WC/WC016-Para-Image-Para.docx", "WC/WC016-Para-Image-Para-w-Deleted-Image.docx", 1)] - [InlineData("WC-1350", "WC/WC017-Image.docx", "WC/WC017-Image-After.docx", 3)] - [InlineData("WC-1360", "WC/WC018-Field-Simple-Before.docx", "WC/WC018-Field-Simple-After-1.docx", 2)] - [InlineData("WC-1370", "WC/WC018-Field-Simple-Before.docx", "WC/WC018-Field-Simple-After-2.docx", 3)] - [InlineData("WC-1380", "WC/WC019-Hyperlink-Before.docx", "WC/WC019-Hyperlink-After-1.docx", 3)] - [InlineData("WC-1390", "WC/WC019-Hyperlink-Before.docx", "WC/WC019-Hyperlink-After-2.docx", 5)] - [InlineData("WC-1400", "WC/WC020-FootNote-Before.docx", "WC/WC020-FootNote-After-1.docx", 3)] - [InlineData("WC-1410", "WC/WC020-FootNote-Before.docx", "WC/WC020-FootNote-After-2.docx", 5)] - [InlineData("WC-1420", "WC/WC021-Math-Before-1.docx", "WC/WC021-Math-After-1.docx", 9)] - [InlineData("WC-1430", "WC/WC021-Math-Before-2.docx", "WC/WC021-Math-After-2.docx", 6)] - [InlineData("WC-1440", "WC/WC022-Image-Math-Para-Before.docx", "WC/WC022-Image-Math-Para-After.docx", 10)] - [InlineData("WC-1450", "WC/WC023-Table-4-Row-Image-Before.docx", "WC/WC023-Table-4-Row-Image-After-Delete-1-Row.docx", 7)] - [InlineData("WC-1460", "WC/WC024-Table-Before.docx", "WC/WC024-Table-After.docx", 1)] - [InlineData("WC-1470", "WC/WC024-Table-Before.docx", "WC/WC024-Table-After2.docx", 7)] - [InlineData("WC-1480", "WC/WC025-Simple-Table-Before.docx", "WC/WC025-Simple-Table-After.docx", 4)] - [InlineData("WC-1500", "WC/WC026-Long-Table-Before.docx", "WC/WC026-Long-Table-After-1.docx", 2)] - [InlineData("WC-1510", "WC/WC027-Twenty-Paras-Before.docx", "WC/WC027-Twenty-Paras-After-1.docx", 2)] - [InlineData("WC-1520", "WC/WC027-Twenty-Paras-After-1.docx", "WC/WC027-Twenty-Paras-Before.docx", 2)] - [InlineData("WC-1530", "WC/WC027-Twenty-Paras-Before.docx", "WC/WC027-Twenty-Paras-After-2.docx", 4)] - [InlineData("WC-1540", "WC/WC030-Image-Math-Before.docx", "WC/WC030-Image-Math-After.docx", 2)] - [InlineData("WC-1550", "WC/WC031-Two-Maths-Before.docx", "WC/WC031-Two-Maths-After.docx", 4)] - [InlineData("WC-1560", "WC/WC032-Para-with-Para-Props.docx", "WC/WC032-Para-with-Para-Props-After.docx", 3)] - [InlineData("WC-1570", "WC/WC033-Merged-Cells-Before.docx", "WC/WC033-Merged-Cells-After1.docx", 2)] - [InlineData("WC-1580", "WC/WC033-Merged-Cells-Before.docx", "WC/WC033-Merged-Cells-After2.docx", 4)] - [InlineData("WC-1600", "WC/WC034-Footnotes-Before.docx", "WC/WC034-Footnotes-After1.docx", 1)] - [InlineData("WC-1610", "WC/WC034-Footnotes-Before.docx", "WC/WC034-Footnotes-After2.docx", 4)] - [InlineData("WC-1620", "WC/WC034-Footnotes-Before.docx", "WC/WC034-Footnotes-After3.docx", 3)] - [InlineData("WC-1630", "WC/WC034-Footnotes-After3.docx", "WC/WC034-Footnotes-Before.docx", 3)] - [InlineData("WC-1640", "WC/WC035-Footnote-Before.docx", "WC/WC035-Footnote-After.docx", 2)] - [InlineData("WC-1650", "WC/WC035-Footnote-After.docx", "WC/WC035-Footnote-Before.docx", 2)] - [InlineData("WC-1660", "WC/WC036-Footnote-With-Table-Before.docx", "WC/WC036-Footnote-With-Table-After.docx", 5)] - [InlineData("WC-1670", "WC/WC036-Footnote-With-Table-After.docx", "WC/WC036-Footnote-With-Table-Before.docx", 5)] - [InlineData("WC-1680", "WC/WC034-Endnotes-Before.docx", "WC/WC034-Endnotes-After1.docx", 1)] - [InlineData("WC-1700", "WC/WC034-Endnotes-Before.docx", "WC/WC034-Endnotes-After2.docx", 4)] - [InlineData("WC-1710", "WC/WC034-Endnotes-Before.docx", "WC/WC034-Endnotes-After3.docx", 7)] - [InlineData("WC-1720", "WC/WC034-Endnotes-After3.docx", "WC/WC034-Endnotes-Before.docx", 7)] - [InlineData("WC-1730", "WC/WC035-Endnote-Before.docx", "WC/WC035-Endnote-After.docx", 2)] - [InlineData("WC-1740", "WC/WC035-Endnote-After.docx", "WC/WC035-Endnote-Before.docx", 2)] - [InlineData("WC-1750", "WC/WC036-Endnote-With-Table-Before.docx", "WC/WC036-Endnote-With-Table-After.docx", 6)] - [InlineData("WC-1760", "WC/WC036-Endnote-With-Table-After.docx", "WC/WC036-Endnote-With-Table-Before.docx", 6)] - [InlineData("WC-1770", "WC/WC037-Textbox-Before.docx", "WC/WC037-Textbox-After1.docx", 2)] - [InlineData("WC-1780", "WC/WC038-Document-With-BR-Before.docx", "WC/WC038-Document-With-BR-After.docx", 2)] - [InlineData("WC-1800", "RC/RC001-Before.docx", "RC/RC001-After1.docx", 2)] - [InlineData("WC-1810", "RC/RC002-Image.docx", "RC/RC002-Image-After1.docx", 1)] - [InlineData("WC-1820", "WC/WC039-Break-In-Row.docx", "WC/WC039-Break-In-Row-After1.docx", 1)] - [InlineData("WC-1830", "WC/WC041-Table-5.docx", "WC/WC041-Table-5-Mod.docx", 2)] - [InlineData("WC-1840", "WC/WC042-Table-5.docx", "WC/WC042-Table-5-Mod.docx", 2)] - [InlineData("WC-1850", "WC/WC043-Nested-Table.docx", "WC/WC043-Nested-Table-Mod.docx", 2)] - [InlineData("WC-1860", "WC/WC044-Text-Box.docx", "WC/WC044-Text-Box-Mod.docx", 2)] - [InlineData("WC-1870", "WC/WC045-Text-Box.docx", "WC/WC045-Text-Box-Mod.docx", 2)] - [InlineData("WC-1880", "WC/WC046-Two-Text-Box.docx", "WC/WC046-Two-Text-Box-Mod.docx", 2)] - [InlineData("WC-1890", "WC/WC047-Two-Text-Box.docx", "WC/WC047-Two-Text-Box-Mod.docx", 2)] - [InlineData("WC-1900", "WC/WC048-Text-Box-in-Cell.docx", "WC/WC048-Text-Box-in-Cell-Mod.docx", 6)] - [InlineData("WC-1910", "WC/WC049-Text-Box-in-Cell.docx", "WC/WC049-Text-Box-in-Cell-Mod.docx", 5)] - [InlineData("WC-1920", "WC/WC050-Table-in-Text-Box.docx", "WC/WC050-Table-in-Text-Box-Mod.docx", 8)] - [InlineData("WC-1930", "WC/WC051-Table-in-Text-Box.docx", "WC/WC051-Table-in-Text-Box-Mod.docx", 9)] - [InlineData("WC-1940", "WC/WC052-SmartArt-Same.docx", "WC/WC052-SmartArt-Same-Mod.docx", 2)] - [InlineData("WC-1950", "WC/WC053-Text-in-Cell.docx", "WC/WC053-Text-in-Cell-Mod.docx", 2)] - [InlineData("WC-1960", "WC/WC054-Text-in-Cell.docx", "WC/WC054-Text-in-Cell-Mod.docx", 0)] - [InlineData("WC-1970", "WC/WC055-French.docx", "WC/WC055-French-Mod.docx", 0)] - [InlineData("WC-1980", "WC/WC056-French.docx", "WC/WC056-French-Mod.docx", 0)] - [InlineData("WC-1990", "WC/WC057-Table-Merged-Cell.docx", "WC/WC057-Table-Merged-Cell-Mod.docx", 4)] - [InlineData("WC-2000", "WC/WC058-Table-Merged-Cell.docx", "WC/WC058-Table-Merged-Cell-Mod.docx", 6)] - [InlineData("WC-2010", "WC/WC059-Footnote.docx", "WC/WC059-Footnote-Mod.docx", 5)] - [InlineData("WC-2020", "WC/WC060-Endnote.docx", "WC/WC060-Endnote-Mod.docx", 3)] - [InlineData("WC-2030", "WC/WC061-Style-Added.docx", "WC/WC061-Style-Added-Mod.docx", 1)] - [InlineData("WC-2040", "WC/WC062-New-Char-Style-Added.docx", "WC/WC062-New-Char-Style-Added-Mod.docx", 2)] - [InlineData("WC-2050", "WC/WC063-Footnote.docx", "WC/WC063-Footnote-Mod.docx", 1)] - [InlineData("WC-2060", "WC/WC063-Footnote-Mod.docx", "WC/WC063-Footnote.docx", 1)] - [InlineData("WC-2070", "WC/WC064-Footnote.docx", "WC/WC064-Footnote-Mod.docx", 0)] - [InlineData("WC-2080", "WC/WC065-Textbox.docx", "WC/WC065-Textbox-Mod.docx", 2)] - [InlineData("WC-2090", "WC/WC066-Textbox-Before-Ins.docx", "WC/WC066-Textbox-Before-Ins-Mod.docx", 1)] - [InlineData("WC-2092", "WC/WC066-Textbox-Before-Ins-Mod.docx", "WC/WC066-Textbox-Before-Ins.docx", 1)] - [InlineData("WC-2100", "WC/WC067-Textbox-Image.docx", "WC/WC067-Textbox-Image-Mod.docx", 2)] - //[InlineData("WC-1000", "", "", 0)] - //[InlineData("WC-1000", "", "", 0)] - //[InlineData("WC-1000", "", "", 0)] - //[InlineData("WC-1000", "", "", 0)] - //[InlineData("WC-1000", "", "", 0)] - //[InlineData("WC-1000", "", "", 0)] - - public void WC003_Compare(string testId, string name1, string name2, int revisionCount) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (thisTestTempDir.Exists) - Assert.True(false, "Duplicate test id???"); - else - thisTestTempDir.Create(); - - var source1CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name)); - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - var before = source1CopiedToDestDocx.Name.Replace(".docx", ""); - var after = source2CopiedToDestDocx.Name.Replace(".docx", ""); - //var baselineDocxWithRevisionsFi = new FileInfo(Path.Combine(source1Docx.DirectoryName, before + "-COMPARE-" + after + ".docx")); - var docxWithRevisionsFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-COMPARE-" + after + ".docx")); - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo source1DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name.Replace(".docx", "-For-Word.docx"))); - var source2CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name.Replace(".docx", "-For-Word.docx"))); - File.Copy(source1Docx.FullName, source1CopiedToDestDocxForWord.FullName); - File.Copy(source2Docx.FullName, source2CopiedToDestDocxForWord.FullName); - - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - var path = new DirectoryInfo(@"C:\Users\Eric\Documents\WindowsPowerShellModules\Open-Xml-PowerTools\TestFiles"); - WordRunner.RunWord(wordExe, source2CopiedToDestDocxForWord); - WordRunner.RunWord(wordExe, source1CopiedToDestDocxForWord); - } - - /************************************************************************************************************************/ - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - settings.DebugTempFileDi = thisTestTempDir; - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - comparedWml.SaveAs(docxWithRevisionsFi.FullName); - -#if false - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // create batch file to copy newly generated ContentTypeXml and NarrDoc to the TestFiles directory. - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var batchFileName = "Copy-Gen-Files-To-TestFiles.bat"; - var batchFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, batchFileName)); - var batch = ""; - batch += "copy " + docxWithRevisionsFi.FullName + " " + source1Docx.DirectoryName + Environment.NewLine; - if (batchFi.Exists) - File.AppendAllText(batchFi.FullName, batch); - else - File.WriteAllText(batchFi.FullName, batch); - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } -#endif - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // validate generated document - var validationErrors = ""; - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(comparedWml.DocumentByteArray, 0, comparedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { -#if true - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); -#else - sb.Append(" \"" + err.Description + "\"," + Environment.NewLine); -#endif - } - validationErrors = sb.ToString(); - } - } - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, docxWithRevisionsFi); - } - - /************************************************************************************************************************/ - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Open Windows Explorer - if (m_OpenTempDirInExplorer) - { - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var semaphorFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "z_ExplorerOpenedSemaphore.txt")); - if (!semaphorFi.Exists) - { - File.WriteAllText(semaphorFi.FullName, ""); - TestUtil.Explorer(thisTestTempDir); - } - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - } - - if (validationErrors != "") - { - Assert.True(false, validationErrors); - } - - WmlComparerSettings settings2 = new WmlComparerSettings(); - - WmlDocument revisionWml = new WmlDocument(docxWithRevisionsFi.FullName); - var revisions = WmlComparer.GetRevisions(revisionWml, settings); - Assert.Equal(revisionCount, revisions.Count()); - - var afterRejectingWml = RevisionProcessor.RejectRevisions(revisionWml); - - var WRITE_TEMP_FILES = true; - - if (WRITE_TEMP_FILES) - { - var afterRejectingFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, "AfterRejecting.docx")); - afterRejectingWml.SaveAs(afterRejectingFi.FullName); - } - - WmlDocument afterRejectingComparedWml = WmlComparer.Compare(source1Wml, afterRejectingWml, settings); - var sanityCheck1 = WmlComparer.GetRevisions(afterRejectingComparedWml, settings); - - if (WRITE_TEMP_FILES) - { - var afterRejectingComparedFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, "AfterRejectingCompared.docx")); - afterRejectingComparedWml.SaveAs(afterRejectingComparedFi.FullName); - } - - var afterAcceptingWml = RevisionProcessor.AcceptRevisions(revisionWml); - - if (WRITE_TEMP_FILES) - { - var afterAcceptingFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, "AfterAccepting.docx")); - afterAcceptingWml.SaveAs(afterAcceptingFi.FullName); - } - - WmlDocument afterAcceptingComparedWml = WmlComparer.Compare(source2Wml, afterAcceptingWml, settings); - var sanityCheck2 = WmlComparer.GetRevisions(afterAcceptingComparedWml, settings); - - if (WRITE_TEMP_FILES) - { - var afterAcceptingComparedFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, "AfterAcceptingCompared.docx")); - afterAcceptingComparedWml.SaveAs(afterAcceptingComparedFi.FullName); - } - - if (sanityCheck1.Count() != 0) - Assert.True(false, "Sanity Check #1 failed"); - if (sanityCheck2.Count() != 0) - Assert.True(false, "Sanity Check #2 failed"); - } - -#if false - [Theory] - [InlineData("WC/WC037-Textbox-Before.docx", "WC/WC037-Textbox-After1.docx", 2)] - - public void WC003_Throws(string name1, string name2, int revisionCount) - { - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source2Docx.Name)); - if (!source1CopiedToDestDocx.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - if (!source2CopiedToDestDocx.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - Assert.Throws(() => - { - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - }); - } -#endif - - [Theory] - [InlineData("WCS-1000", "WC/WC001-Digits.docx")] - [InlineData("WCS-1010", "WC/WC001-Digits-Deleted-Paragraph.docx")] - [InlineData("WCS-1020", "WC/WC001-Digits-Mod.docx")] - [InlineData("WCS-1030", "WC/WC002-DeleteAtBeginning.docx")] - [InlineData("WCS-1040", "WC/WC002-DeleteAtEnd.docx")] - [InlineData("WCS-1050", "WC/WC002-DeleteInMiddle.docx")] - [InlineData("WCS-1060", "WC/WC002-DiffAtBeginning.docx")] - [InlineData("WCS-1070", "WC/WC002-DiffInMiddle.docx")] - [InlineData("WCS-1080", "WC/WC002-InsertAtBeginning.docx")] - [InlineData("WCS-1090", "WC/WC002-InsertAtEnd.docx")] - [InlineData("WCS-1100", "WC/WC002-InsertInMiddle.docx")] - [InlineData("WCS-1110", "WC/WC002-Unmodified.docx")] - //[InlineData("WCS-1120", "WC/WC004-Large.docx")] - //[InlineData("WCS-1130", "WC/WC004-Large-Mod.docx")] - [InlineData("WCS-1140", "WC/WC006-Table.docx")] - [InlineData("WCS-1150", "WC/WC006-Table-Delete-Contests-of-Row.docx")] - [InlineData("WCS-1160", "WC/WC006-Table-Delete-Row.docx")] - [InlineData("WCS-1170", "WC/WC007-Deleted-at-Beginning-of-Para.docx")] - [InlineData("WCS-1180", "WC/WC007-Longest-At-End.docx")] - [InlineData("WCS-1190", "WC/WC007-Moved-into-Table.docx")] - [InlineData("WCS-1200", "WC/WC007-Unmodified.docx")] - [InlineData("WCS-1210", "WC/WC009-Table-Cell-1-1-Mod.docx")] - [InlineData("WCS-1220", "WC/WC009-Table-Unmodified.docx")] - [InlineData("WCS-1230", "WC/WC010-Para-Before-Table-Mod.docx")] - [InlineData("WCS-1240", "WC/WC010-Para-Before-Table-Unmodified.docx")] - [InlineData("WCS-1250", "WC/WC011-After.docx")] - [InlineData("WCS-1260", "WC/WC011-Before.docx")] - [InlineData("WCS-1270", "WC/WC012-Math-After.docx")] - [InlineData("WCS-1280", "WC/WC012-Math-Before.docx")] - [InlineData("WCS-1290", "WC/WC013-Image-After.docx")] - [InlineData("WCS-1300", "WC/WC013-Image-After2.docx")] - [InlineData("WCS-1310", "WC/WC013-Image-Before.docx")] - [InlineData("WCS-1320", "WC/WC013-Image-Before2.docx")] - [InlineData("WCS-1330", "WC/WC014-SmartArt-After.docx")] - [InlineData("WCS-1340", "WC/WC014-SmartArt-Before.docx")] - [InlineData("WCS-1350", "WC/WC014-SmartArt-With-Image-After.docx")] - [InlineData("WCS-1360", "WC/WC014-SmartArt-With-Image-Before.docx")] - [InlineData("WCS-1370", "WC/WC014-SmartArt-With-Image-Deleted-After.docx")] - [InlineData("WCS-1380", "WC/WC014-SmartArt-With-Image-Deleted-After2.docx")] - [InlineData("WCS-1390", "WC/WC015-Three-Paragraphs.docx")] - [InlineData("WCS-1400", "WC/WC015-Three-Paragraphs-After.docx")] - [InlineData("WCS-1410", "WC/WC016-Para-Image-Para.docx")] - [InlineData("WCS-1420", "WC/WC016-Para-Image-Para-w-Deleted-Image.docx")] - [InlineData("WCS-1430", "WC/WC017-Image.docx")] - [InlineData("WCS-1440", "WC/WC017-Image-After.docx")] - [InlineData("WCS-1450", "WC/WC018-Field-Simple-After-1.docx")] - [InlineData("WCS-1460", "WC/WC018-Field-Simple-After-2.docx")] - [InlineData("WCS-1470", "WC/WC018-Field-Simple-Before.docx")] - [InlineData("WCS-1480", "WC/WC019-Hyperlink-After-1.docx")] - [InlineData("WCS-1490", "WC/WC019-Hyperlink-After-2.docx")] - [InlineData("WCS-1500", "WC/WC019-Hyperlink-Before.docx")] - [InlineData("WCS-1510", "WC/WC020-FootNote-After-1.docx")] - [InlineData("WCS-1520", "WC/WC020-FootNote-After-2.docx")] - [InlineData("WCS-1530", "WC/WC020-FootNote-Before.docx")] - [InlineData("WCS-1540", "WC/WC021-Math-After-1.docx")] - [InlineData("WCS-1550", "WC/WC021-Math-Before-1.docx")] - [InlineData("WCS-1560", "WC/WC022-Image-Math-Para-After.docx")] - [InlineData("WCS-1570", "WC/WC022-Image-Math-Para-Before.docx")] - //[InlineData("WCS-1580", "", "")] - //[InlineData("WCS-1590", "", "")] - //[InlineData("WCS-1600", "", "")] - //[InlineData("WCS-1610", "", "")] - - public void WC004_Compare_To_Self(string testId, string name) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (thisTestTempDir.Exists) - Assert.True(false, "Duplicate test id???"); - else - thisTestTempDir.Create(); - - var sourceCopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, sourceDocx.Name.Replace(".docx", "-Source.docx"))); - if (!sourceCopiedToDestDocx.Exists) - File.Copy(sourceDocx.FullName, sourceCopiedToDestDocx.FullName); - - var before = sourceCopiedToDestDocx.Name.Replace(".docx", ""); - var docxComparedFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-COMPARE" + ".docx")); - var docxCompared2Fi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-COMPARE2" + ".docx")); - - WmlDocument source1Wml = new WmlDocument(sourceCopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(sourceCopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - comparedWml.SaveAs(docxComparedFi.FullName); - ValidateDocument(comparedWml); - - WmlDocument comparedWml2 = WmlComparer.Compare(comparedWml, source1Wml, settings); - comparedWml2.SaveAs(docxCompared2Fi.FullName); - ValidateDocument(comparedWml2); - } - - [Theory] - [InlineData("WCI-1000", "WC/WC040-Case-Before.docx", "WC/WC040-Case-After.docx", 2)] - - public void WC005_Compare_CaseInsensitive(string testId, string name1, string name2, int revisionCount) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (thisTestTempDir.Exists) - Assert.True(false, "Duplicate test id???"); - else - thisTestTempDir.Create(); - - var source1CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name)); - if (!source1CopiedToDestDocx.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - if (!source2CopiedToDestDocx.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo source1DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name.Replace(".docx", "-For-Word.docx"))); - var source2CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name.Replace(".docx", "-For-Word.docx"))); - if (!source1CopiedToDestDocxForWord.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocxForWord.FullName); - if (!source2CopiedToDestDocxForWord.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocxForWord.FullName); - - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - var path = new DirectoryInfo(@"C:\Users\Eric\Documents\WindowsPowerShellModules\Open-Xml-PowerTools\TestFiles"); - WordRunner.RunWord(wordExe, source2CopiedToDestDocxForWord); - WordRunner.RunWord(wordExe, source1CopiedToDestDocxForWord); - } - - /************************************************************************************************************************/ - - var before = source1CopiedToDestDocx.Name.Replace(".docx", ""); - var after = source2CopiedToDestDocx.Name.Replace(".docx", ""); - var docxWithRevisionsFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-COMPARE-" + after + ".docx")); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - settings.CaseInsensitive = true; - settings.CultureInfo = System.Globalization.CultureInfo.CurrentCulture; - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - comparedWml.SaveAs(docxWithRevisionsFi.FullName); - - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(comparedWml.DocumentByteArray, 0, comparedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { -#if true - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); -#else - sb.Append(" \"" + err.Description + "\"," + Environment.NewLine); -#endif - } - var sbs = sb.ToString(); - Assert.Equal("", sbs); - } - } - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, docxWithRevisionsFi); - } - - /************************************************************************************************************************/ - - WmlDocument revisionWml = new WmlDocument(docxWithRevisionsFi.FullName); - var revisions = WmlComparer.GetRevisions(revisionWml, settings); - Assert.Equal(revisionCount, revisions.Count()); - } - - private static void ValidateDocument(WmlDocument wmlToValidate) - { - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(wmlToValidate.DocumentByteArray, 0, wmlToValidate.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() != 0) - { - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { -#if true - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); -#else - sb.Append(" \"" + err.Description + "\"," + Environment.NewLine); -#endif - - } - var sbs = sb.ToString(); - Assert.Equal("", sbs); - } - } - } - } - - public static string[] ExpectedErrors = new string[] { - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstRow' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastRow' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstColumn' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastColumn' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noHBand' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noVBand' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:allStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:customStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:latentStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:stylesInUse' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:headingStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:numberingStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:tableStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnRuns' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnParagraphs' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnNumbering' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnTables' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:clearFormatting' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:top3HeadingStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:visibleStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:alternateStyleNames' attribute is not declared.", - "The attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:val' has invalid value '0'. The MinInclusive constraint failed. The value must be greater than or equal to 1.", - "The attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:val' has invalid value '0'. The MinInclusive constraint failed. The value must be greater than or equal to 2.", - "The 'urn:schemas-microsoft-com:office:office:gfxdata' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:fill' attribute is invalid - The value '0' is not valid according to any of the memberTypes of the union.", - }; - - } - - public class WordRunner - { - public static void RunWord(FileInfo executablePath, FileInfo docxPath) - { - if (executablePath.Exists) - { - using (Process proc = new Process()) - { - proc.StartInfo.FileName = executablePath.FullName; - proc.StartInfo.Arguments = docxPath.FullName; - proc.StartInfo.WorkingDirectory = docxPath.DirectoryName; - proc.StartInfo.UseShellExecute = false; - proc.StartInfo.RedirectStandardOutput = true; - proc.StartInfo.RedirectStandardError = true; - proc.Start(); - } - } - else - { - throw new ArgumentException("Invalid executable path.", "executablePath"); - } - } - } -} - -#endif diff --git a/OpenXmlPowerTools.Tests/WmlComparerTests2.cs b/OpenXmlPowerTools.Tests/WmlComparerTests2.cs deleted file mode 100644 index 69ad2a52..00000000 --- a/OpenXmlPowerTools.Tests/WmlComparerTests2.cs +++ /dev/null @@ -1,988 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; -using Xunit; -using System.Diagnostics; - -#if !ELIDE_XUNIT_TESTS - -namespace OxPt -{ - public class WcTests2 - { - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - public static bool m_OpenWord = false; - public static bool m_OpenTempDirInExplorer = false; - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - [Theory] - [InlineData("CZ-1000", "CZ/CZ001-Plain.docx", "CZ/CZ001-Plain-Mod.docx", 1)] - [InlineData("CZ-1010", "CZ/CZ002-Multi-Paragraphs.docx", "CZ/CZ002-Multi-Paragraphs-Mod.docx", 1)] - [InlineData("CZ-1020", "CZ/CZ003-Multi-Paragraphs.docx", "CZ/CZ003-Multi-Paragraphs-Mod.docx", 1)] - [InlineData("CZ-1030", "CZ/CZ004-Multi-Paragraphs-in-Cell.docx", "CZ/CZ004-Multi-Paragraphs-in-Cell-Mod.docx", 1)] - public void CZ001_CompareTrackedInPrev(string testId, string name1, string name2, int revisionCount) - { - // TODO: Do we need to keep the revision count parameter? - Assert.Equal(1, revisionCount); - - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (thisTestTempDir.Exists) - Assert.True(false, "Duplicate test id???"); - else - thisTestTempDir.Create(); - var source1CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name)); - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - if (m_OpenWord) - { - FileInfo source1DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name.Replace(".docx", "-For-Word.docx"))); - var source2CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name.Replace(".docx", "-For-Word.docx"))); - if (!source1CopiedToDestDocxForWord.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocxForWord.FullName); - if (!source2CopiedToDestDocxForWord.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocxForWord.FullName); - - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, source2CopiedToDestDocxForWord); - WordRunner.RunWord(wordExe, source1CopiedToDestDocxForWord); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - var before = source1CopiedToDestDocx.Name.Replace(".docx", ""); - var after = source2CopiedToDestDocx.Name.Replace(".docx", ""); - var docxWithRevisionsFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-COMPARE-" + after + ".docx")); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - settings.DebugTempFileDi = thisTestTempDir; - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - - /////////////////////////// - comparedWml.SaveAs(docxWithRevisionsFi.FullName); - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(comparedWml.DocumentByteArray, 0, comparedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); - } - var sbs = sb.ToString(); - if (sbs != "") - Assert.True(false, sbs.ToString()); - } - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - if (m_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, docxWithRevisionsFi); - } - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Open Windows Explorer - if (m_OpenTempDirInExplorer) - { - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var semaphorFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "z_ExplorerOpenedSemaphore.txt")); - if (!semaphorFi.Exists) - { - File.WriteAllText(semaphorFi.FullName, ""); - TestUtil.Explorer(thisTestTempDir); - } - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - } -#if false - WmlDocument revisionWml = new WmlDocument(docxWithRevisionsFi.FullName); - var revisions = WmlComparer.GetRevisions(revisionWml, settings); - Assert.Equal(revisionCount, revisions.Count()); -#endif - } - -#if false - [Theory] - [InlineData("CZ-2000", "CA001-Plain.docx", "CA001-Plain-Mod.docx", 1)] - [InlineData("CZ-2010", "WC001-Digits.docx", "WC001-Digits-Mod.docx", 4)] - [InlineData("CZ-2020", "WC001-Digits.docx", "WC001-Digits-Deleted-Paragraph.docx", 1)] - [InlineData("CZ-2030", "WC001-Digits-Deleted-Paragraph.docx", "WC001-Digits.docx", 1)] - [InlineData("CZ-2040", "WC002-Unmodified.docx", "WC002-DiffInMiddle.docx", 2)] - [InlineData("CZ-2050", "WC002-Unmodified.docx", "WC002-DiffAtBeginning.docx", 2)] - [InlineData("CZ-2060", "WC002-Unmodified.docx", "WC002-DeleteAtBeginning.docx", 1)] - [InlineData("CZ-2070", "WC002-Unmodified.docx", "WC002-InsertAtBeginning.docx", 1)] - [InlineData("CZ-2080", "WC002-Unmodified.docx", "WC002-InsertAtEnd.docx", 1)] - [InlineData("CZ-2080", "WC002-Unmodified.docx", "WC002-DeleteAtEnd.docx", 1)] - [InlineData("CZ-2100", "WC002-Unmodified.docx", "WC002-DeleteInMiddle.docx", 1)] - [InlineData("CZ-2110", "WC002-Unmodified.docx", "WC002-InsertInMiddle.docx", 1)] - [InlineData("CZ-2120", "WC002-DeleteInMiddle.docx", "WC002-Unmodified.docx", 1)] - //[InlineData("CZ-2130", "WC004-Large.docx", "WC004-Large-Mod.docx", 2)] - [InlineData("CZ-2140", "WC006-Table.docx", "WC006-Table-Delete-Row.docx", 1)] - [InlineData("CZ-2150", "WC006-Table-Delete-Row.docx", "WC006-Table.docx", 1)] - [InlineData("CZ-2160", "WC006-Table.docx", "WC006-Table-Delete-Contests-of-Row.docx", 2)] - [InlineData("CZ-2170", "WC007-Unmodified.docx", "WC007-Longest-At-End.docx", 2)] - [InlineData("CZ-2180", "WC007-Unmodified.docx", "WC007-Deleted-at-Beginning-of-Para.docx", 2)] - [InlineData("CZ-2200", "WC007-Unmodified.docx", "WC007-Moved-into-Table.docx", 2)] - [InlineData("CZ-2210", "WC009-Table-Unmodified.docx", "WC009-Table-Cell-1-1-Mod.docx", 1)] - [InlineData("CZ-2220", "WC010-Para-Before-Table-Unmodified.docx", "WC010-Para-Before-Table-Mod.docx", 3)] - [InlineData("CZ-2230", "WC011-Before.docx", "WC011-After.docx", 2)] - [InlineData("CZ-2240", "WC012-Math-Before.docx", "WC012-Math-After.docx", 2)] - [InlineData("CZ-2250", "WC013-Image-Before.docx", "WC013-Image-After.docx", 2)] - [InlineData("CZ-2260", "WC013-Image-Before.docx", "WC013-Image-After2.docx", 2)] - [InlineData("CZ-2270", "WC013-Image-Before2.docx", "WC013-Image-After2.docx", 2)] - [InlineData("CZ-2280", "WC014-SmartArt-Before.docx", "WC014-SmartArt-After.docx", 2)] - [InlineData("CZ-2300", "WC014-SmartArt-With-Image-Before.docx", "WC014-SmartArt-With-Image-After.docx", 2)] - [InlineData("CZ-2310", "WC014-SmartArt-With-Image-Before.docx", "WC014-SmartArt-With-Image-Deleted-After.docx", 3)] - [InlineData("CZ-2320", "WC014-SmartArt-With-Image-Before.docx", "WC014-SmartArt-With-Image-Deleted-After2.docx", 1)] - [InlineData("CZ-2330", "WC015-Three-Paragraphs.docx", "WC015-Three-Paragraphs-After.docx", 3)] - [InlineData("CZ-2340", "WC016-Para-Image-Para.docx", "WC016-Para-Image-Para-w-Deleted-Image.docx", 1)] - [InlineData("CZ-2350", "WC017-Image.docx", "WC017-Image-After.docx", 3)] - [InlineData("CZ-2360", "WC018-Field-Simple-Before.docx", "WC018-Field-Simple-After-1.docx", 2)] - [InlineData("CZ-2370", "WC018-Field-Simple-Before.docx", "WC018-Field-Simple-After-2.docx", 3)] - [InlineData("CZ-2380", "WC019-Hyperlink-Before.docx", "WC019-Hyperlink-After-1.docx", 3)] - [InlineData("CZ-2400", "WC019-Hyperlink-Before.docx", "WC019-Hyperlink-After-2.docx", 5)] - [InlineData("CZ-2410", "WC020-FootNote-Before.docx", "WC020-FootNote-After-1.docx", 3)] - [InlineData("CZ-2420", "WC020-FootNote-Before.docx", "WC020-FootNote-After-2.docx", 5)] - [InlineData("CZ-2430", "WC021-Math-Before-1.docx", "WC021-Math-After-1.docx", 9)] - [InlineData("CZ-2440", "WC021-Math-Before-2.docx", "WC021-Math-After-2.docx", 6)] - [InlineData("CZ-2450", "WC022-Image-Math-Para-Before.docx", "WC022-Image-Math-Para-After.docx", 22)] - [InlineData("CZ-2460", "WC023-Table-4-Row-Image-Before.docx", "WC023-Table-4-Row-Image-After-Delete-1-Row.docx", 9)] - [InlineData("CZ-2470", "WC024-Table-Before.docx", "WC024-Table-After.docx", 1)] - [InlineData("CZ-2480", "WC024-Table-Before.docx", "WC024-Table-After2.docx", 7)] - [InlineData("CZ-2500", "WC025-Simple-Table-Before.docx", "WC025-Simple-Table-After.docx", 4)] - [InlineData("CZ-2510", "WC026-Long-Table-Before.docx", "WC026-Long-Table-After-1.docx", 2)] - [InlineData("CZ-2520", "WC027-Twenty-Paras-Before.docx", "WC027-Twenty-Paras-After-1.docx", 2)] - [InlineData("CZ-2530", "WC027-Twenty-Paras-After-1.docx", "WC027-Twenty-Paras-Before.docx", 2)] - [InlineData("CZ-2540", "WC027-Twenty-Paras-Before.docx", "WC027-Twenty-Paras-After-2.docx", 4)] - [InlineData("CZ-2550", "WC030-Image-Math-Before.docx", "WC030-Image-Math-After.docx", 2)] - [InlineData("CZ-2560", "WC031-Two-Maths-Before.docx", "WC031-Two-Maths-After.docx", 4)] - [InlineData("CZ-2570", "WC032-Para-with-Para-Props.docx", "WC032-Para-with-Para-Props-After.docx", 3)] - [InlineData("CZ-2580", "WC033-Merged-Cells-Before.docx", "WC033-Merged-Cells-After1.docx", 2)] - [InlineData("CZ-2600", "WC033-Merged-Cells-Before.docx", "WC033-Merged-Cells-After2.docx", 4)] - [InlineData("CZ-2610", "WC034-Footnotes-Before.docx", "WC034-Footnotes-After1.docx", 1)] - [InlineData("CZ-2620", "WC034-Footnotes-Before.docx", "WC034-Footnotes-After2.docx", 6)] - [InlineData("CZ-2630", "WC034-Footnotes-Before.docx", "WC034-Footnotes-After3.docx", 3)] - [InlineData("CZ-2640", "WC034-Footnotes-After3.docx", "WC034-Footnotes-Before.docx", 3)] - [InlineData("CZ-2650", "WC035-Footnote-Before.docx", "WC035-Footnote-After.docx", 2)] - [InlineData("CZ-2660", "WC035-Footnote-After.docx", "WC035-Footnote-Before.docx", 2)] - [InlineData("CZ-2670", "WC036-Footnote-With-Table-Before.docx", "WC036-Footnote-With-Table-After.docx", 5)] - [InlineData("CZ-2680", "WC036-Footnote-With-Table-After.docx", "WC036-Footnote-With-Table-Before.docx", 5)] - [InlineData("CZ-2700", "WC034-Endnotes-Before.docx", "WC034-Endnotes-After1.docx", 1)] - [InlineData("CZ-2710", "WC034-Endnotes-Before.docx", "WC034-Endnotes-After2.docx", 6)] - [InlineData("CZ-2720", "WC034-Endnotes-Before.docx", "WC034-Endnotes-After3.docx", 8)] - [InlineData("CZ-2730", "WC034-Endnotes-After3.docx", "WC034-Endnotes-Before.docx", 8)] - [InlineData("CZ-2740", "WC035-Endnote-Before.docx", "WC035-Endnote-After.docx", 2)] - [InlineData("CZ-2750", "WC035-Endnote-After.docx", "WC035-Endnote-Before.docx", 2)] - [InlineData("CZ-2760", "WC036-Endnote-With-Table-Before.docx", "WC036-Endnote-With-Table-After.docx", 6)] - [InlineData("CZ-2770", "WC036-Endnote-With-Table-After.docx", "WC036-Endnote-With-Table-Before.docx", 6)] - [InlineData("CZ-2780", "WC038-Document-With-BR-Before.docx", "WC038-Document-With-BR-After.docx", 2)] - [InlineData("CZ-2790", "RC001-Before.docx", "RC001-After1.docx", 2)] - [InlineData("CZ-2800", "RC002-Image.docx", "RC002-Image-After1.docx", 1)] - [InlineData("CZ-2810", "WC039-Break-In-Row.docx", "WC039-Break-In-Row-After1.docx", 1)] - //[InlineData("CZ-2820", "", "", 0)] - //[InlineData("CZ-2830", "", "", 0)] - //[InlineData("CZ-2840", "", "", 0)] - //[InlineData("CZ-2850", "", "", 0)] - //[InlineData("CZ-2860", "", "", 0)] - //[InlineData("CZ-2870", "", "", 0)] - //[InlineData("CZ-2880", "", "", 0)] - //[InlineData("CZ-2890", "", "", 0)] - public void CZ002_Compare(string testId, string name1, string name2, int revisionCount) - { - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var rootTempDir = TestUtil.TempDir; - var thisTestTempDir = new DirectoryInfo(Path.Combine(rootTempDir.FullName, testId)); - if (!thisTestTempDir.Exists) - thisTestTempDir.Create(); - var source1CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name)); - if (!source1CopiedToDestDocx.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - if (!source2CopiedToDestDocx.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - /************************************************************************************************************************/ - - if (m_OpenWord) - { - FileInfo source1DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source1Docx.Name.Replace(".docx", "-For-Word.docx"))); - var source2CopiedToDestDocxForWord = new FileInfo(Path.Combine(thisTestTempDir.FullName, source2Docx.Name.Replace(".docx", "-For-Word.docx"))); - if (!source1CopiedToDestDocxForWord.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocxForWord.FullName); - if (!source2CopiedToDestDocxForWord.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocxForWord.FullName); - - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, source2CopiedToDestDocxForWord); - WordRunner.RunWord(wordExe, source1CopiedToDestDocxForWord); - } - - /************************************************************************************************************************/ - - var before = source1CopiedToDestDocx.Name.Replace(".docx", ""); - var after = source2CopiedToDestDocx.Name.Replace(".docx", ""); - var docxWithRevisionsFi = new FileInfo(Path.Combine(thisTestTempDir.FullName, before + "-COMPARE-" + after + ".docx")); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - settings.DebugTempFileDi = TestUtil.TempDir; - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - comparedWml.SaveAs(docxWithRevisionsFi.FullName); - - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(comparedWml.DocumentByteArray, 0, comparedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); - } - var sbs = sb.ToString(); - if (sbs != "") - Assert.True(false, sbs.ToString()); - } - } - } - - /************************************************************************************************************************/ - - if (m_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, docxWithRevisionsFi); - } - - /************************************************************************************************************************/ - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Open Windows Explorer - if (m_OpenTempDirInExplorer) - { - while (true) - { - try - { - ////////// CODE TO REPEAT UNTIL SUCCESS ////////// - var semaphorFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, "z_ExplorerOpenedSemaphore.txt")); - if (!semaphorFi.Exists) - { - File.WriteAllText(semaphorFi.FullName, ""); - TestUtil.Explorer(thisTestTempDir); - } - ////////////////////////////////////////////////// - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(50); - } - } - } -#if false - WmlDocument revisionWml = new WmlDocument(docxWithRevisionsFi.FullName); - var revisions = WmlComparer.GetRevisions(revisionWml, settings); - Assert.Equal(revisionCount, revisions.Count()); -#endif - } -#endif - -#if false - [Theory] - [InlineData("RC001-Before.docx", - @" - - RC001-After1.docx - LightYellow - From Bob - - - RC001-After2.docx - LightPink - From Fred - - ")] - [InlineData("RC002-Image.docx", - @" - - RC002-Image-After1.docx - LightBlue - From Bob - - ")] - [InlineData("RC002-Image-After1.docx", - @" - - RC002-Image.docx - LightBlue - From Bob - - ")] - [InlineData("WC027-Twenty-Paras-Before.docx", - @" - - WC027-Twenty-Paras-After-1.docx - LightBlue - From Bob - - ")] - [InlineData("WC027-Twenty-Paras-Before.docx", - @" - - WC027-Twenty-Paras-After-3.docx - LightBlue - From Bob - - ")] - [InlineData("RC003-Multi-Paras.docx", - @" - - RC003-Multi-Paras-After.docx - LightBlue - From Bob - - ")] - [InlineData("RC004-Before.docx", - @" - - RC004-After1.docx - LightYellow - From Bob - - - RC004-After2.docx - LightPink - From Fred - - ")] - - public void WC001_Consolidate(string originalName, string revisedDocumentsXml) - { - FileInfo originalDocx = new FileInfo(Path.Combine(sourceDir.FullName, originalName)); - - var originalCopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, originalDocx.Name)); - if (!originalCopiedToDestDocx.Exists) - File.Copy(originalDocx.FullName, originalCopiedToDestDocx.FullName); - - var revisedDocumentsXElement = XElement.Parse(revisedDocumentsXml); - var revisedDocumentsArray = revisedDocumentsXElement - .Elements() - .Select(z => - { - FileInfo revisedDocx = new FileInfo(Path.Combine(sourceDir.FullName, z.Element("DocName").Value)); - var revisedCopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, revisedDocx.Name)); - if (!revisedCopiedToDestDocx.Exists) - File.Copy(revisedDocx.FullName, revisedCopiedToDestDocx.FullName); - return new WmlRevisedDocumentInfo() - { - RevisedDocument = new WmlDocument(revisedCopiedToDestDocx.FullName), - Color = ColorParser.FromName(z.Element("Color").Value), - Revisor = z.Element("Revisor").Value, - }; - }) - .ToList(); - - var consolidatedDocxName = originalCopiedToDestDocx.Name.Replace(".docx", "-Consolidated.docx"); - var consolidatedDocumentFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, consolidatedDocxName)); - - WmlDocument source1Wml = new WmlDocument(originalCopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - WmlDocument consolidatedWml = WmlComparer.Consolidate( - source1Wml, - revisedDocumentsArray, - settings); - consolidatedWml.SaveAs(consolidatedDocumentFi.FullName); - - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(consolidatedWml.DocumentByteArray, 0, consolidatedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { -#if true - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); -#else - sb.Append(" \"" + err.Description + "\"," + Environment.NewLine); -#endif - } - var sbs = sb.ToString(); - Assert.Equal("", sbs); - } - } - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, consolidatedDocumentFi); - } - - /************************************************************************************************************************/ - } - - [Theory] - [InlineData("CA001-Plain.docx", "CA001-Plain-Mod.docx")] - [InlineData("WC001-Digits.docx", "WC001-Digits-Mod.docx")] - [InlineData("WC001-Digits.docx", "WC001-Digits-Deleted-Paragraph.docx")] - [InlineData("WC001-Digits-Deleted-Paragraph.docx", "WC001-Digits.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-DiffInMiddle.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-DiffAtBeginning.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-DeleteAtBeginning.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-InsertAtBeginning.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-InsertAtEnd.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-DeleteAtEnd.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-DeleteInMiddle.docx")] - [InlineData("WC002-Unmodified.docx", "WC002-InsertInMiddle.docx")] - [InlineData("WC002-DeleteInMiddle.docx", "WC002-Unmodified.docx")] - //[InlineData("WC004-Large.docx", "WC004-Large-Mod.docx")] - [InlineData("WC006-Table.docx", "WC006-Table-Delete-Row.docx")] - [InlineData("WC006-Table-Delete-Row.docx", "WC006-Table.docx")] - [InlineData("WC006-Table.docx", "WC006-Table-Delete-Contests-of-Row.docx")] - [InlineData("WC007-Unmodified.docx", "WC007-Longest-At-End.docx")] - [InlineData("WC007-Unmodified.docx", "WC007-Deleted-at-Beginning-of-Para.docx")] - [InlineData("WC007-Unmodified.docx", "WC007-Moved-into-Table.docx")] - [InlineData("WC009-Table-Unmodified.docx", "WC009-Table-Cell-1-1-Mod.docx")] - [InlineData("WC010-Para-Before-Table-Unmodified.docx", "WC010-Para-Before-Table-Mod.docx")] - [InlineData("WC011-Before.docx", "WC011-After.docx")] - [InlineData("WC012-Math-Before.docx", "WC012-Math-After.docx")] - [InlineData("WC013-Image-Before.docx", "WC013-Image-After.docx")] - [InlineData("WC013-Image-Before.docx", "WC013-Image-After2.docx")] - [InlineData("WC013-Image-Before2.docx", "WC013-Image-After2.docx")] - [InlineData("WC014-SmartArt-Before.docx", "WC014-SmartArt-After.docx")] - [InlineData("WC014-SmartArt-With-Image-Before.docx", "WC014-SmartArt-With-Image-After.docx")] - [InlineData("WC014-SmartArt-With-Image-Before.docx", "WC014-SmartArt-With-Image-Deleted-After.docx")] - [InlineData("WC014-SmartArt-With-Image-Before.docx", "WC014-SmartArt-With-Image-Deleted-After2.docx")] - [InlineData("WC015-Three-Paragraphs.docx", "WC015-Three-Paragraphs-After.docx")] - [InlineData("WC016-Para-Image-Para.docx", "WC016-Para-Image-Para-w-Deleted-Image.docx")] - [InlineData("WC017-Image.docx", "WC017-Image-After.docx")] - [InlineData("WC018-Field-Simple-Before.docx", "WC018-Field-Simple-After-1.docx")] - [InlineData("WC018-Field-Simple-Before.docx", "WC018-Field-Simple-After-2.docx")] - [InlineData("WC019-Hyperlink-Before.docx", "WC019-Hyperlink-After-1.docx")] - [InlineData("WC019-Hyperlink-Before.docx", "WC019-Hyperlink-After-2.docx")] - [InlineData("WC020-FootNote-Before.docx", "WC020-FootNote-After-1.docx")] - [InlineData("WC020-FootNote-Before.docx", "WC020-FootNote-After-2.docx")] - [InlineData("WC021-Math-Before-1.docx", "WC021-Math-After-1.docx")] - [InlineData("WC021-Math-Before-2.docx", "WC021-Math-After-2.docx")] - [InlineData("WC022-Image-Math-Para-Before.docx", "WC022-Image-Math-Para-After.docx")] - [InlineData("WC023-Table-4-Row-Image-Before.docx", "WC023-Table-4-Row-Image-After-Delete-1-Row.docx")] - [InlineData("WC024-Table-Before.docx", "WC024-Table-After.docx")] - [InlineData("WC024-Table-Before.docx", "WC024-Table-After2.docx")] - [InlineData("WC025-Simple-Table-Before.docx", "WC025-Simple-Table-After.docx")] - [InlineData("WC026-Long-Table-Before.docx", "WC026-Long-Table-After-1.docx")] - [InlineData("WC027-Twenty-Paras-Before.docx", "WC027-Twenty-Paras-After-1.docx")] - [InlineData("WC027-Twenty-Paras-After-1.docx", "WC027-Twenty-Paras-Before.docx")] - [InlineData("WC027-Twenty-Paras-Before.docx", "WC027-Twenty-Paras-After-2.docx")] - [InlineData("WC030-Image-Math-Before.docx", "WC030-Image-Math-After.docx")] - [InlineData("WC031-Two-Maths-Before.docx", "WC031-Two-Maths-After.docx")] - [InlineData("WC032-Para-with-Para-Props.docx", "WC032-Para-with-Para-Props-After.docx")] - [InlineData("WC033-Merged-Cells-Before.docx", "WC033-Merged-Cells-After1.docx")] - [InlineData("WC033-Merged-Cells-Before.docx", "WC033-Merged-Cells-After2.docx")] - [InlineData("WC034-Footnotes-Before.docx", "WC034-Footnotes-After1.docx")] - [InlineData("WC034-Footnotes-Before.docx", "WC034-Footnotes-After2.docx")] - [InlineData("WC034-Footnotes-Before.docx", "WC034-Footnotes-After3.docx")] - [InlineData("WC034-Footnotes-After3.docx", "WC034-Footnotes-Before.docx")] - [InlineData("WC035-Footnote-Before.docx", "WC035-Footnote-After.docx")] - [InlineData("WC035-Footnote-After.docx", "WC035-Footnote-Before.docx")] - [InlineData("WC036-Footnote-With-Table-Before.docx", "WC036-Footnote-With-Table-After.docx")] - [InlineData("WC036-Footnote-With-Table-After.docx", "WC036-Footnote-With-Table-Before.docx")] - [InlineData("WC034-Endnotes-Before.docx", "WC034-Endnotes-After1.docx")] - [InlineData("WC034-Endnotes-Before.docx", "WC034-Endnotes-After2.docx")] - [InlineData("WC034-Endnotes-Before.docx", "WC034-Endnotes-After3.docx")] - [InlineData("WC034-Endnotes-After3.docx", "WC034-Endnotes-Before.docx")] - [InlineData("WC035-Endnote-Before.docx", "WC035-Endnote-After.docx")] - [InlineData("WC035-Endnote-After.docx", "WC035-Endnote-Before.docx")] - [InlineData("WC036-Endnote-With-Table-Before.docx", "WC036-Endnote-With-Table-After.docx")] - [InlineData("WC036-Endnote-With-Table-After.docx", "WC036-Endnote-With-Table-Before.docx")] - [InlineData("WC038-Document-With-BR-Before.docx", "WC038-Document-With-BR-After.docx")] - [InlineData("RC001-Before.docx", "RC001-After1.docx")] - [InlineData("RC002-Image.docx", "RC002-Image-After1.docx")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - - - public void WC002_Consolidate_Bulk_Test(string name1, string name2) - { - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source2Docx.Name)); - if (!source1CopiedToDestDocx.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - if (!source2CopiedToDestDocx.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo source1DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocxForWord = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source1Docx.Name.Replace(".docx", "-For-Word.docx"))); - var source2CopiedToDestDocxForWord = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source2Docx.Name.Replace(".docx", "-For-Word.docx"))); - if (!source1CopiedToDestDocxForWord.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocxForWord.FullName); - if (!source2CopiedToDestDocxForWord.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocxForWord.FullName); - - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - var path = new DirectoryInfo(@"C:\Users\Eric\Documents\WindowsPowerShellModules\Open-Xml-PowerTools\TestFiles"); - WordRunner.RunWord(wordExe, source2CopiedToDestDocxForWord); - WordRunner.RunWord(wordExe, source1CopiedToDestDocxForWord); - } - - /************************************************************************************************************************/ - - var before = source1CopiedToDestDocx.Name.Replace(".docx", ""); - var after = source2CopiedToDestDocx.Name.Replace(".docx", ""); - var docxWithRevisionsFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, before + "-COMPARE-" + after + ".docx")); - var docxConsolidatedFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, before + "-CONSOLIDATED-" + after + ".docx")); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - comparedWml.SaveAs(docxWithRevisionsFi.FullName); - - List revisedDocInfo = new List() - { - new WmlRevisedDocumentInfo() - { - RevisedDocument = source2Wml, - Color = Color.LightBlue, - Revisor = "Revised by Eric White", - } - }; - WmlDocument consolidatedWml = WmlComparer.Consolidate( - source1Wml, - revisedDocInfo, - settings); - consolidatedWml.SaveAs(docxConsolidatedFi.FullName); - - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(consolidatedWml.DocumentByteArray, 0, consolidatedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { -#if true - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); -#else - sb.Append(" \"" + err.Description + "\"," + Environment.NewLine); -#endif - } - var sbs = sb.ToString(); - Assert.Equal("", sbs); - } - } - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, docxConsolidatedFi); - } - - /************************************************************************************************************************/ - } -#endif - -#if false - [Theory] - [InlineData("WC037-Textbox-Before.docx", "WC037-Textbox-After1.docx", 2)] - - public void WC003_Throws(string name1, string name2, int revisionCount) - { - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source2Docx.Name)); - if (!source1CopiedToDestDocx.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - if (!source2CopiedToDestDocx.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - Assert.Throws(() => - { - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - }); - } - - [Theory] - [InlineData("WC001-Digits.docx")] - [InlineData("WC001-Digits-Deleted-Paragraph.docx")] - [InlineData("WC001-Digits-Mod.docx")] - [InlineData("WC002-DeleteAtBeginning.docx")] - [InlineData("WC002-DeleteAtEnd.docx")] - [InlineData("WC002-DeleteInMiddle.docx")] - [InlineData("WC002-DiffAtBeginning.docx")] - [InlineData("WC002-DiffInMiddle.docx")] - [InlineData("WC002-InsertAtBeginning.docx")] - [InlineData("WC002-InsertAtEnd.docx")] - [InlineData("WC002-InsertInMiddle.docx")] - [InlineData("WC002-Unmodified.docx")] - //[InlineData("WC004-Large.docx")] - //[InlineData("WC004-Large-Mod.docx")] - [InlineData("WC006-Table.docx")] - [InlineData("WC006-Table-Delete-Contests-of-Row.docx")] - [InlineData("WC006-Table-Delete-Row.docx")] - [InlineData("WC007-Deleted-at-Beginning-of-Para.docx")] - [InlineData("WC007-Longest-At-End.docx")] - [InlineData("WC007-Moved-into-Table.docx")] - [InlineData("WC007-Unmodified.docx")] - [InlineData("WC009-Table-Cell-1-1-Mod.docx")] - [InlineData("WC009-Table-Unmodified.docx")] - [InlineData("WC010-Para-Before-Table-Mod.docx")] - [InlineData("WC010-Para-Before-Table-Unmodified.docx")] - [InlineData("WC011-After.docx")] - [InlineData("WC011-Before.docx")] - [InlineData("WC012-Math-After.docx")] - [InlineData("WC012-Math-Before.docx")] - [InlineData("WC013-Image-After.docx")] - [InlineData("WC013-Image-After2.docx")] - [InlineData("WC013-Image-Before.docx")] - [InlineData("WC013-Image-Before2.docx")] - [InlineData("WC014-SmartArt-After.docx")] - [InlineData("WC014-SmartArt-Before.docx")] - [InlineData("WC014-SmartArt-With-Image-After.docx")] - [InlineData("WC014-SmartArt-With-Image-Before.docx")] - [InlineData("WC014-SmartArt-With-Image-Deleted-After.docx")] - [InlineData("WC014-SmartArt-With-Image-Deleted-After2.docx")] - [InlineData("WC015-Three-Paragraphs.docx")] - [InlineData("WC015-Three-Paragraphs-After.docx")] - [InlineData("WC016-Para-Image-Para.docx")] - [InlineData("WC016-Para-Image-Para-w-Deleted-Image.docx")] - [InlineData("WC017-Image.docx")] - [InlineData("WC017-Image-After.docx")] - [InlineData("WC018-Field-Simple-After-1.docx")] - [InlineData("WC018-Field-Simple-After-2.docx")] - [InlineData("WC018-Field-Simple-Before.docx")] - [InlineData("WC019-Hyperlink-After-1.docx")] - [InlineData("WC019-Hyperlink-After-2.docx")] - [InlineData("WC019-Hyperlink-Before.docx")] - [InlineData("WC020-FootNote-After-1.docx")] - [InlineData("WC020-FootNote-After-2.docx")] - [InlineData("WC020-FootNote-Before.docx")] - [InlineData("WC021-Math-After-1.docx")] - [InlineData("WC021-Math-Before-1.docx")] - [InlineData("WC022-Image-Math-Para-After.docx")] - [InlineData("WC022-Image-Math-Para-Before.docx")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - //[InlineData("", "")] - - public void WC004_Compare_To_Self(string name) - { - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - - var sourceCopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-Source.docx"))); - if (!sourceCopiedToDestDocx.Exists) - File.Copy(sourceDocx.FullName, sourceCopiedToDestDocx.FullName); - - var before = sourceCopiedToDestDocx.Name.Replace(".docx", ""); - var docxComparedFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, before + "-COMPARE" + ".docx")); - var docxCompared2Fi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, before + "-COMPARE2" + ".docx")); - - WmlDocument source1Wml = new WmlDocument(sourceCopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(sourceCopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - comparedWml.SaveAs(docxComparedFi.FullName); - ValidateDocument(comparedWml); - - WmlDocument comparedWml2 = WmlComparer.Compare(comparedWml, source1Wml, settings); - comparedWml2.SaveAs(docxCompared2Fi.FullName); - ValidateDocument(comparedWml2); - } - - [Theory] - [InlineData("WC040-Case-Before.docx", "WC040-Case-After.docx", 2)] - //[InlineData("", "", 0)] - //[InlineData("", "", 0)] - //[InlineData("", "", 0)] - //[InlineData("", "", 0)] - //[InlineData("", "", 0)] - //[InlineData("", "", 0)] - //[InlineData("", "", 0)] - //[InlineData("", "", 0)] - - public void WC005_Compare_CaseInsensitive(string name1, string name2, int revisionCount) - { - FileInfo source1Docx = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2Docx = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source1Docx.Name)); - var source2CopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source2Docx.Name)); - if (!source1CopiedToDestDocx.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocx.FullName); - if (!source2CopiedToDestDocx.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocx.FullName); - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo source1DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name1)); - FileInfo source2DocxForWord = new FileInfo(Path.Combine(sourceDir.FullName, name2)); - - var source1CopiedToDestDocxForWord = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source1Docx.Name.Replace(".docx", "-For-Word.docx"))); - var source2CopiedToDestDocxForWord = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, source2Docx.Name.Replace(".docx", "-For-Word.docx"))); - if (!source1CopiedToDestDocxForWord.Exists) - File.Copy(source1Docx.FullName, source1CopiedToDestDocxForWord.FullName); - if (!source2CopiedToDestDocxForWord.Exists) - File.Copy(source2Docx.FullName, source2CopiedToDestDocxForWord.FullName); - - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - var path = new DirectoryInfo(@"C:\Users\Eric\Documents\WindowsPowerShellModules\Open-Xml-PowerTools\TestFiles"); - WordRunner.RunWord(wordExe, source2CopiedToDestDocxForWord); - WordRunner.RunWord(wordExe, source1CopiedToDestDocxForWord); - } - - /************************************************************************************************************************/ - - var before = source1CopiedToDestDocx.Name.Replace(".docx", ""); - var after = source2CopiedToDestDocx.Name.Replace(".docx", ""); - var docxWithRevisionsFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, before + "-COMPARE-" + after + ".docx")); - - WmlDocument source1Wml = new WmlDocument(source1CopiedToDestDocx.FullName); - WmlDocument source2Wml = new WmlDocument(source2CopiedToDestDocx.FullName); - WmlComparerSettings settings = new WmlComparerSettings(); - settings.CaseInsensitive = true; - settings.CultureInfo = System.Globalization.CultureInfo.CurrentCulture; - WmlDocument comparedWml = WmlComparer.Compare(source1Wml, source2Wml, settings); - comparedWml.SaveAs(docxWithRevisionsFi.FullName); - - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(comparedWml.DocumentByteArray, 0, comparedWml.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() > 0) - { - - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); - } - var sbs = sb.ToString(); - Assert.Equal("", sbs); - } - } - } - - /************************************************************************************************************************/ - - if (s_OpenWord) - { - FileInfo wordExe = new FileInfo(@"C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE"); - WordRunner.RunWord(wordExe, docxWithRevisionsFi); - } - - /************************************************************************************************************************/ - - WmlDocument revisionWml = new WmlDocument(docxWithRevisionsFi.FullName); - var revisions = WmlComparer.GetRevisions(revisionWml, settings); - Assert.Equal(revisionCount, revisions.Count()); - } -#endif - - private static void ValidateDocument(WmlDocument wmlToValidate) - { - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(wmlToValidate.DocumentByteArray, 0, wmlToValidate.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - OpenXmlValidator validator = new OpenXmlValidator(); - var errors = validator.Validate(wDoc).Where(e => !ExpectedErrors.Contains(e.Description)); - if (errors.Count() != 0) - { - var ind = " "; - var sb = new StringBuilder(); - foreach (var err in errors) - { - sb.Append("Error" + Environment.NewLine); - sb.Append(ind + "ErrorType: " + err.ErrorType.ToString() + Environment.NewLine); - sb.Append(ind + "Description: " + err.Description + Environment.NewLine); - sb.Append(ind + "Part: " + err.Part.Uri.ToString() + Environment.NewLine); - sb.Append(ind + "XPath: " + err.Path.XPath + Environment.NewLine); - } - var sbs = sb.ToString(); - Assert.Equal("", sbs); - } - } - } - } - - public static string[] ExpectedErrors = new string[] { - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstRow' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastRow' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:firstColumn' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:lastColumn' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noHBand' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:noVBand' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:allStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:customStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:latentStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:stylesInUse' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:headingStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:numberingStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:tableStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnRuns' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnParagraphs' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnNumbering' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:directFormattingOnTables' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:clearFormatting' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:top3HeadingStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:visibleStyles' attribute is not declared.", - "The 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:alternateStyleNames' attribute is not declared.", - "The attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:val' has invalid value '0'. The MinInclusive constraint failed. The value must be greater than or equal to 1.", - "The attribute 'http://schemas.openxmlformats.org/wordprocessingml/2006/main:val' has invalid value '0'. The MinInclusive constraint failed. The value must be greater than or equal to 2.", - }; - - } -#if false - public class WordRunner - { - public static void RunWord(FileInfo executablePath, FileInfo docxPath) - { - if (executablePath.Exists) - { - using (Process proc = new Process()) - { - proc.StartInfo.FileName = executablePath.FullName; - proc.StartInfo.Arguments = docxPath.FullName; - proc.StartInfo.WorkingDirectory = docxPath.DirectoryName; - proc.StartInfo.UseShellExecute = false; - proc.StartInfo.RedirectStandardOutput = true; - proc.StartInfo.RedirectStandardError = true; - proc.Start(); - } - } - else - { - throw new ArgumentException("Invalid executable path.", "executablePath"); - } - } - } -#endif -} - -#endif diff --git a/OpenXmlPowerTools.Tests/WmlContentAtomListTests.cs b/OpenXmlPowerTools.Tests/WmlContentAtomListTests.cs deleted file mode 100644 index 889f9bb0..00000000 --- a/OpenXmlPowerTools.Tests/WmlContentAtomListTests.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#define COPY_FILES_FOR_DEBUGGING - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using Xunit; - -#if !ELIDE_XUNIT_TESTS - -namespace OxPt -{ - public class CaTests - { - /* - * This test was removed because it depends on the Coalesce method, which is only ever used - * by this test. - * - [Theory] - [InlineData("CA/CA001-Plain.docx", 60)] - [InlineData("CA/CA002-Bookmark.docx", 7)] - [InlineData("CA/CA003-Numbered-List.docx", 8)] - [InlineData("CA/CA004-TwoParas.docx", 88)] - [InlineData("CA/CA005-Table.docx", 27)] - [InlineData("CA/CA006-ContentControl.docx", 60)] - [InlineData("CA/CA007-DayLong.docx", 10)] - [InlineData("CA/CA008-Footnote-Reference.docx", 23)] - [InlineData("CA/CA010-Delete-Run.docx", 16)] - [InlineData("CA/CA011-Insert-Run.docx", 16)] - [InlineData("CA/CA012-fldSimple.docx", 10)] - [InlineData("CA/CA013-Lots-of-Stuff.docx", 168)] - [InlineData("CA/CA014-Complex-Table.docx", 193)] - [InlineData("WC/WC024-Table-Before.docx", 24)] - [InlineData("WC/WC024-Table-After2.docx", 18)] - //[InlineData("", 0)] - //[InlineData("", 0)] - //[InlineData("", 0)] - //[InlineData("", 0)] - - public void CA001_ContentAtoms(string name, int contentAtomCount) - { - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - - var thisGuid = Guid.NewGuid().ToString().Replace("-", ""); - var sourceCopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", string.Format("-{0}-1-Source.docx", thisGuid)))); - if (!sourceCopiedToDestDocx.Exists) - File.Copy(sourceDocx.FullName, sourceCopiedToDestDocx.FullName); - - var coalescedDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", string.Format("-{0}-2-Coalesced.docx", thisGuid)))); - if (!coalescedDocx.Exists) - File.Copy(sourceDocx.FullName, coalescedDocx.FullName); - - var contentAtomDataFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", string.Format("-{0}-3-ContentAtomData.txt", thisGuid)))); - - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(coalescedDocx.FullName, true)) - { - var contentParent = wDoc.MainDocumentPart.GetXDocument().Root.Element(W.body); - var settings = new WmlComparerSettings(); - ComparisonUnitAtom[] contentAtomList = WmlComparer.CreateComparisonUnitAtomList(wDoc.MainDocumentPart, contentParent, settings); - StringBuilder sb = new StringBuilder(); - var part = wDoc.MainDocumentPart; - - sb.AppendFormat("Part: {0}", part.Uri.ToString()); - sb.Append(Environment.NewLine); - sb.Append(ComparisonUnit.ComparisonUnitListToString(contentAtomList.ToArray()) + Environment.NewLine); - sb.Append(Environment.NewLine); - - XDocument newMainXDoc = WmlComparer.Coalesce(contentAtomList); - var partXDoc = wDoc.MainDocumentPart.GetXDocument(); - partXDoc.Root.ReplaceWith(newMainXDoc.Root); - wDoc.MainDocumentPart.PutXDocument(); - - File.WriteAllText(contentAtomDataFi.FullName, sb.ToString()); - - Assert.Equal(contentAtomCount, contentAtomList.Count()); - } - } - */ - - [Theory] - [InlineData("HC009-Test-04.docx")] - public void CA002_Annotations(string name) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - -#if COPY_FILES_FOR_DEBUGGING - var sourceCopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-1-Source.docx"))); - if (!sourceCopiedToDestDocx.Exists) - File.Copy(sourceDocx.FullName, sourceCopiedToDestDocx.FullName); - - var annotatedDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", "-2-Annotated.docx"))); - if (!annotatedDocx.Exists) - File.Copy(sourceDocx.FullName, annotatedDocx.FullName); - - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(annotatedDocx.FullName, true)) - { - var contentParent = wDoc.MainDocumentPart.GetXDocument().Root.Element(W.body); - var settings = new WmlComparerSettings(); - WmlComparer.CreateComparisonUnitAtomList(wDoc.MainDocumentPart, contentParent, settings); - } -#endif - } - - [Theory] - [InlineData("CA/CA009-altChunk.docx")] - //[InlineData("")] - //[InlineData("")] - //[InlineData("")] - - public void CA003_ContentAtoms_Throws(string name) - { - DirectoryInfo sourceDir = new DirectoryInfo("../../../../TestFiles/"); - FileInfo sourceDocx = new FileInfo(Path.Combine(sourceDir.FullName, name)); - var thisGuid = Guid.NewGuid().ToString().Replace("-", ""); - var sourceCopiedToDestDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", string.Format("-{0}-1-Source.docx", thisGuid)))); - if (!sourceCopiedToDestDocx.Exists) - File.Copy(sourceDocx.FullName, sourceCopiedToDestDocx.FullName); - - var coalescedDocx = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", string.Format("-{0}-2-Coalesced.docx", thisGuid)))); - if (!coalescedDocx.Exists) - File.Copy(sourceDocx.FullName, coalescedDocx.FullName); - - var contentAtomDataFi = new FileInfo(Path.Combine(TestUtil.TempDir.FullName, sourceDocx.Name.Replace(".docx", string.Format("-{0}-3-ContentAtomData.txt", thisGuid)))); - - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(coalescedDocx.FullName, true)) - { - Assert.Throws(() => - { - var contentParent = wDoc.MainDocumentPart.GetXDocument().Root.Element(W.body); - var settings = new WmlComparerSettings(); - WmlComparer.CreateComparisonUnitAtomList(wDoc.MainDocumentPart, contentParent, settings); - }); - } - } - } -} - -#endif diff --git a/OpenXmlPowerTools.sln b/OpenXmlPowerTools.sln index 806dc01e..57fdb2bd 100644 --- a/OpenXmlPowerTools.sln +++ b/OpenXmlPowerTools.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenXmlPowerTools", "OpenXmlPowerTools\OpenXmlPowerTools.csproj", "{6F957FF3-AFCC-4D69-8FBC-71AE21BC45C9}" EndProject @@ -25,18 +25,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlConverter01", "OpenXmlP EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ListItemRetriever01", "OpenXmlPowerToolsExamples\ListItemRetriever01\ListItemRetriever01.csproj", "{04935FA6-E48B-496F-8D6A-41A59232303D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MarkupSimplifierApp", "OpenXmlPowerToolsExamples\MarkupSimplifierApp\MarkupSimplifierApp.csproj", "{0CCE83BA-8B8B-4B98-8846-B62A61FDF170}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MetricsGetter01", "OpenXmlPowerToolsExamples\MetricsGetter01\MetricsGetter01.csproj", "{ED37372C-DFBF-4720-BD4B-CE1415961038}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenXmlRegex01", "OpenXmlPowerToolsExamples\OpenXmlRegex01\OpenXmlRegex01.csproj", "{4330D46F-6703-4BA2-844D-177F722C0820}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PivotTables01", "OpenXmlPowerToolsExamples\PivotTables01\PivotTables01.csproj", "{6785A554-BBBB-480C-8E4D-9FE90DD91A46}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresentationBuilder01", "OpenXmlPowerToolsExamples\PresentationBuilder01\PresentationBuilder01.csproj", "{A4128935-B707-4D56-B924-27910EC92664}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresentationBuilder02", "OpenXmlPowerToolsExamples\PresentationBuilder02\PresentationBuilder02.csproj", "{495060B4-BD80-4B18-AC44-4680F0D6D5DB}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReferenceAdder01", "OpenXmlPowerToolsExamples\ReferenceAdder01\ReferenceAdder01.csproj", "{211F05D3-F8EE-497F-9DE9-AFFA0A72140D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevisionAccepter01", "OpenXmlPowerToolsExamples\RevisionAccepter01\RevisionAccepter01.csproj", "{84823BA5-B860-4CAF-885E-981D7F121830}" @@ -69,22 +63,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WmlToHtmlConverter02", "Ope EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmlDataRetriever01", "OpenXmlPowerToolsExamples\SmlDataRetriever01\SmlDataRetriever01.csproj", "{DCE8EC51-1E58-49A0-82CF-5BE269FA0A9D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WmlComparer01", "OpenXmlPowerToolsExamples\WmlComparer01\WmlComparer01.csproj", "{C9CAA69C-575A-442E-9D8A-69C53B39D72C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WmlComparer02", "OpenXmlPowerToolsExamples\WmlComparer02\WmlComparer02.csproj", "{3D1C46E1-7A9E-4A4B-8D95-68613603DEDF}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{A83D6B58-6D38-46AF-8C20-5CFC170A1063}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9AFB8C96-1E6E-483E-9882-75D2483E7076}" ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - LICENSE.txt = LICENSE.txt + .editorconfig = .editorconfig + .github\workflows\cla.yml = .github\workflows\cla.yml + .github\dependabot.yml = .github\dependabot.yml + .github\workflows\dotnet.yml = .github\workflows\dotnet.yml + LICENSE.md = LICENSE.md README.md = README.md - rules.ruleset = rules.ruleset - stylecop.json = stylecop.json + .github\workflows\stale.yml = .github\workflows\stale.yml EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MarkupSimplifierApp", "OpenXmlPowerToolsExamples\MarkupSimplifierApp\MarkupSimplifierApp.csproj", "{6731E031-9C81-48FB-97A7-0E945993BCE2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -135,10 +128,6 @@ Global {04935FA6-E48B-496F-8D6A-41A59232303D}.Debug|Any CPU.Build.0 = Debug|Any CPU {04935FA6-E48B-496F-8D6A-41A59232303D}.Release|Any CPU.ActiveCfg = Release|Any CPU {04935FA6-E48B-496F-8D6A-41A59232303D}.Release|Any CPU.Build.0 = Release|Any CPU - {0CCE83BA-8B8B-4B98-8846-B62A61FDF170}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0CCE83BA-8B8B-4B98-8846-B62A61FDF170}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0CCE83BA-8B8B-4B98-8846-B62A61FDF170}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0CCE83BA-8B8B-4B98-8846-B62A61FDF170}.Release|Any CPU.Build.0 = Release|Any CPU {ED37372C-DFBF-4720-BD4B-CE1415961038}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED37372C-DFBF-4720-BD4B-CE1415961038}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED37372C-DFBF-4720-BD4B-CE1415961038}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -151,14 +140,6 @@ Global {6785A554-BBBB-480C-8E4D-9FE90DD91A46}.Debug|Any CPU.Build.0 = Debug|Any CPU {6785A554-BBBB-480C-8E4D-9FE90DD91A46}.Release|Any CPU.ActiveCfg = Release|Any CPU {6785A554-BBBB-480C-8E4D-9FE90DD91A46}.Release|Any CPU.Build.0 = Release|Any CPU - {A4128935-B707-4D56-B924-27910EC92664}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4128935-B707-4D56-B924-27910EC92664}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4128935-B707-4D56-B924-27910EC92664}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4128935-B707-4D56-B924-27910EC92664}.Release|Any CPU.Build.0 = Release|Any CPU - {495060B4-BD80-4B18-AC44-4680F0D6D5DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {495060B4-BD80-4B18-AC44-4680F0D6D5DB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {495060B4-BD80-4B18-AC44-4680F0D6D5DB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {495060B4-BD80-4B18-AC44-4680F0D6D5DB}.Release|Any CPU.Build.0 = Release|Any CPU {211F05D3-F8EE-497F-9DE9-AFFA0A72140D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {211F05D3-F8EE-497F-9DE9-AFFA0A72140D}.Debug|Any CPU.Build.0 = Debug|Any CPU {211F05D3-F8EE-497F-9DE9-AFFA0A72140D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -223,14 +204,10 @@ Global {DCE8EC51-1E58-49A0-82CF-5BE269FA0A9D}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCE8EC51-1E58-49A0-82CF-5BE269FA0A9D}.Release|Any CPU.ActiveCfg = Release|Any CPU {DCE8EC51-1E58-49A0-82CF-5BE269FA0A9D}.Release|Any CPU.Build.0 = Release|Any CPU - {C9CAA69C-575A-442E-9D8A-69C53B39D72C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9CAA69C-575A-442E-9D8A-69C53B39D72C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9CAA69C-575A-442E-9D8A-69C53B39D72C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9CAA69C-575A-442E-9D8A-69C53B39D72C}.Release|Any CPU.Build.0 = Release|Any CPU - {3D1C46E1-7A9E-4A4B-8D95-68613603DEDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3D1C46E1-7A9E-4A4B-8D95-68613603DEDF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3D1C46E1-7A9E-4A4B-8D95-68613603DEDF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3D1C46E1-7A9E-4A4B-8D95-68613603DEDF}.Release|Any CPU.Build.0 = Release|Any CPU + {6731E031-9C81-48FB-97A7-0E945993BCE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6731E031-9C81-48FB-97A7-0E945993BCE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6731E031-9C81-48FB-97A7-0E945993BCE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6731E031-9C81-48FB-97A7-0E945993BCE2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -246,12 +223,9 @@ Global {618B95DB-3A70-4DC8-BD87-00F636F72453} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {BC9E7408-508D-482D-8602-96E76ACBB5EA} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {04935FA6-E48B-496F-8D6A-41A59232303D} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} - {0CCE83BA-8B8B-4B98-8846-B62A61FDF170} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {ED37372C-DFBF-4720-BD4B-CE1415961038} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {4330D46F-6703-4BA2-844D-177F722C0820} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {6785A554-BBBB-480C-8E4D-9FE90DD91A46} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} - {A4128935-B707-4D56-B924-27910EC92664} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} - {495060B4-BD80-4B18-AC44-4680F0D6D5DB} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {211F05D3-F8EE-497F-9DE9-AFFA0A72140D} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {84823BA5-B860-4CAF-885E-981D7F121830} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {5CA29B44-C4A5-4310-9429-553F0BC9FC1D} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} @@ -267,8 +241,7 @@ Global {396D9209-0D9A-4E61-9471-04C71F6CA6B9} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {D4078011-2611-46A7-8A30-55E4AB8FA786} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} {DCE8EC51-1E58-49A0-82CF-5BE269FA0A9D} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} - {C9CAA69C-575A-442E-9D8A-69C53B39D72C} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} - {3D1C46E1-7A9E-4A4B-8D95-68613603DEDF} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} + {6731E031-9C81-48FB-97A7-0E945993BCE2} = {A83D6B58-6D38-46AF-8C20-5CFC170A1063} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E623EFF5-2CA4-4FA0-B3AB-53F921DA212E} diff --git a/OpenXmlPowerTools/Chart/ChartData.cs b/OpenXmlPowerTools/Chart/ChartData.cs new file mode 100644 index 00000000..9908475d --- /dev/null +++ b/OpenXmlPowerTools/Chart/ChartData.cs @@ -0,0 +1,13 @@ +namespace Codeuctivity.OpenXmlPowerTools.Chart +{ + public class ChartData + { + public string[] SeriesNames { get; set; } + + public ChartDataType CategoryDataType { get; set; } + public int CategoryFormatCode { get; set; } + public string[] CategoryNames { get; set; } + + public double[][] Values { get; set; } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/Chart/ChartDataType.cs b/OpenXmlPowerTools/Chart/ChartDataType.cs new file mode 100644 index 00000000..32c6de53 --- /dev/null +++ b/OpenXmlPowerTools/Chart/ChartDataType.cs @@ -0,0 +1,9 @@ +namespace Codeuctivity.OpenXmlPowerTools.Chart +{ + public enum ChartDataType + { + Number, + String, + DateTime, + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/ChartUpdater.cs b/OpenXmlPowerTools/Chart/ChartUpdater.cs similarity index 75% rename from OpenXmlPowerTools/ChartUpdater.cs rename to OpenXmlPowerTools/Chart/ChartUpdater.cs index 79b0f74b..2d6c7842 100644 --- a/OpenXmlPowerTools/ChartUpdater.cs +++ b/OpenXmlPowerTools/Chart/ChartUpdater.cs @@ -1,65 +1,11 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools.Chart { - public enum ChartDataType - { - Number, - String, - DateTime, - } - - // Format Codes - // 0 - general - // 1 - 0 - // 2 - 0.00 - // 3 - #,##0 - // 4 - #,##0.00 - // 9 - 0% - // 10 - 0.00% - // 11 - 0.00E+00 - // 12 - # ?/? - // 13 - # ??/?? - // 14 - mm-dd-yy - // 15 - d-mmm-yy - // 16 - d-mmm - // 17 - mmm-yy - // 18 - h:mm AM/PM - // 19 - h:mm:ss AM/PM - // 20 - h:mm - // 21 - h:mm:ss - // 22 - m/d/yy h:mm - // 37 - #,##0 ;(#,##0) - // 38 - #,##0 ;[Red](#,##0) - // 39 - #,##0.00;(#,##0.00) - // 40 - #,##0.00;[Red](#,##0.00) - // 45 - mm:ss - // 46 - [h]:mm:ss - // 47 - mmss.0 - // 48 - ##0.0E+0 - // 49 - @ - - public class ChartData - { - public string[] SeriesNames; - - public ChartDataType CategoryDataType; - public int CategoryFormatCode; - public string[] CategoryNames; - - public double[][] Values; - } - public class ChartUpdater { public static bool UpdateChart(WordprocessingDocument wDoc, string contentControlTag, ChartData chartData) @@ -73,7 +19,7 @@ public static bool UpdateChart(WordprocessingDocument wDoc, string contentContro var chartRid = (string)cc.Descendants(C.chart).Attributes(R.id).FirstOrDefault(); if (chartRid != null) { - ChartPart chartPart = (ChartPart)mainDocumentPart.GetPartById(chartRid); + var chartPart = (ChartPart)mainDocumentPart.GetPartById(chartRid); UpdateChart(chartPart, chartData); var newContent = cc.Elements(W.sdtContent).Elements().Select(e => new XElement(e)); cc.ReplaceWith(newContent); @@ -84,20 +30,47 @@ public static bool UpdateChart(WordprocessingDocument wDoc, string contentContro return false; } + public static bool UpdateChart(PresentationDocument pDoc, int slideNumber, ChartData chartData) + { + var presentationPart = pDoc.PresentationPart; + var pXDoc = presentationPart.GetXDocument(); + var sldIdElement = pXDoc.Root.Elements(P.sldIdLst).Elements(P.sldId).Skip(slideNumber - 1).FirstOrDefault(); + if (sldIdElement != null) + { + var rId = (string)sldIdElement.Attribute(R.id); + var slidePart = presentationPart.GetPartById(rId); + var sXDoc = slidePart.GetXDocument(); + var chartRid = (string)sXDoc.Descendants(C.chart).Attributes(R.id).FirstOrDefault(); + if (chartRid != null) + { + var chartPart = (ChartPart)slidePart.GetPartById(chartRid); + UpdateChart(chartPart, chartData); + return true; + } + return true; + } + return false; + } + public static void UpdateChart(ChartPart chartPart, ChartData chartData) { if (chartData.Values.Length != chartData.SeriesNames.Length) + { throw new ArgumentException("Invalid chart data"); + } + foreach (var ser in chartData.Values) { if (ser.Length != chartData.CategoryNames.Length) + { throw new ArgumentException("Invalid chart data"); + } } UpdateSeries(chartPart, chartData); } - private static Dictionary FormatCodes = new Dictionary() + private static readonly Dictionary FormatCodes = new Dictionary() { { 0, "general" }, { 1, "0" }, @@ -133,34 +106,41 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData) { UpdateEmbeddedWorkbook(chartPart, chartData); - XDocument cpXDoc = chartPart.GetXDocument(); - XElement root = cpXDoc.Root; + var cpXDoc = chartPart.GetXDocument(); + var root = cpXDoc.Root; var firstSeries = root.Descendants(C.ser).FirstOrDefault(); var numRef = firstSeries.Elements(C.val).Elements(C.numRef).FirstOrDefault(); - string sheetName = null; + string? sheetName = null; var f = (string)firstSeries.Descendants(C.f).FirstOrDefault(); if (f != null) + { sheetName = f.Split('!')[0]; + } // remove all but first series - XName chartType = firstSeries.Parent.Name; + var chartType = firstSeries.Parent.Name; firstSeries.Parent.Elements(C.ser).Skip(1).Remove(); var newSetOfSeries = chartData.SeriesNames - .Select((string sn, int si) => + .Select((sn, si) => { - XElement cat = null; + XElement? cat = null; var oldCat = firstSeries.Elements(C.cat).FirstOrDefault(); if (oldCat == null) + { throw new OpenXmlPowerToolsException("Invalid chart markup"); + } var catHasFormula = oldCat.Descendants(C.f).Any(); if (catHasFormula) { - XElement newFormula = null; + XElement? newFormula = null; if (sheetName != null) + { newFormula = new XElement(C.f, string.Format("{0}!$A$2:$A${1}", sheetName, chartData.CategoryNames.Length + 1)); + } + if (chartData.CategoryDataType == ChartDataType.String) { cat = new XElement(C.cat, @@ -223,7 +203,7 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData) } } - XElement newCval = null; + XElement? newCval = null; if (sheetName == null) { @@ -257,12 +237,15 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData) } var serHasFormula = firstSeries.Descendants(C.f).Any(); - XElement tx = null; + XElement? tx = null; if (serHasFormula) { - XElement newFormula = null; + XElement? newFormula = null; if (sheetName != null) + { newFormula = new XElement(C.f, string.Format("{0}!${1}$1", sheetName, SpreadsheetMLUtil.IntToColumnId(si + 1))); + } + tx = new XElement(C.tx, new XElement(C.strRef, newFormula, @@ -278,7 +261,7 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData) new XElement(C.v, chartData.SeriesNames[si])); } - XElement newSer = null; + XElement? newSer = null; if (chartType == C.area3DChart || chartType == C.areaChart) { @@ -373,9 +356,11 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData) } if (newSer == null) + { throw new OpenXmlPowerToolsException("Unsupported chart type"); + } - int accentNumber = (si % 6) + 1; + var accentNumber = si % 6 + 1; newSer = (XElement)UpdateAccentTransform(newSer, accentNumber); return newSer; }); @@ -385,120 +370,133 @@ private static void UpdateSeries(ChartPart chartPart, ChartData chartData) private static void UpdateEmbeddedWorkbook(ChartPart chartPart, ChartData chartData) { - XDocument cpXDoc = chartPart.GetXDocument(); - XElement root = cpXDoc.Root; + var cpXDoc = chartPart.GetXDocument(); + var root = cpXDoc.Root; var firstSeries = root.Descendants(C.ser).FirstOrDefault(); if (firstSeries == null) + { return; + } + var firstFormula = (string)firstSeries.Descendants(C.f).FirstOrDefault(); if (firstFormula == null) + { return; + } + var sheet = firstFormula.Split('!')[0]; var embeddedSpreadsheetRid = (string)root.Descendants(C.externalData).Attributes(R.id).FirstOrDefault(); if (embeddedSpreadsheetRid == null) + { return; + } + var embeddedSpreadsheet = chartPart.GetPartById(embeddedSpreadsheetRid); if (embeddedSpreadsheet != null) { - using (SpreadsheetDocument sDoc = SpreadsheetDocument.Open(embeddedSpreadsheet.GetStream(), true)) + using var sDoc = SpreadsheetDocument.Open(embeddedSpreadsheet.GetStream(), true); + var workbookPart = sDoc.WorkbookPart; + var wbRoot = workbookPart.GetXDocument().Root; + var sheetRid = (string)wbRoot + .Elements(S.sheets) + .Elements(S.sheet) + .Where(s => (string)s.Attribute("name") == sheet) + .Attributes(R.id) + .FirstOrDefault(); + if (sheetRid != null) { - var workbookPart = sDoc.WorkbookPart; - var wbRoot = workbookPart.GetXDocument().Root; - var sheetRid = (string)wbRoot - .Elements(S.sheets) - .Elements(S.sheet) - .Where(s => (string)s.Attribute("name") == sheet) - .Attributes(R.id) - .FirstOrDefault(); - if (sheetRid != null) + var sheetPart = workbookPart.GetPartById(sheetRid); + var xdSheet = sheetPart.GetXDocument(); + var sheetData = xdSheet.Descendants(S.sheetData).FirstOrDefault(); + + var stylePart = workbookPart.WorkbookStylesPart; + var xdStyle = stylePart.GetXDocument(); + + var categoryStyleId = 0; + if (chartData.CategoryFormatCode != 0) { - var sheetPart = workbookPart.GetPartById(sheetRid); - var xdSheet = sheetPart.GetXDocument(); - var sheetData = xdSheet.Descendants(S.sheetData).FirstOrDefault(); - - var stylePart = workbookPart.WorkbookStylesPart; - var xdStyle = stylePart.GetXDocument(); - - int categoryStyleId = 0; - if (chartData.CategoryFormatCode != 0) - categoryStyleId = AddDxfToDxfs(xdSheet, xdStyle, chartData.CategoryFormatCode); - stylePart.PutXDocument(); - - var firstRow = new XElement(S.row, - new XAttribute("r", "1"), - new XAttribute("spans", string.Format("1:{0}", chartData.SeriesNames.Length + 1)), - new [] { new XElement(S.c, + categoryStyleId = AddDxfToDxfs(xdSheet, xdStyle, chartData.CategoryFormatCode); + } + + stylePart.PutXDocument(); + + var firstRow = new XElement(S.row, + new XAttribute("r", "1"), + new XAttribute("spans", string.Format("1:{0}", chartData.SeriesNames.Length + 1)), + new[] { new XElement(S.c, new XAttribute("r", "A1"), new XAttribute("t", "str"), new XElement(S.v, new XAttribute(XNamespace.Xml + "space", "preserve"), " "))} - .Concat( - chartData.SeriesNames - .Select((sn, i) => new XElement(S.c, - new XAttribute("r", RowColToString(0, i + 1)), - new XAttribute("t", "str"), - new XElement(S.v, sn))))); - var otherRows = chartData - .CategoryNames - .Select((cn, r) => - { - var row = new XElement(S.row, - new XAttribute("r", r + 2), - new XAttribute("spans", string.Format("1:{0}", chartData.SeriesNames.Length + 1)), - new[] { + .Concat( + chartData.SeriesNames + .Select((sn, i) => new XElement(S.c, + new XAttribute("r", RowColToString(0, i + 1)), + new XAttribute("t", "str"), + new XElement(S.v, sn))))); + var otherRows = chartData + .CategoryNames + .Select((cn, r) => + { + var row = new XElement(S.row, + new XAttribute("r", r + 2), + new XAttribute("spans", string.Format("1:{0}", chartData.SeriesNames.Length + 1)), + new[] { new XElement(S.c, new XAttribute("r", RowColToString(r + 1, 0)), categoryStyleId != 0 ? new XAttribute("s", categoryStyleId) : null, chartData.CategoryDataType == ChartDataType.String ? new XAttribute("t", "str") : null, new XElement(S.v, cn)) - }.Concat( - Enumerable.Range(0, chartData.Values.Length) - .Select((c, ci) => - { - var cell = new XElement(S.c, - new XAttribute("r", RowColToString(r + 1, ci + 1)), - new XElement(S.v, chartData.Values[ci][r])); - return cell; - }))); - return row; - }); - var allRows = new[] { + }.Concat( + Enumerable.Range(0, chartData.Values.Length) + .Select((c, ci) => + { + var cell = new XElement(S.c, + new XAttribute("r", RowColToString(r + 1, ci + 1)), + new XElement(S.v, chartData.Values[ci][r])); + return cell; + }))); + return row; + }); + var allRows = new[] { firstRow }.Concat(otherRows); - var newSheetData = new XElement(S.sheetData, - allRows); - sheetData.ReplaceWith(newSheetData); - sheetPart.PutXDocument(); - - var tablePartRid = (string)xdSheet - .Root - .Elements(S.tableParts) - .Elements(S.tablePart) - .Attributes(R.id) - .FirstOrDefault(); - if (tablePartRid != null) - { - var partTable = sheetPart.GetPartById(tablePartRid); - var xdTablePart = partTable.GetXDocument(); - var xaRef = xdTablePart.Root.Attribute("ref"); - xaRef.Value = string.Format("A1:{0}", RowColToString(chartData.CategoryNames.Length - 1, chartData.SeriesNames.Length)); - var xeNewTableColumns = new XElement(S.tableColumns, - new XAttribute("count", chartData.SeriesNames.Count() + 1), - new[] { + var newSheetData = new XElement(S.sheetData, + allRows); + sheetData.ReplaceWith(newSheetData); + sheetPart.PutXDocument(); + + var tablePartRid = (string)xdSheet + .Root + .Elements(S.tableParts) + .Elements(S.tablePart) + .Attributes(R.id) + .FirstOrDefault(); + if (tablePartRid != null) + { + var partTable = sheetPart.GetPartById(tablePartRid); + var xdTablePart = partTable.GetXDocument(); + var xaRef = xdTablePart.Root.Attribute("ref"); + xaRef.Value = string.Format("A1:{0}", RowColToString(chartData.CategoryNames.Length - 1, chartData.SeriesNames.Length)); + var xeNewTableColumns = new XElement(S.tableColumns, + new XAttribute("count", chartData.SeriesNames.Length + 1), + new[] { new XElement(S.tableColumn, new XAttribute("id", 1), new XAttribute("name", " ")) - }.Concat( - chartData.SeriesNames.Select((cn, ci) => - new XElement(S.tableColumn, - new XAttribute("id", ci + 2), - new XAttribute("name", cn))))); - var xeExistingTableColumns = xdTablePart.Root.Element(S.tableColumns); - if (xeExistingTableColumns != null) - xeExistingTableColumns.ReplaceWith(xeNewTableColumns); - partTable.PutXDocument(); + }.Concat( + chartData.SeriesNames.Select((cn, ci) => + new XElement(S.tableColumn, + new XAttribute("id", ci + 2), + new XAttribute("name", cn))))); + var xeExistingTableColumns = xdTablePart.Root.Element(S.tableColumns); + if (xeExistingTableColumns != null) + { + xeExistingTableColumns.ReplaceWith(xeNewTableColumns); } + + partTable.PutXDocument(); } } } @@ -541,7 +539,9 @@ private static int AddDxfToDxfs(XDocument xdSheet, XDocument xdStyle, int format } } if (cellXfs == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } var cnt = (int)cellXfs.Attribute("count"); cnt++; @@ -564,11 +564,12 @@ private static string RowColToString(int row, int col) private static object UpdateAccentTransform(XNode node, int accentNumber) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == A.schemeClr && (string)element.Attribute("val") == "accent1") + { return new XElement(A.schemeClr, new XAttribute("val", "accent" + accentNumber)); + } return new XElement(element.Name, element.Attributes(), @@ -576,27 +577,5 @@ private static object UpdateAccentTransform(XNode node, int accentNumber) } return node; } - - public static bool UpdateChart(PresentationDocument pDoc, int slideNumber, ChartData chartData) - { - var presentationPart = pDoc.PresentationPart; - var pXDoc = presentationPart.GetXDocument(); - var sldIdElement = pXDoc.Root.Elements(P.sldIdLst).Elements(P.sldId).Skip(slideNumber - 1).FirstOrDefault(); - if (sldIdElement != null) - { - var rId = (string)sldIdElement.Attribute(R.id); - var slidePart = presentationPart.GetPartById(rId); - var sXDoc = slidePart.GetXDocument(); - var chartRid = (string)sXDoc.Descendants(C.chart).Attributes(R.id).FirstOrDefault(); - if (chartRid != null) - { - ChartPart chartPart = (ChartPart)slidePart.GetPartById(chartRid); - UpdateChart(chartPart, chartData); - return true; - } - return true; - } - return false; - } } } \ No newline at end of file diff --git a/OpenXmlPowerTools/ColorParser.cs b/OpenXmlPowerTools/ColorParser.cs index 7539e9fd..993abb4f 100644 --- a/OpenXmlPowerTools/ColorParser.cs +++ b/OpenXmlPowerTools/ColorParser.cs @@ -1,29 +1,37 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using SkiaSharp; +using System; using System.Drawing; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public static class ColorParser { - public static Color FromName(string name) + public static SKColor FromName(string name) { - return Color.FromName(name); + if (!TryFromName(name, out var color)) + { + throw new ArgumentException("Invalid color name", nameof(name)); + } + return color; } - public static bool TryFromName(string name, out Color color) + public static bool TryFromName(string? name, out SKColor color) { - try + if (string.IsNullOrWhiteSpace(name)) { - color = Color.FromName(name); + color = default; + return false; + } - return color.IsNamedColor; + try + { + var drawingColor = ColorTranslator.FromHtml(name); + color = new SKColor(drawingColor.R, drawingColor.G, drawingColor.B, drawingColor.A); + return true; } catch { - color = default(Color); - + color = default; return false; } } diff --git a/OpenXmlPowerTools/DocumentAssembler.cs b/OpenXmlPowerTools/DocumentAssembler/DocumentAssembler.cs similarity index 82% rename from OpenXmlPowerTools/DocumentAssembler.cs rename to OpenXmlPowerTools/DocumentAssembler/DocumentAssembler.cs index f4d9673e..c838e7bf 100644 --- a/OpenXmlPowerTools/DocumentAssembler.cs +++ b/OpenXmlPowerTools/DocumentAssembler/DocumentAssembler.cs @@ -1,57 +1,51 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; -using System.Xml.XPath; using System.Xml.Schema; -using DocumentFormat.OpenXml.Office.CustomUI; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using System.Collections; +using System.Xml.XPath; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { - public class DocumentAssembler + public partial class DocumentAssembler { public static WmlDocument AssembleDocument(WmlDocument templateDoc, XmlDocument data, out bool templateError) { - XDocument xDoc = data.GetXDocument(); + var xDoc = data.GetXDocument(); return AssembleDocument(templateDoc, xDoc.Root, out templateError); } public static WmlDocument AssembleDocument(WmlDocument templateDoc, XElement data, out bool templateError) { - byte[] byteArray = templateDoc.DocumentByteArray; - using (MemoryStream mem = new MemoryStream()) + var byteArray = templateDoc.DocumentByteArray; + using var mem = new MemoryStream(); + mem.Write(byteArray, 0, byteArray.Length); + using (var wordDoc = WordprocessingDocument.Open(mem, true)) { - mem.Write(byteArray, 0, (int)byteArray.Length); - using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(mem, true)) + if (RevisionAccepter.HasTrackedRevisions(wordDoc)) { - if (RevisionAccepter.HasTrackedRevisions(wordDoc)) - throw new OpenXmlPowerToolsException("Invalid DocumentAssembler template - contains tracked revisions"); + throw new OpenXmlPowerToolsException("Invalid DocumentAssembler template - contains tracked revisions"); + } - var te = new TemplateError(); - foreach (var part in wordDoc.ContentParts()) - { - ProcessTemplatePart(data, te, part); - } - templateError = te.HasError; + var te = new TemplateError(); + foreach (var part in wordDoc.ContentParts()) + { + ProcessTemplatePart(data, te, part); } - WmlDocument assembledDocument = new WmlDocument("TempFileName.docx", mem.ToArray()); - return assembledDocument; + templateError = te.HasError; } + var assembledDocument = new WmlDocument("TempFileName.docx", mem.ToArray()); + return assembledDocument; } private static void ProcessTemplatePart(XElement data, TemplateError te, OpenXmlPart part) { - XDocument xDoc = part.GetXDocument(); + var xDoc = part.GetXDocument(); var xDocRoot = RemoveGoBackBookmarks(xDoc.Root); @@ -72,14 +66,14 @@ private static void ProcessTemplatePart(XElement data, TemplateError te, OpenXml ProcessOrphanEndRepeatEndConditional(xDocRoot, te); // do the actual content replacement - xDocRoot = (XElement)ContentReplacementTransform(xDocRoot, data, te); + xDocRoot = ContentReplacementTransform(xDocRoot, data, te) as XElement; xDoc.Elements().First().ReplaceWith(xDocRoot); part.PutXDocument(); return; } - private static XName[] s_MetaToForceToBlock = new XName[] { + private static readonly XName[] s_MetaToForceToBlock = new XName[] { PA.Conditional, PA.EndConditional, PA.Repeat, @@ -89,20 +83,19 @@ private static void ProcessTemplatePart(XElement data, TemplateError te, OpenXml private static object ForceBlockLevelAsAppropriate(XNode node, TemplateError te) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.p) { var childMeta = element.Elements().Where(n => s_MetaToForceToBlock.Contains(n.Name)).ToList(); - if (childMeta.Count() == 1) + if (childMeta.Count == 1) { var child = childMeta.First(); var otherTextInParagraph = element.Elements(W.r).Elements(W.t).Select(t => (string)t).StringConcatenate().Trim(); if (otherTextInParagraph != "") { var newPara = new XElement(element); - var newMeta = newPara.Elements().Where(n => s_MetaToForceToBlock.Contains(n.Name)).First(); + var newMeta = newPara.Elements().First(n => s_MetaToForceToBlock.Contains(n.Name)); newMeta.ReplaceWith(CreateRunErrorMessage("Error: Unmatched metadata can't be in paragraph with other text", te)); return newPara; } @@ -114,13 +107,19 @@ private static object ForceBlockLevelAsAppropriate(XNode node, TemplateError te) child.Elements())); return meta; } - var count = childMeta.Count(); + var count = childMeta.Count; if (count % 2 == 0) { if (childMeta.Where(c => c.Name == PA.Repeat).Count() != childMeta.Where(c => c.Name == PA.EndRepeat).Count()) + { return CreateContextErrorMessage(element, "Error: Mismatch Repeat / EndRepeat at run level", te); + } + if (childMeta.Where(c => c.Name == PA.Conditional).Count() != childMeta.Where(c => c.Name == PA.EndConditional).Count()) + { return CreateContextErrorMessage(element, "Error: Mismatch Conditional / EndConditional at run level", te); + } + return new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => ForceBlockLevelAsAppropriate(n, te))); @@ -158,7 +157,10 @@ private static XElement RemoveGoBackBookmarks(XElement xElement) { var bm = cloneXDoc.DescendantsAndSelf(W.bookmarkStart).FirstOrDefault(b => (string)b.Attribute(W.name) == "_GoBack"); if (bm == null) + { break; + } + var id = (string)bm.Attribute(W.id); var endBm = cloneXDoc.DescendantsAndSelf(W.bookmarkEnd).FirstOrDefault(b => (string)b.Attribute(W.id) == id); bm.Remove(); @@ -171,8 +173,7 @@ private static XElement RemoveGoBackBookmarks(XElement xElement) // the content control, which contains the paragraph content of the cell. private static object NormalizeContentControlsInCells(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.sdt && element.Parent.Name == W.tr) { @@ -200,7 +201,7 @@ private static void NormalizeTablesRepeatAndConditional(XElement xDoc, TemplateE var tables = xDoc.Descendants(PA.Table).ToList(); foreach (var table in tables) { - var followingElement = table.ElementsAfterSelf().Where(e => e.Name == W.tbl || e.Name == W.p).FirstOrDefault(); + var followingElement = table.ElementsAfterSelf().FirstOrDefault(e => e.Name == W.tbl || e.Name == W.p); if (followingElement == null || followingElement.Name != W.tbl) { table.ReplaceWith(CreateParaErrorMessage("Table metadata is not immediately followed by a table", te)); @@ -213,8 +214,8 @@ private static void NormalizeTablesRepeatAndConditional(XElement xDoc, TemplateE table.Add(followingElement); } - int repeatDepth = 0; - int conditionalDepth = 0; + var repeatDepth = 0; + var conditionalDepth = 0; foreach (var metadata in xDoc.Descendants().Where(d => d.Name == PA.Repeat || d.Name == PA.Conditional || @@ -249,17 +250,25 @@ private static void NormalizeTablesRepeatAndConditional(XElement xDoc, TemplateE while (true) { - bool didReplace = false; + var didReplace = false; foreach (var metadata in xDoc.Descendants().Where(d => (d.Name == PA.Repeat || d.Name == PA.Conditional) && d.Attribute(PA.Depth) != null).ToList()) { var depth = (int)metadata.Attribute(PA.Depth); - XName matchingEndName = null; + XName? matchingEndName = null; if (metadata.Name == PA.Repeat) + { matchingEndName = PA.EndRepeat; + } else if (metadata.Name == PA.Conditional) + { matchingEndName = PA.EndConditional; + } + if (matchingEndName == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } + var matchingEnd = metadata.ElementsAfterSelf(matchingEndName).FirstOrDefault(end => { return (int)end.Attribute(PA.Depth) == depth; }); if (matchingEnd == null) { @@ -269,7 +278,10 @@ private static void NormalizeTablesRepeatAndConditional(XElement xDoc, TemplateE metadata.RemoveNodes(); var contentBetween = metadata.ElementsAfterSelf().TakeWhile(after => after != matchingEnd).ToList(); foreach (var item in contentBetween) + { item.Remove(); + } + contentBetween = contentBetween.Where(n => n.Name != W.bookmarkStart && n.Name != W.bookmarkEnd).ToList(); metadata.Add(contentBetween); metadata.Attributes(PA.Depth).Remove(); @@ -278,11 +290,13 @@ private static void NormalizeTablesRepeatAndConditional(XElement xDoc, TemplateE break; } if (!didReplace) + { break; + } } } - private static List s_AliasList = new List() + private static readonly List s_AliasList = new List() { "Content", "Table", @@ -294,8 +308,7 @@ private static void NormalizeTablesRepeatAndConditional(XElement xDoc, TemplateE private static object TransformToMetadata(XNode node, XElement data, TemplateError te) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.sdt) { @@ -312,19 +325,26 @@ private static object TransformToMetadata(XNode node, XElement data, TemplateErr .Replace('”', '"'); if (ccContents.StartsWith("<")) { - XElement xml = TransformXmlTextToMetadata(te, ccContents); + var xml = TransformXmlTextToMetadata(te, ccContents); if (xml.Name == W.p || xml.Name == W.r) // this means there was an error processing the XML. { if (element.Parent.Name == W.p) + { return xml.Elements(W.r); + } + return xml; } if (alias != null && xml.Name.LocalName != alias) { if (element.Parent.Name == W.p) + { return CreateRunErrorMessage("Error: Content control alias does not match metadata element name", te); + } else + { return CreateParaErrorMessage("Error: Content control alias does not match metadata element name", te); + } } xml.Add(element.Elements(W.sdtContent).Elements()); return xml; @@ -345,22 +365,25 @@ private static object TransformToMetadata(XNode node, XElement data, TemplateErr .Select(t => (string)t) .StringConcatenate() .Trim(); - int occurances = paraContents.Select((c, i) => paraContents.Substring(i)).Count(sub => sub.StartsWith("<#")); + var occurances = paraContents.Select((c, i) => paraContents.Substring(i)).Count(sub => sub.StartsWith("<#")); if (paraContents.StartsWith("<#") && paraContents.EndsWith("#>") && occurances == 1) { var xmlText = paraContents.Substring(2, paraContents.Length - 4).Trim(); - XElement xml = TransformXmlTextToMetadata(te, xmlText); + var xml = TransformXmlTextToMetadata(te, xmlText); if (xml.Name == W.p || xml.Name == W.r) + { return xml; + } + xml.Add(element); return xml; } if (paraContents.Contains("<#")) { - List runReplacementInfo = new List(); + var runReplacementInfo = new List(); var thisGuid = Guid.NewGuid().ToString(); var r = new Regex("<#.*?#>"); - XElement xml = null; + XElement? xml = null; OpenXmlRegex.Replace(new[] { element }, r, thisGuid, (para, match) => { var matchString = match.Value.Trim(); @@ -371,7 +394,7 @@ private static object TransformToMetadata(XNode node, XElement data, TemplateErr } catch (XmlException e) { - RunReplacementInfo rri = new RunReplacementInfo() + var rri = new RunReplacementInfo() { Xml = null, XmlExceptionMessage = "XmlException: " + e.Message, @@ -380,10 +403,10 @@ private static object TransformToMetadata(XNode node, XElement data, TemplateErr runReplacementInfo.Add(rri); return true; } - string schemaError = ValidatePerSchema(xml); + var schemaError = ValidatePerSchema(xml); if (schemaError != null) { - RunReplacementInfo rri = new RunReplacementInfo() + var rri = new RunReplacementInfo() { Xml = null, XmlExceptionMessage = null, @@ -392,7 +415,7 @@ private static object TransformToMetadata(XNode node, XElement data, TemplateErr runReplacementInfo.Add(rri); return true; } - RunReplacementInfo rri2 = new RunReplacementInfo() + var rri2 = new RunReplacementInfo() { Xml = xml, XmlExceptionMessage = null, @@ -407,11 +430,18 @@ private static object TransformToMetadata(XNode node, XElement data, TemplateErr { var runToReplace = newPara.Descendants(W.r).FirstOrDefault(rn => rn.Value == thisGuid && rn.Parent.Name != PA.Content); if (runToReplace == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } + if (rri.XmlExceptionMessage != null) + { runToReplace.ReplaceWith(CreateRunErrorMessage(rri.XmlExceptionMessage, te)); + } else if (rri.SchemaValidationMessage != null) + { runToReplace.ReplaceWith(CreateRunErrorMessage(rri.SchemaValidationMessage, te)); + } else { var newXml = new XElement(rri.Xml); @@ -442,20 +472,23 @@ private static XElement TransformXmlTextToMetadata(TemplateError te, string xmlT { return CreateParaErrorMessage("XmlException: " + e.Message, te); } - string schemaError = ValidatePerSchema(xml); + var schemaError = ValidatePerSchema(xml); if (schemaError != null) + { return CreateParaErrorMessage("Schema Validation Error: " + schemaError, te); + } + return xml; } private class RunReplacementInfo { - public XElement Xml; - public string XmlExceptionMessage; - public string SchemaValidationMessage; + public XElement? Xml; + public string? XmlExceptionMessage; + public string? SchemaValidationMessage; } - private static string ValidatePerSchema(XElement element) + private static string? ValidatePerSchema(XElement element) { if (s_PASchemaSets == null) { @@ -539,7 +572,7 @@ private static string ValidatePerSchema(XElement element) foreach (var item in s_PASchemaSets) { var itemPAss = item.Value; - XmlSchemaSet schemas = new XmlSchemaSet(); + var schemas = new XmlSchemaSet(); schemas.Add("", XmlReader.Create(new StringReader(itemPAss.XsdMarkup))); itemPAss.SchemaSet = schemas; } @@ -549,60 +582,37 @@ private static string ValidatePerSchema(XElement element) return string.Format("Invalid XML: {0} is not a valid element", element.Name.LocalName); } var paSchemaSet = s_PASchemaSets[element.Name]; - XDocument d = new XDocument(element); - string message = null; + var d = new XDocument(element); + string? message = null; d.Validate(paSchemaSet.SchemaSet, (sender, e) => { if (message == null) + { message = e.Message; + } }, true); if (message != null) + { return message; - return null; - } - - private class PA - { - public static XName Content = "Content"; - public static XName Table = "Table"; - public static XName Repeat = "Repeat"; - public static XName EndRepeat = "EndRepeat"; - public static XName Conditional = "Conditional"; - public static XName EndConditional = "EndConditional"; - - public static XName Select = "Select"; - public static XName Optional = "Optional"; - public static XName Match = "Match"; - public static XName NotMatch = "NotMatch"; - public static XName Depth = "Depth"; - } + } - private class PASchemaSet - { - public string XsdMarkup; - public XmlSchemaSet SchemaSet; + return null; } - private static Dictionary s_PASchemaSets = null; + private static Dictionary s_PASchemaSets; - private class TemplateError + private static object? ContentReplacementTransform(XNode node, XElement data, TemplateError templateError) { - public bool HasError = false; - } - - static object ContentReplacementTransform(XNode node, XElement data, TemplateError templateError) - { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == PA.Content) { - XElement para = element.Descendants(W.p).FirstOrDefault(); - XElement run = element.Descendants(W.r).FirstOrDefault(); + var para = element.Descendants(W.p).FirstOrDefault(); + var run = element.Descendants(W.r).FirstOrDefault(); - var xPath = (string) element.Attribute(PA.Select); - var optionalString = (string) element.Attribute(PA.Optional); - bool optional = (optionalString != null && optionalString.ToLower() == "true"); + var xPath = (string)element.Attribute(PA.Select); + var optionalString = (string)element.Attribute(PA.Optional); + var optional = (optionalString != null && optionalString.ToLower() == "true"); string newValue; try @@ -616,9 +626,8 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr if (para != null) { - - XElement p = new XElement(W.p, para.Elements(W.pPr)); - foreach(string line in newValue.Split('\n')) + var p = new XElement(W.p, para.Elements(W.pPr)); + foreach (var line in newValue.Split('\n')) { p.Add(new XElement(W.r, para.Elements(W.r).Elements(W.rPr).FirstOrDefault(), @@ -629,8 +638,8 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr } else { - List list = new List(); - foreach(string line in newValue.Split('\n')) + var list = new List(); + foreach (var line in newValue.Split('\n')) { list.Add(new XElement(W.r, run.Elements().Where(e => e.Name != W.t), @@ -642,9 +651,9 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr } if (element.Name == PA.Repeat) { - string selector = (string)element.Attribute(PA.Select); + var selector = (string)element.Attribute(PA.Select); var optionalString = (string)element.Attribute(PA.Optional); - bool optional = (optionalString != null && optionalString.ToLower() == "true"); + var optional = (optionalString != null && optionalString.ToLower() == "true"); IEnumerable repeatingData; try @@ -660,11 +669,6 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr if (optional) { return null; - //XElement para = element.Descendants(W.p).FirstOrDefault(); - //if (para != null) - // return new XElement(W.p, new XElement(W.r)); - //else - // return new XElement(W.r); } return CreateContextErrorMessage(element, "Repeat: Select returned no data", templateError); } @@ -690,10 +694,13 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr { return CreateContextErrorMessage(element, "XPathException: " + e.Message, templateError); } - if (tableData.Count() == 0) + if (!tableData.Any()) + { return CreateContextErrorMessage(element, "Table Select returned no data", templateError); - XElement table = element.Element(W.tbl); - XElement protoRow = table.Elements(W.tr).Skip(1).FirstOrDefault(); + } + + var table = element.Element(W.tbl); + var protoRow = table.Elements(W.tr).Skip(1).FirstOrDefault(); var footerRowsBeforeTransform = table .Elements(W.tr) .Skip(2) @@ -702,10 +709,13 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr .Select(x => ContentReplacementTransform(x, data, templateError)) .ToList(); if (protoRow == null) + { return CreateContextErrorMessage(element, string.Format("Table does not contain a prototype row"), templateError); + } + protoRow.Descendants(W.bookmarkStart).Remove(); protoRow.Descendants(W.bookmarkEnd).Remove(); - XElement newTable = new XElement(W.tbl, + var newTable = new XElement(W.tbl, table.Elements().Where(e => e.Name != W.tr), table.Elements(W.tr).FirstOrDefault(), tableData.Select(d => @@ -714,17 +724,17 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr protoRow.Elements(W.tc) .Select(tc => { - XElement paragraph = tc.Elements(W.p).FirstOrDefault(); - XElement cellRun = paragraph.Elements(W.r).FirstOrDefault(); - string xPath = paragraph.Value; - string newValue = null; + var paragraph = tc.Elements(W.p).FirstOrDefault(); + var cellRun = paragraph.Elements(W.r).FirstOrDefault(); + var xPath = paragraph.Value; + string? newValue = null; try { newValue = EvaluateXPathToString(d, xPath, false); } catch (XPathException e) { - XElement errorCell = new XElement(W.tc, + var errorCell = new XElement(W.tc, tc.Elements().Where(z => z.Name != W.p), new XElement(W.p, paragraph.Element(W.pPr), @@ -732,7 +742,7 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr return errorCell; } - XElement newCell = new XElement(W.tc, + var newCell = new XElement(W.tc, tc.Elements().Where(z => z.Name != W.p), new XElement(W.p, paragraph.Element(W.pPr), @@ -747,26 +757,31 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr } if (element.Name == PA.Conditional) { - string xPath = (string)element.Attribute(PA.Select); + var xPath = (string)element.Attribute(PA.Select); var match = (string)element.Attribute(PA.Match); var notMatch = (string)element.Attribute(PA.NotMatch); if (match == null && notMatch == null) + { return CreateContextErrorMessage(element, "Conditional: Must specify either Match or NotMatch", templateError); + } + if (match != null && notMatch != null) + { return CreateContextErrorMessage(element, "Conditional: Cannot specify both Match and NotMatch", templateError); + } + + string? testValue = null; - string testValue = null; - try { testValue = EvaluateXPathToString(data, xPath, false); } - catch (XPathException e) + catch (XPathException e) { return CreateContextErrorMessage(element, e.Message, templateError); } - + if ((match != null && testValue == match) || (notMatch != null && testValue != notMatch)) { var content = element.Elements().Select(e => ContentReplacementTransform(e, data, templateError)); @@ -783,13 +798,16 @@ static object ContentReplacementTransform(XNode node, XElement data, TemplateErr private static object CreateContextErrorMessage(XElement element, string errorMessage, TemplateError templateError) { - XElement para = element.Descendants(W.p).FirstOrDefault(); - XElement run = element.Descendants(W.r).FirstOrDefault(); + var para = element.Descendants(W.p).FirstOrDefault(); var errorRun = CreateRunErrorMessage(errorMessage, templateError); if (para != null) + { return new XElement(W.p, errorRun); + } else + { return errorRun; + } } private static XElement CreateRunErrorMessage(string errorMessage, TemplateError templateError) @@ -815,14 +833,17 @@ private static XElement CreateParaErrorMessage(string errorMessage, TemplateErro return errorPara; } - private static string EvaluateXPathToString(XElement element, string xPath, bool optional ) + private static string EvaluateXPathToString(XElement element, string xPath, bool optional) { object xPathSelectResult; try { //support some cells in the table may not have an xpath expression. - if (String.IsNullOrWhiteSpace(xPath)) return String.Empty; - + if (string.IsNullOrWhiteSpace(xPath)) + { + return string.Empty; + } + xPathSelectResult = element.XPathEvaluate(xPath); } catch (XPathException e) @@ -832,10 +853,14 @@ private static string EvaluateXPathToString(XElement element, string xPath, bool if ((xPathSelectResult is IEnumerable) && !(xPathSelectResult is string)) { - var selectedData = ((IEnumerable) xPathSelectResult).Cast(); + var selectedData = ((IEnumerable)xPathSelectResult).Cast(); if (!selectedData.Any()) { - if (optional) return string.Empty; + if (optional) + { + return string.Empty; + } + throw new XPathException(string.Format("XPath expression ({0}) returned no results", xPath)); } if (selectedData.Count() > 1) @@ -843,15 +868,20 @@ private static string EvaluateXPathToString(XElement element, string xPath, bool throw new XPathException(string.Format("XPath expression ({0}) returned more than one node", xPath)); } - XObject selectedDatum = selectedData.First(); - - if (selectedDatum is XElement) return ((XElement) selectedDatum).Value; + var selectedDatum = selectedData.First(); - if (selectedDatum is XAttribute) return ((XAttribute) selectedDatum).Value; + if (selectedDatum is XElement element1) + { + return element1.Value; + } + + if (selectedDatum is XAttribute) + { + return ((XAttribute)selectedDatum).Value; + } } return xPathSelectResult.ToString(); - } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/DocumentAssembler/PA.cs b/OpenXmlPowerTools/DocumentAssembler/PA.cs new file mode 100644 index 00000000..a4482288 --- /dev/null +++ b/OpenXmlPowerTools/DocumentAssembler/PA.cs @@ -0,0 +1,22 @@ +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools +{ + public partial class DocumentAssembler + { + private static class PA + { + public static readonly XName Content = "Content"; + public static readonly XName Table = "Table"; + public static readonly XName Repeat = "Repeat"; + public static readonly XName EndRepeat = "EndRepeat"; + public static readonly XName Conditional = "Conditional"; + public static readonly XName EndConditional = "EndConditional"; + public static readonly XName Select = "Select"; + public static readonly XName Optional = "Optional"; + public static readonly XName Match = "Match"; + public static readonly XName NotMatch = "NotMatch"; + public static readonly XName Depth = "Depth"; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/DocumentAssembler/PASchemaSet.cs b/OpenXmlPowerTools/DocumentAssembler/PASchemaSet.cs new file mode 100644 index 00000000..f0fa1209 --- /dev/null +++ b/OpenXmlPowerTools/DocumentAssembler/PASchemaSet.cs @@ -0,0 +1,13 @@ +using System.Xml.Schema; + +namespace Codeuctivity.OpenXmlPowerTools +{ + public partial class DocumentAssembler + { + private class PASchemaSet + { + public string XsdMarkup; + public XmlSchemaSet SchemaSet; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/DocumentAssembler/TemplateError.cs b/OpenXmlPowerTools/DocumentAssembler/TemplateError.cs new file mode 100644 index 00000000..e533ddfb --- /dev/null +++ b/OpenXmlPowerTools/DocumentAssembler/TemplateError.cs @@ -0,0 +1,10 @@ +namespace Codeuctivity.OpenXmlPowerTools +{ + public partial class DocumentAssembler + { + private class TemplateError + { + public bool HasError; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/DocumentBuilder.cs b/OpenXmlPowerTools/DocumentBuilder/DocumentBuilder.cs similarity index 73% rename from OpenXmlPowerTools/DocumentBuilder.cs rename to OpenXmlPowerTools/DocumentBuilder/DocumentBuilder.cs index 18114cc6..b2bbd452 100644 --- a/OpenXmlPowerTools/DocumentBuilder.cs +++ b/OpenXmlPowerTools/DocumentBuilder/DocumentBuilder.cs @@ -1,220 +1,66 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#define TestForUnsupportedDocuments +#define TestForUnsupportedDocuments #define MergeStylesWithSameNames +using Codeuctivity.OpenXmlPowerTools.Exceptions; +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools.DocumentBuilder { - public partial class WmlDocument : OpenXmlPowerToolsDocument - { - public IEnumerable SplitOnSections() - { - return DocumentBuilder.SplitOnSections(this); - } - } - - public class Source - { - public WmlDocument WmlDocument { get; set; } - public int Start { get; set; } - public int Count { get; set; } - public bool KeepSections { get; set; } - public bool DiscardHeadersAndFootersInKeptSections { get; set; } - public string InsertId { get; set; } - - public Source(string fileName) - { - WmlDocument = new WmlDocument(fileName); - Start = 0; - Count = Int32.MaxValue; - KeepSections = false; - InsertId = null; - } - - public Source(WmlDocument source) - { - WmlDocument = source; - Start = 0; - Count = Int32.MaxValue; - KeepSections = false; - InsertId = null; - } - - public Source(string fileName, bool keepSections) - { - WmlDocument = new WmlDocument(fileName); - Start = 0; - Count = Int32.MaxValue; - KeepSections = keepSections; - InsertId = null; - } - - public Source(WmlDocument source, bool keepSections) - { - WmlDocument = source; - Start = 0; - Count = Int32.MaxValue; - KeepSections = keepSections; - InsertId = null; - } - - public Source(string fileName, string insertId) - { - WmlDocument = new WmlDocument(fileName); - Start = 0; - Count = Int32.MaxValue; - KeepSections = false; - InsertId = insertId; - } - - public Source(WmlDocument source, string insertId) - { - WmlDocument = source; - Start = 0; - Count = Int32.MaxValue; - KeepSections = false; - InsertId = insertId; - } - - public Source(string fileName, int start, bool keepSections) - { - WmlDocument = new WmlDocument(fileName); - Start = start; - Count = Int32.MaxValue; - KeepSections = keepSections; - InsertId = null; - } - - public Source(WmlDocument source, int start, bool keepSections) - { - WmlDocument = source; - Start = start; - Count = Int32.MaxValue; - KeepSections = keepSections; - InsertId = null; - } - - public Source(string fileName, int start, string insertId) - { - WmlDocument = new WmlDocument(fileName); - Start = start; - Count = Int32.MaxValue; - KeepSections = false; - InsertId = insertId; - } - - public Source(WmlDocument source, int start, string insertId) - { - WmlDocument = source; - Start = start; - Count = Int32.MaxValue; - KeepSections = false; - InsertId = insertId; - } - - public Source(string fileName, int start, int count, bool keepSections) - { - WmlDocument = new WmlDocument(fileName); - Start = start; - Count = count; - KeepSections = keepSections; - InsertId = null; - } - - public Source(WmlDocument source, int start, int count, bool keepSections) - { - WmlDocument = source; - Start = start; - Count = count; - KeepSections = keepSections; - InsertId = null; - } - - public Source(string fileName, int start, int count, string insertId) - { - WmlDocument = new WmlDocument(fileName); - Start = start; - Count = count; - KeepSections = false; - InsertId = insertId; - } - - public Source(WmlDocument source, int start, int count, string insertId) - { - WmlDocument = source; - Start = start; - Count = count; - KeepSections = false; - InsertId = insertId; - } - } - public class DocumentBuilderSettings { - public HashSet CustomXmlGuidList = null; - public bool NormalizeStyleIds = false; + public HashSet CustomXmlGuidList { get; set; } + public bool NormalizeStyleIds { get; set; } } public static class DocumentBuilder { public static void BuildDocument(List sources, string fileName) { - using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument()) + using var streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument(); + using (var output = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument output = streamDoc.GetWordprocessingDocument()) - { - BuildDocument(sources, output, new DocumentBuilderSettings()); - output.Close(); - } - streamDoc.GetModifiedDocument().SaveAs(fileName); + BuildDocument(sources, output, new DocumentBuilderSettings()); + output.Dispose(); } + streamDoc.GetModifiedDocument().SaveAs(fileName); } public static void BuildDocument(List sources, string fileName, DocumentBuilderSettings settings) { - using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument()) + using var streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument(); + using (var output = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument output = streamDoc.GetWordprocessingDocument()) - { - BuildDocument(sources, output, settings); - output.Close(); - } - streamDoc.GetModifiedDocument().SaveAs(fileName); + BuildDocument(sources, output, settings); + output.Dispose(); } + streamDoc.GetModifiedDocument().SaveAs(fileName); } public static WmlDocument BuildDocument(List sources) { - using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument()) + using var streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument(); + using (var output = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument output = streamDoc.GetWordprocessingDocument()) - { - BuildDocument(sources, output, new DocumentBuilderSettings()); - output.Close(); - } - return streamDoc.GetModifiedWmlDocument(); + BuildDocument(sources, output, new DocumentBuilderSettings()); + output.Dispose(); } + return streamDoc.GetModifiedWmlDocument(); } public static WmlDocument BuildDocument(List sources, DocumentBuilderSettings settings) { - using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument()) + using var streamDoc = OpenXmlMemoryStreamDocument.CreateWordprocessingDocument(); + using (var output = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument output = streamDoc.GetWordprocessingDocument()) - { - BuildDocument(sources, output, settings); - output.Close(); - } - return streamDoc.GetModifiedWmlDocument(); + BuildDocument(sources, output, settings); + output.Dispose(); } + return streamDoc.GetModifiedWmlDocument(); } private struct TempSource @@ -231,7 +77,7 @@ private class Atbi private class Atbid { - public XElement BlockLevelContent; + public XElement? BlockLevelContent; public int Index; public int Div; } @@ -243,106 +89,101 @@ private class Atbid public static IEnumerable SplitOnSections(WmlDocument doc) { List tempSourceList; - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc)) - using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument()) - { - XDocument mainDocument = document.MainDocumentPart.GetXDocument(); - var divs = mainDocument - .Root - .Element(W.body) - .Elements() - .Select((p, i) => new Atbi + using var streamDoc = new OpenXmlMemoryStreamDocument(doc); + using var document = streamDoc.GetWordprocessingDocument(); + var mainDocument = document.MainDocumentPart.GetXDocument(); + var divs = mainDocument + .Root + .Element(W.body) + .Elements() + .Select((p, i) => new Atbi + { + BlockLevelContent = p, + Index = i, + }) + .Rollup(new Atbid + { + BlockLevelContent = null, + Index = -1, + Div = 0, + }, + (b, p) => { - BlockLevelContent = p, - Index = i, - }) - .Rollup(new Atbid - { - BlockLevelContent = (XElement)null, - Index = -1, - Div = 0, - }, - (b, p) => - { - XElement elementBefore = b.BlockLevelContent - .SiblingsBeforeSelfReverseDocumentOrder() - .FirstOrDefault(); - if (elementBefore != null && elementBefore.Descendants(W.sectPr).Any()) - return new Atbid - { - BlockLevelContent = b.BlockLevelContent, - Index = b.Index, - Div = p.Div + 1, - }; + var elementBefore = b.BlockLevelContent + .SiblingsBeforeSelfReverseDocumentOrder() + .FirstOrDefault(); + if (elementBefore != null && elementBefore.Descendants(W.sectPr).Any()) + { return new Atbid { BlockLevelContent = b.BlockLevelContent, Index = b.Index, - Div = p.Div, + Div = p.Div + 1, }; - }); - var groups = divs - .GroupAdjacent(b => b.Div); - tempSourceList = groups - .Select(g => new TempSource - { - Start = g.First().Index, - Count = g.Count(), - }) - .ToList(); - foreach (var ts in tempSourceList) + } + + return new Atbid + { + BlockLevelContent = b.BlockLevelContent, + Index = b.Index, + Div = p.Div, + }; + }); + var groups = divs + .GroupAdjacent(b => b.Div); + tempSourceList = groups + .Select(g => new TempSource { - List sources = new List() + Start = g.First().Index, + Count = g.Count(), + }) + .ToList(); + foreach (var ts in tempSourceList) + { + var sources = new List() { new Source(doc, ts.Start, ts.Count, true) }; - WmlDocument newDoc = DocumentBuilder.BuildDocument(sources); - newDoc = AdjustSectionBreak(newDoc); - yield return newDoc; - } + var newDoc = BuildDocument(sources); + newDoc = AdjustSectionBreak(newDoc); + yield return newDoc; } } private static WmlDocument AdjustSectionBreak(WmlDocument doc) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc)) + using var streamDoc = new OpenXmlMemoryStreamDocument(doc); + using (var document = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument()) + var mainXDoc = document.MainDocumentPart.GetXDocument(); + var lastElement = mainXDoc.Root.Element(W.body).Elements().LastOrDefault(); + if (lastElement != null && lastElement.Name != W.sectPr && lastElement.Descendants(W.sectPr).Any()) { - XDocument mainXDoc = document.MainDocumentPart.GetXDocument(); - XElement lastElement = mainXDoc.Root - .Element(W.body) - .Elements() - .LastOrDefault(); - if (lastElement != null) + mainXDoc.Root.Element(W.body).Add(lastElement.Descendants(W.sectPr).First()); + lastElement.Descendants(W.sectPr).Remove(); + if (!lastElement.Elements().Any(e => e.Name != W.pPr)) { - if (lastElement.Name != W.sectPr && - lastElement.Descendants(W.sectPr).Any()) - { - mainXDoc.Root.Element(W.body).Add(lastElement.Descendants(W.sectPr).First()); - lastElement.Descendants(W.sectPr).Remove(); - if (!lastElement.Elements() - .Where(e => e.Name != W.pPr) - .Any()) - lastElement.Remove(); - document.MainDocumentPart.PutXDocument(); - } + lastElement.Remove(); } + + document.MainDocumentPart.PutXDocument(); } - return streamDoc.GetModifiedWmlDocument(); } + return streamDoc.GetModifiedWmlDocument(); } private static void BuildDocument(List sources, WordprocessingDocument output, DocumentBuilderSettings settings) { - WmlDocument wmlGlossaryDocument = CoalesceGlossaryDocumentParts(sources, settings); + var wmlGlossaryDocument = CoalesceGlossaryDocumentParts(sources); if (RelationshipMarkup == null) + { InitRelationshipMarkup(); + } // This list is used to eliminate duplicate images - List images = new List(); - XDocument mainPart = output.MainDocumentPart.GetXDocument(); + var images = new List(); + var mainPart = output.MainDocumentPart.GetXDocument(); mainPart.Declaration.Standalone = Yes; mainPart.Declaration.Encoding = Utf8; mainPart.Root.ReplaceWith( @@ -352,17 +193,19 @@ private static void BuildDocument(List sources, WordprocessingDocument o { // the following function makes sure that for a given style name, the same style ID is used for all documents. if (settings != null && settings.NormalizeStyleIds) + { sources = NormalizeStyleNamesAndIds(sources); + } - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument)) - using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) + using (var streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument)) + using (var doc = streamDoc.GetWordprocessingDocument()) { CopyStartingParts(doc, output, images); CopySpecifiedCustomXmlParts(doc, output, settings); } - int sourceNum2 = 0; - foreach (Source source in sources) + var sourceNum2 = 0; + foreach (var source in sources) { if (source.InsertId != null) { @@ -374,111 +217,122 @@ modify AppendDocument so that it can take a part. are there any PtOpenXml.Insert elements in any of them? if so, then open and process all. #endif - bool foundInMainDocPart = false; - XDocument mainXDoc = output.MainDocumentPart.GetXDocument(); + var foundInMainDocPart = false; + var mainXDoc = output.MainDocumentPart.GetXDocument(); if (mainXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId)) + { foundInMainDocPart = true; + } + if (foundInMainDocPart) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument)) - using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) - { + using var streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument); + using var doc = streamDoc.GetWordprocessingDocument(); #if TestForUnsupportedDocuments - // throws exceptions if a document contains unsupported content - TestForUnsupportedDocument(doc, sources.IndexOf(source)); + // throws exceptions if a document contains unsupported content + TestForUnsupportedDocument(doc, sources.IndexOf(source)); #endif - if (foundInMainDocPart) + if (foundInMainDocPart) + { + if (source.KeepSections && source.DiscardHeadersAndFootersInKeptSections) + { + RemoveHeadersAndFootersFromSections(doc); + } + else if (source.KeepSections) { - if (source.KeepSections && source.DiscardHeadersAndFootersInKeptSections) - RemoveHeadersAndFootersFromSections(doc); - else if (source.KeepSections) - ProcessSectionsForLinkToPreviousHeadersAndFooters(doc); - - List contents = doc.MainDocumentPart.GetXDocument() - .Root - .Element(W.body) - .Elements() - .Skip(source.Start) - .Take(source.Count) - .ToList(); - - try + ProcessSectionsForLinkToPreviousHeadersAndFooters(doc); + } + + var contents = doc.MainDocumentPart.GetXDocument() + .Root + .Element(W.body) + .Elements() + .Skip(source.Start) + .Take(source.Count) + .ToList(); + + try + { + AppendDocument(doc, output, contents, source.KeepSections, source.InsertId, images); + } + catch (DocumentBuilderInternalException dbie) + { + if (dbie.Message.Contains("{0}")) { - AppendDocument(doc, output, contents, source.KeepSections, source.InsertId, images); - } - catch (DocumentBuilderInternalException dbie) - { - if (dbie.Message.Contains("{0}")) - throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum2)); - else - throw dbie; + throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum2), dbie); } + throw; } } } else + { break; + } } } else { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument)) - using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) - { + using var streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument); + using var doc = streamDoc.GetWordprocessingDocument(); #if TestForUnsupportedDocuments - // throws exceptions if a document contains unsupported content - TestForUnsupportedDocument(doc, sources.IndexOf(source)); + // throws exceptions if a document contains unsupported content + TestForUnsupportedDocument(doc, sources.IndexOf(source)); #endif - if (source.KeepSections && source.DiscardHeadersAndFootersInKeptSections) - RemoveHeadersAndFootersFromSections(doc); - else if (source.KeepSections) - ProcessSectionsForLinkToPreviousHeadersAndFooters(doc); + if (source.KeepSections && source.DiscardHeadersAndFootersInKeptSections) + { + RemoveHeadersAndFootersFromSections(doc); + } + else if (source.KeepSections) + { + ProcessSectionsForLinkToPreviousHeadersAndFooters(doc); + } - var body = doc.MainDocumentPart.GetXDocument() - .Root - .Element(W.body); - - if (body == null) - throw new DocumentBuilderException( - String.Format("Source {0} is unsupported document - contains no body element in the correct namespace", sourceNum2)); - - List contents = body - .Elements() - .Skip(source.Start) - .Take(source.Count) - .ToList(); - try - { - AppendDocument(doc, output, contents, source.KeepSections, null, images); - } - catch (DocumentBuilderInternalException dbie) + var body = doc.MainDocumentPart.GetXDocument() + .Root + .Element(W.body); + + if (body == null) + { + throw new DocumentBuilderException( + string.Format("Source {0} is unsupported document - contains no body element in the correct namespace", sourceNum2)); + } + + var contents = body + .Elements() + .Skip(source.Start) + .Take(source.Count) + .ToList(); + try + { + AppendDocument(doc, output, contents, source.KeepSections, null, images); + } + catch (DocumentBuilderInternalException dbie) + { + if (dbie.Message.Contains("{0}")) { - if (dbie.Message.Contains("{0}")) - throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum2)); - else - throw dbie; + throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum2), dbie); } + throw; } } ++sourceNum2; } if (!sources.Any(s => s.KeepSections)) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument)) - using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) - { - var body = doc.MainDocumentPart.GetXDocument().Root.Element(W.body); + using var streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument); + using var doc = streamDoc.GetWordprocessingDocument(); + var body = doc.MainDocumentPart.GetXDocument().Root.Element(W.body); - if (body != null && body.Elements().Any()) - { - var sectPr = doc.MainDocumentPart.GetXDocument().Root.Elements(W.body) - .Elements().LastOrDefault(); - if (sectPr != null && sectPr.Name == W.sectPr) - { - AddSectionAndDependencies(doc, output, sectPr, images); - output.MainDocumentPart.GetXDocument().Root.Element(W.body).Add(sectPr); - } - } + if (body != null && body.Elements().Any()) + { + var sectPr = doc.MainDocumentPart.GetXDocument().Root.Elements(W.body) + .Elements().LastOrDefault(); + if (sectPr != null && sectPr.Name == W.sectPr) + { + AddSectionAndDependencies(doc, output, sectPr, images); + output.MainDocumentPart.GetXDocument().Root.Element(W.body).Add(sectPr); + } } } else @@ -490,7 +344,7 @@ are there any PtOpenXml.Insert elements in any of them? var mxd = output.MainDocumentPart.GetXDocument(); var sections = mxd.Descendants(W.sectPr).Reverse().ToList(); - CachedHeaderFooter[] cachedHeaderFooter = new[] + var cachedHeaderFooter = new[] { new CachedHeaderFooter() { Ref = W.headerReference, Type = "first" }, new CachedHeaderFooter() { Ref = W.headerReference, Type = "even" }, @@ -500,7 +354,7 @@ are there any PtOpenXml.Insert elements in any of them? new CachedHeaderFooter() { Ref = W.footerReference, Type = "default" }, }; - bool firstSection = true; + var firstSection = true; foreach (var sect in sections) { if (firstSection) @@ -509,7 +363,9 @@ are there any PtOpenXml.Insert elements in any of them? { var referenceElement = sect.Elements(hf.Ref).FirstOrDefault(z => (string)z.Attribute(W.type) == hf.Type); if (referenceElement != null) + { hf.CachedPartRid = (string)referenceElement.Attribute(R.id); + } } firstSection = false; continue; @@ -523,13 +379,12 @@ are there any PtOpenXml.Insert elements in any of them? CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.footerReference, "even"); CopyOrCacheHeaderOrFooter(output, cachedHeaderFooter, sect, W.footerReference, "default"); } - } } // Now can process PtOpenXml:Insert elements in headers / footers - int sourceNum = 0; - foreach (Source source in sources) + var sourceNum = 0; + foreach (var source in sources) { if (source.InsertId != null) { @@ -541,86 +396,99 @@ this uses an overload of AppendDocument that takes a part. are there any PtOpenXml.Insert elements in any of them? if so, then open and process all. #endif - bool foundInHeadersFooters = false; + var foundInHeadersFooters = false; if (output.MainDocumentPart.HeaderParts.Any(hp => { var hpXDoc = hp.GetXDocument(); return hpXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId); })) + { foundInHeadersFooters = true; + } + if (output.MainDocumentPart.FooterParts.Any(fp => { var hpXDoc = fp.GetXDocument(); return hpXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId); })) + { foundInHeadersFooters = true; + } if (foundInHeadersFooters) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument)) - using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) - { + using var streamDoc = new OpenXmlMemoryStreamDocument(source.WmlDocument); + using var doc = streamDoc.GetWordprocessingDocument(); #if TestForUnsupportedDocuments - // throws exceptions if a document contains unsupported content - TestForUnsupportedDocument(doc, sources.IndexOf(source)); + // throws exceptions if a document contains unsupported content + TestForUnsupportedDocument(doc, sources.IndexOf(source)); #endif - var partList = output.MainDocumentPart.HeaderParts.Cast().Concat(output.MainDocumentPart.FooterParts.Cast()).ToList(); - foreach (var part in partList) + var partList = output.MainDocumentPart.HeaderParts.Cast().Concat(output.MainDocumentPart.FooterParts.Cast()).ToList(); + foreach (var part in partList) + { + var partXDoc = part.GetXDocument(); + if (!partXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId)) { - var partXDoc = part.GetXDocument(); - if (!partXDoc.Descendants(PtOpenXml.Insert).Any(d => (string)d.Attribute(PtOpenXml.Id) == source.InsertId)) - continue; - List contents = doc.MainDocumentPart.GetXDocument() - .Root - .Element(W.body) - .Elements() - .Skip(source.Start) - .Take(source.Count) - .ToList(); - - try - { - AppendDocument(doc, output, part, contents, source.KeepSections, source.InsertId, images); - } - catch (DocumentBuilderInternalException dbie) + continue; + } + + var contents = doc.MainDocumentPart.GetXDocument() + .Root + .Element(W.body) + .Elements() + .Skip(source.Start) + .Take(source.Count) + .ToList(); + + try + { + AppendDocument(doc, output, part, contents, source.InsertId, images); + } + catch (DocumentBuilderInternalException dbie) + { + if (dbie.Message.Contains("{0}")) { - if (dbie.Message.Contains("{0}")) - throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum)); - else - throw dbie; + throw new DocumentBuilderException(string.Format(dbie.Message, sourceNum), dbie); } + throw; } } } else + { break; + } } } ++sourceNum; } if (sources.Any(s => s.KeepSections) && !output.MainDocumentPart.GetXDocument().Root.Descendants(W.sectPr).Any()) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument)) - using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) + using var streamDoc = new OpenXmlMemoryStreamDocument(sources[0].WmlDocument); + using var doc = streamDoc.GetWordprocessingDocument(); + var sectPr = doc.MainDocumentPart.GetXDocument().Root.Element(W.body) + .Elements().LastOrDefault(); + if (sectPr != null && sectPr.Name == W.sectPr) { - var sectPr = doc.MainDocumentPart.GetXDocument().Root.Element(W.body) - .Elements().LastOrDefault(); - if (sectPr != null && sectPr.Name == W.sectPr) - { - AddSectionAndDependencies(doc, output, sectPr, images); - output.MainDocumentPart.GetXDocument().Root.Element(W.body).Add(sectPr); - } + AddSectionAndDependencies(doc, output, sectPr, images); + output.MainDocumentPart.GetXDocument().Root.Element(W.body).Add(sectPr); } } AdjustDocPrIds(output); } if (wmlGlossaryDocument != null) + { WriteGlossaryDocumentPart(wmlGlossaryDocument, output, images); + } foreach (var part in output.GetAllParts()) + { if (part.Annotation() != null) + { part.PutXDocument(); + } + } } // there are two scenarios that need to be handled @@ -632,9 +500,9 @@ are there any PtOpenXml.Insert elements in any of them? // - mark the document as changed, and change it in the sources. private static List NormalizeStyleNamesAndIds(List sources) { - Dictionary styleNameMap = new Dictionary(); - HashSet styleIds = new HashSet(); - List newSources = new List(); + var styleNameMap = new Dictionary(); + var styleIds = new HashSet(); + var newSources = new List(); foreach (var src in sources) { @@ -646,13 +514,13 @@ private static List NormalizeStyleNamesAndIds(List sources) private static Source AddAndRectify(Source src, Dictionary styleNameMap, HashSet styleIds) { - bool modified = false; - using (MemoryStream ms = new MemoryStream()) + var modified = false; + using (var ms = new MemoryStream()) { ms.Write(src.WmlDocument.DocumentByteArray, 0, src.WmlDocument.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) + using (var wDoc = WordprocessingDocument.Open(ms, true)) { - Dictionary correctionList = new Dictionary(); + var correctionList = new Dictionary(); var thisStyleNameMap = GetStyleNameMap(wDoc); foreach (var pair in thisStyleNameMap) { @@ -669,7 +537,7 @@ private static Source AddAndRectify(Source src, Dictionary style while (true) { var newStyleId = GenStyleIdFromStyleName(styleName); - if (! styleIds.Contains(newStyleId)) + if (!styleIds.Contains(newStyleId)) { correctionList.Add(styleId, newStyleId); styleNameMap.Add(styleName, newStyleId); @@ -690,7 +558,10 @@ private static Source AddAndRectify(Source src, Dictionary style { // if the id is the same as the existing ID, then nothing to do if (styleNameMap[styleName] == styleId) + { continue; + } + correctionList.Add(styleId, styleNameMap[styleName]); } } @@ -703,9 +574,11 @@ private static Source AddAndRectify(Source src, Dictionary style if (modified) { var newWmlDocument = new WmlDocument(src.WmlDocument.FileName, ms.ToArray()); - var newSrc = new Source(newWmlDocument, src.Start, src.Count, src.KeepSections); - newSrc.DiscardHeadersAndFootersInKeptSections = src.DiscardHeadersAndFootersInKeptSections; - newSrc.InsertId = src.InsertId; + var newSrc = new Source(newWmlDocument, src.Start, src.Count, src.KeepSections) + { + DiscardHeadersAndFootersInKeptSections = src.DiscardHeadersAndFootersInKeptSections, + InsertId = src.InsertId + }; return newSrc; } } @@ -770,22 +643,41 @@ private static void AdjustStyleIdsForDocument(WordprocessingDocument wDoc, Dicti // update styles part UpdateStyleIdsForStylePart(wDoc.MainDocumentPart.StyleDefinitionsPart, correctionList); if (wDoc.MainDocumentPart.StylesWithEffectsPart != null) + { UpdateStyleIdsForStylePart(wDoc.MainDocumentPart.StylesWithEffectsPart, correctionList); + } // update content parts UpdateStyleIdsForContentPart(wDoc.MainDocumentPart, correctionList); foreach (var part in wDoc.MainDocumentPart.HeaderParts) + { UpdateStyleIdsForContentPart(part, correctionList); + } + foreach (var part in wDoc.MainDocumentPart.FooterParts) + { UpdateStyleIdsForContentPart(part, correctionList); + } + if (wDoc.MainDocumentPart.FootnotesPart != null) + { UpdateStyleIdsForContentPart(wDoc.MainDocumentPart.FootnotesPart, correctionList); + } + if (wDoc.MainDocumentPart.EndnotesPart != null) + { UpdateStyleIdsForContentPart(wDoc.MainDocumentPart.EndnotesPart, correctionList); + } + if (wDoc.MainDocumentPart.WordprocessingCommentsPart != null) + { UpdateStyleIdsForContentPart(wDoc.MainDocumentPart.WordprocessingCommentsPart, correctionList); + } + if (wDoc.MainDocumentPart.WordprocessingCommentsExPart != null) + { UpdateStyleIdsForContentPart(wDoc.MainDocumentPart.WordprocessingCommentsExPart, correctionList); + } // update numbering part UpdateStyleIdsForNumberingPart(wDoc.MainDocumentPart.NumberingDefinitionsPart, correctionList); @@ -825,11 +717,19 @@ private static void UpdateStyleIdsForNumberingPart(OpenXmlPart part, Dictionary< foreach (var item in numAttributeChangeList) { foreach (var att in item.PStyleAttributesToChange) + { att.Value = item.NewId; + } + foreach (var att in item.NumStyleLinkAttributesToChange) + { att.Value = item.NewId; + } + foreach (var att in item.StyleLinkAttributesToChange) + { att.Value = item.NewId; + } } part.PutXDocument(); } @@ -881,13 +781,24 @@ private static void UpdateStyleIdsForStylePart(OpenXmlPart part, Dictionary images) { - using (MemoryStream ms = new MemoryStream()) - { - ms.Write(wmlGlossaryDocument.DocumentByteArray, 0, wmlGlossaryDocument.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - var fromXDoc = wDoc.MainDocumentPart.GetXDocument(); - - var outputGlossaryDocumentPart = output.MainDocumentPart.AddNewPart(); - var newXDoc = new XDocument( - new XDeclaration(OnePointZero, Utf8, Yes), - new XElement(W.glossaryDocument, - NamespaceAttributes, - new XElement(W.docParts, - fromXDoc.Descendants(W.docPart)))); - outputGlossaryDocumentPart.PutXDocument(newXDoc); - - CopyGlossaryDocumentPartsToGD(wDoc, output, fromXDoc.Root.Descendants(W.docPart), images); - CopyRelatedPartsForContentParts(wDoc.MainDocumentPart, outputGlossaryDocumentPart, new[] { fromXDoc.Root }, images); - } - } + using var ms = new MemoryStream(); + ms.Write(wmlGlossaryDocument.DocumentByteArray, 0, wmlGlossaryDocument.DocumentByteArray.Length); + using var wDoc = WordprocessingDocument.Open(ms, true); + var fromXDoc = wDoc.MainDocumentPart.GetXDocument(); + + var outputGlossaryDocumentPart = output.MainDocumentPart.AddNewPart(); + var newXDoc = new XDocument( + new XDeclaration(OnePointZero, Utf8, Yes), + new XElement(W.glossaryDocument, + NamespaceAttributes, + new XElement(W.docParts, + fromXDoc.Descendants(W.docPart)))); + outputGlossaryDocumentPart.PutXDocument(newXDoc); + + CopyGlossaryDocumentPartsToGD(wDoc, output, fromXDoc.Root.Descendants(W.docPart), images); + CopyRelatedPartsForContentParts(wDoc.MainDocumentPart, outputGlossaryDocumentPart, new[] { fromXDoc.Root }, images); } - private static WmlDocument CoalesceGlossaryDocumentParts(IEnumerable sources, DocumentBuilderSettings settings) + private static WmlDocument? CoalesceGlossaryDocumentParts(IEnumerable sources) { - List allGlossaryDocuments = sources - .Select(s => DocumentBuilder.ExtractGlossaryDocument(s.WmlDocument)) + var allGlossaryDocuments = sources + .Select(s => ExtractGlossaryDocument(s.WmlDocument)) .Where(s => s != null) .Select(s => new Source(s)) .ToList(); if (!allGlossaryDocuments.Any()) + { return null; + } - WmlDocument coalescedRaw = DocumentBuilder.BuildDocument(allGlossaryDocuments); + var coalescedRaw = BuildDocument(allGlossaryDocuments); // now need to do some fix up - using (MemoryStream ms = new MemoryStream()) + using var ms = new MemoryStream(); + ms.Write(coalescedRaw.DocumentByteArray, 0, coalescedRaw.DocumentByteArray.Length); + using (var wDoc = WordprocessingDocument.Open(ms, true)) { - ms.Write(coalescedRaw.DocumentByteArray, 0, coalescedRaw.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, true)) - { - var mainXDoc = wDoc.MainDocumentPart.GetXDocument(); + var mainXDoc = wDoc.MainDocumentPart.GetXDocument(); - var newBody = new XElement(W.body, - new XElement(W.docParts, - mainXDoc.Root.Element(W.body).Elements(W.docParts).Elements(W.docPart))); + var newBody = new XElement(W.body, + new XElement(W.docParts, + mainXDoc.Root.Element(W.body).Elements(W.docParts).Elements(W.docPart))); - mainXDoc.Root.Element(W.body).ReplaceWith(newBody); + mainXDoc.Root.Element(W.body).ReplaceWith(newBody); - wDoc.MainDocumentPart.PutXDocument(); - } + wDoc.MainDocumentPart.PutXDocument(); + } - WmlDocument coalescedGlossaryDocument = new WmlDocument("Coalesced.docx", ms.ToArray()); + var coalescedGlossaryDocument = new WmlDocument("Coalesced.docx", ms.ToArray()); - return coalescedGlossaryDocument; - } + return coalescedGlossaryDocument; } private static void InitRelationshipMarkup() @@ -1085,18 +1000,19 @@ private static void InitRelationshipMarkup() private static void CopySpecifiedCustomXmlParts(WordprocessingDocument sourceDocument, WordprocessingDocument output, DocumentBuilderSettings settings) { if (settings.CustomXmlGuidList == null || !settings.CustomXmlGuidList.Any()) + { return; + } - foreach (CustomXmlPart customXmlPart in sourceDocument.MainDocumentPart.CustomXmlParts) + foreach (var customXmlPart in sourceDocument.MainDocumentPart.CustomXmlParts) { - OpenXmlPart propertyPart = customXmlPart + var propertyPart = customXmlPart .Parts .Select(p => p.OpenXmlPart) - .Where(p => p.ContentType == "application/vnd.openxmlformats-officedocument.customXmlProperties+xml") - .FirstOrDefault(); +.FirstOrDefault(p => p.ContentType == "application/vnd.openxmlformats-officedocument.customXmlProperties+xml"); if (propertyPart != null) { - XDocument propertyPartDoc = propertyPart.GetXDocument(); + var propertyPartDoc = propertyPart.GetXDocument(); #if false At various locations in Open-Xml-PowerTools, you will find examples of Open XML markup that is associated with code associated with querying or generating that markup. This is an example of the Custom XML Properties part. @@ -1111,11 +1027,11 @@ querying or generating that markup. This is an example of the Custom XML Proper itemID = itemID.Trim('{', '}'); if (settings.CustomXmlGuidList.Contains(itemID)) { - CustomXmlPart newPart = output.MainDocumentPart.AddCustomXmlPart(customXmlPart.ContentType); + var newPart = output.MainDocumentPart.AddCustomXmlPart(customXmlPart.ContentType); newPart.GetXDocument().Add(customXmlPart.GetXDocument().Root); - foreach (OpenXmlPart propPart in customXmlPart.Parts.Select(p => p.OpenXmlPart)) + foreach (var propPart in customXmlPart.Parts.Select(p => p.OpenXmlPart)) { - CustomXmlPropertiesPart newPropPart = newPart.AddNewPart(); + var newPropPart = newPart.AddNewPart(); newPropPart.GetXDocument().Add(propPart.GetXDocument().Root); } } @@ -1145,7 +1061,7 @@ private class CachedHeaderFooter private static void ProcessSectionsForLinkToPreviousHeadersAndFooters(WordprocessingDocument doc) { - CachedHeaderFooter[] cachedHeaderFooter = new[] + var cachedHeaderFooter = new[] { new CachedHeaderFooter() { Ref = W.headerReference, Type = "first" }, new CachedHeaderFooter() { Ref = W.headerReference, Type = "even" }, @@ -1172,42 +1088,64 @@ private static void ProcessSectionsForLinkToPreviousHeadersAndFooters(Wordproces if (headerEven == null) { if (headerDefault != null) - AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)headerDefault.Attribute(R.id), W.headerReference, "even"); + { + AddReferenceToExistingHeaderOrFooter(sect, (string)headerDefault.Attribute(R.id), W.headerReference, "even"); + } else + { InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.headerReference, "even"); + } } if (headerFirst == null) { if (headerDefault != null) - AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)headerDefault.Attribute(R.id), W.headerReference, "first"); + { + AddReferenceToExistingHeaderOrFooter(sect, (string)headerDefault.Attribute(R.id), W.headerReference, "first"); + } else + { InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.headerReference, "first"); + } } if (footerEven == null) { if (footerDefault != null) - AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)footerDefault.Attribute(R.id), W.footerReference, "even"); + { + AddReferenceToExistingHeaderOrFooter(sect, (string)footerDefault.Attribute(R.id), W.footerReference, "even"); + } else + { InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.footerReference, "even"); + } } if (footerFirst == null) { if (footerDefault != null) - AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, (string)footerDefault.Attribute(R.id), W.footerReference, "first"); + { + AddReferenceToExistingHeaderOrFooter(sect, (string)footerDefault.Attribute(R.id), W.footerReference, "first"); + } else + { InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, W.footerReference, "first"); + } } foreach (var hf in cachedHeaderFooter) { if (sect.Elements(hf.Ref).FirstOrDefault(z => (string)z.Attribute(W.type) == hf.Type) == null) + { InitEmptyHeaderOrFooter(doc.MainDocumentPart, sect, hf.Ref, hf.Type); + } + var reference = sect.Elements(hf.Ref).FirstOrDefault(z => (string)z.Attribute(W.type) == hf.Type); if (reference == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } + hf.CachedPartRid = (string)reference.Attribute(R.id); } firstSection = false; @@ -1231,8 +1169,11 @@ private static void CopyOrCacheHeaderOrFooter(WordprocessingDocument doc, Cached var referenceElement = FindReference(sect, referenceXName, type); if (referenceElement == null) { - var cachedPartRid = cachedHeaderFooter.FirstOrDefault(z => z.Ref == referenceXName && z.Type == type).CachedPartRid; - AddReferenceToExistingHeaderOrFooter(doc.MainDocumentPart, sect, cachedPartRid, referenceXName, type); + var cachedPartRid = cachedHeaderFooter.FirstOrDefault(z => z.Ref == referenceXName && z.Type == type)?.CachedPartRid; + if (cachedPartRid != null) + { + AddReferenceToExistingHeaderOrFooter(sect, cachedPartRid, referenceXName, type); + } } else { @@ -1249,7 +1190,7 @@ private static XElement FindReference(XElement sect, XName reference, string typ }); } - private static void AddReferenceToExistingHeaderOrFooter(MainDocumentPart mainDocPart, XElement sect, string rId, XName reference, string toType) + private static void AddReferenceToExistingHeaderOrFooter(XElement sect, string rId, XName reference, string toType) { if (reference == W.headerReference) { @@ -1269,7 +1210,7 @@ private static void AddReferenceToExistingHeaderOrFooter(MainDocumentPart mainDo private static void InitEmptyHeaderOrFooter(MainDocumentPart mainDocPart, XElement sect, XName referenceXName, string toType) { - XDocument xDoc = null; + XDocument xDoc; if (referenceXName == W.headerReference) { xDoc = XDocument.Parse( @@ -1347,43 +1288,57 @@ private static void InitEmptyHeaderOrFooter(MainDocumentPart mainDocPart, XEleme private static void TestPartForUnsupportedContent(OpenXmlPart part, int sourceNumber) { - XNamespace[] obsoleteNamespaces = new[] + var obsoleteNamespaces = new[] { XNamespace.Get("http://schemas.microsoft.com/office/word/2007/5/30/wordml"), XNamespace.Get("http://schemas.microsoft.com/office/word/2008/9/16/wordprocessingDrawing"), XNamespace.Get("http://schemas.microsoft.com/office/word/2009/2/wordml"), }; - XDocument xDoc = part.GetXDocument(); - XElement invalidElement = xDoc.Descendants() + var xDoc = part.GetXDocument(); + var invalidElement = xDoc.Descendants() .FirstOrDefault(d => { - bool b = d.Name == W.subDoc || + var b = d.Name == W.subDoc || d.Name == W.control || d.Name == W.altChunk || d.Name.LocalName == "contentPart" || obsoleteNamespaces.Contains(d.Name.Namespace); - bool b2 = b || + var b2 = b || d.Attributes().Any(a => obsoleteNamespaces.Contains(a.Name.Namespace)); return b2; }); if (invalidElement != null) { if (invalidElement.Name == W.subDoc) - throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains sub document", + { + throw new DocumentBuilderException(string.Format("Source {0} is unsupported document - contains sub document", sourceNumber)); + } + if (invalidElement.Name == W.control) - throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains ActiveX controls", + { + throw new DocumentBuilderException(string.Format("Source {0} is unsupported document - contains ActiveX controls", sourceNumber)); + } + if (invalidElement.Name == W.altChunk) - throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains altChunk", + { + throw new DocumentBuilderException(string.Format("Source {0} is unsupported document - contains altChunk", sourceNumber)); + } + if (invalidElement.Name.LocalName == "contentPart") - throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains contentPart content", + { + throw new DocumentBuilderException(string.Format("Source {0} is unsupported document - contains contentPart content", sourceNumber)); + } + if (obsoleteNamespaces.Contains(invalidElement.Name.Namespace) || invalidElement.Attributes().Any(a => obsoleteNamespaces.Contains(a.Name.Namespace))) - throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains obsolete namespace", + { + throw new DocumentBuilderException(string.Format("Source {0} is unsupported document - contains obsolete namespace", sourceNumber)); + } } } @@ -1398,67 +1353,88 @@ private static void TestPartForUnsupportedContent(OpenXmlPart part, int sourceNu private static void TestForUnsupportedDocument(WordprocessingDocument doc, int sourceNumber) { if (doc.MainDocumentPart.GetXDocument().Root == null) + { throw new DocumentBuilderException(string.Format("Source {0} is an invalid document - MainDocumentPart contains no content.", sourceNumber)); + } - if ((string)doc.MainDocumentPart.GetXDocument().Root.Name.NamespaceName == "http://purl.oclc.org/ooxml/wordprocessingml/main") + if (doc.MainDocumentPart.GetXDocument().Root.Name.NamespaceName == "http://purl.oclc.org/ooxml/wordprocessingml/main") + { throw new DocumentBuilderException(string.Format("Source {0} is saved in strict mode, not supported", sourceNumber)); + } // note: if ever want to support section changes, need to address the code that rationalizes headers and footers, propagating to sections that inherit headers/footers from prev section foreach (var d in doc.MainDocumentPart.GetXDocument().Descendants()) { if (d.Name == W.sectPrChange) + { throw new DocumentBuilderException(string.Format("Source {0} contains section changes (w:sectPrChange), not supported", sourceNumber)); - - // note: if ever want to support Open-Xml-PowerTools attributes, need to make sure that all attributes are propagated in all cases - //if (d.Name.Namespace == PtOpenXml.ptOpenXml || - // d.Name.Namespace == PtOpenXml.pt) - // throw new DocumentBuilderException(string.Format("Source {0} contains Open-Xml-PowerTools markup, not supported", sourceNumber)); - //if (d.Attributes().Any(a => a.Name.Namespace == PtOpenXml.ptOpenXml || a.Name.Namespace == PtOpenXml.pt)) - // throw new DocumentBuilderException(string.Format("Source {0} contains Open-Xml-PowerTools markup, not supported", sourceNumber)); + } } TestPartForUnsupportedContent(doc.MainDocumentPart, sourceNumber); foreach (var hdr in doc.MainDocumentPart.HeaderParts) + { TestPartForUnsupportedContent(hdr, sourceNumber); + } + foreach (var ftr in doc.MainDocumentPart.FooterParts) + { TestPartForUnsupportedContent(ftr, sourceNumber); + } + if (doc.MainDocumentPart.FootnotesPart != null) + { TestPartForUnsupportedContent(doc.MainDocumentPart.FootnotesPart, sourceNumber); + } + if (doc.MainDocumentPart.EndnotesPart != null) + { TestPartForUnsupportedContent(doc.MainDocumentPart.EndnotesPart, sourceNumber); + } if (doc.MainDocumentPart.DocumentSettingsPart != null && doc.MainDocumentPart.DocumentSettingsPart.GetXDocument().Descendants().Any(d => d.Name == W.src || d.Name == W.recipientData || d.Name == W.mailMerge)) - throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains Mail Merge content", + { + throw new DocumentBuilderException(string.Format("Source {0} is unsupported document - contains Mail Merge content", sourceNumber)); + } + if (doc.MainDocumentPart.WebSettingsPart != null && doc.MainDocumentPart.WebSettingsPart.GetXDocument().Descendants().Any(d => d.Name == W.frameset)) - throw new DocumentBuilderException(String.Format("Source {0} is unsupported document - contains a frameset", sourceNumber)); + { + throw new DocumentBuilderException(string.Format("Source {0} is unsupported document - contains a frameset", sourceNumber)); + } + var numberingElements = doc.MainDocumentPart .GetXDocument() .Descendants(W.numPr) .Where(n => { - bool zeroId = (int?)n.Attribute(W.id) == 0; - bool hasChildInsId = n.Elements(W.ins).Any(); + var zeroId = (int?)n.Attribute(W.id) == 0; + var hasChildInsId = n.Elements(W.ins).Any(); if (zeroId || hasChildInsId) + { return false; + } + return true; }) .ToList(); if (numberingElements.Any() && doc.MainDocumentPart.NumberingDefinitionsPart == null) - throw new DocumentBuilderException(String.Format( + { + throw new DocumentBuilderException(string.Format( "Source {0} is invalid document - contains numbering markup but no numbering part", sourceNumber)); + } } private static void FixUpSectionProperties(WordprocessingDocument newDocument) { - XDocument mainDocumentXDoc = newDocument.MainDocumentPart.GetXDocument(); + var mainDocumentXDoc = newDocument.MainDocumentPart.GetXDocument(); mainDocumentXDoc.Declaration.Standalone = Yes; mainDocumentXDoc.Declaration.Encoding = Utf8; - XElement body = mainDocumentXDoc.Root.Element(W.body); + var body = mainDocumentXDoc.Root.Element(W.body); var sectionPropertiesToMove = body .Elements() .Take(body.Elements().Count() - 1) @@ -1468,11 +1444,16 @@ private static void FixUpSectionProperties(WordprocessingDocument newDocument) { var p = s.SiblingsBeforeSelfReverseDocumentOrder().First(); if (p.Element(W.pPr) == null) + { p.AddFirst(new XElement(W.pPr)); + } + p.Element(W.pPr).Add(s); } foreach (var s in sectionPropertiesToMove) + { s.Remove(); + } } private static void AddSectionAndDependencies(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, @@ -1481,8 +1462,8 @@ private static void AddSectionAndDependencies(WordprocessingDocument sourceDocum var headerReferences = sectionMarkup.Elements(W.headerReference); foreach (var headerReference in headerReferences) { - string oldRid = headerReference.Attribute(R.id).Value; - HeaderPart oldHeaderPart = null; + var oldRid = headerReference.Attribute(R.id).Value; + HeaderPart? oldHeaderPart = null; try { oldHeaderPart = (HeaderPart)sourceDocument.MainDocumentPart.GetPartById(oldRid); @@ -1492,11 +1473,14 @@ private static void AddSectionAndDependencies(WordprocessingDocument sourceDocum var message = string.Format("ArgumentOutOfRangeException, attempting to get header rId={0}", oldRid); throw new OpenXmlPowerToolsException(message); } - XDocument oldHeaderXDoc = oldHeaderPart.GetXDocument(); + var oldHeaderXDoc = oldHeaderPart.GetXDocument(); if (oldHeaderXDoc != null && oldHeaderXDoc.Root != null) + { CopyNumbering(sourceDocument, newDocument, new[] { oldHeaderXDoc.Root }, images); - HeaderPart newHeaderPart = newDocument.MainDocumentPart.AddNewPart(); - XDocument newHeaderXDoc = newHeaderPart.GetXDocument(); + } + + var newHeaderPart = newDocument.MainDocumentPart.AddNewPart(); + var newHeaderXDoc = newHeaderPart.GetXDocument(); newHeaderXDoc.Declaration.Standalone = Yes; newHeaderXDoc.Declaration.Encoding = Utf8; newHeaderXDoc.Add(oldHeaderXDoc.Root); @@ -1508,17 +1492,22 @@ private static void AddSectionAndDependencies(WordprocessingDocument sourceDocum var footerReferences = sectionMarkup.Elements(W.footerReference); foreach (var footerReference in footerReferences) { - string oldRid = footerReference.Attribute(R.id).Value; + var oldRid = footerReference.Attribute(R.id).Value; var oldFooterPart2 = sourceDocument.MainDocumentPart.GetPartById(oldRid); if (!(oldFooterPart2 is FooterPart)) + { throw new DocumentBuilderException("Invalid document - invalid footer part."); + } - FooterPart oldFooterPart = (FooterPart)oldFooterPart2; - XDocument oldFooterXDoc = oldFooterPart.GetXDocument(); + var oldFooterPart = (FooterPart)oldFooterPart2; + var oldFooterXDoc = oldFooterPart.GetXDocument(); if (oldFooterXDoc != null && oldFooterXDoc.Root != null) + { CopyNumbering(sourceDocument, newDocument, new[] { oldFooterXDoc.Root }, images); - FooterPart newFooterPart = newDocument.MainDocumentPart.AddNewPart(); - XDocument newFooterXDoc = newFooterPart.GetXDocument(); + } + + var newFooterPart = newDocument.MainDocumentPart.AddNewPart(); + var newFooterXDoc = newFooterPart.GetXDocument(); newFooterXDoc.Declaration.Standalone = Yes; newFooterXDoc.Declaration.Encoding = Utf8; newFooterXDoc.Add(oldFooterXDoc.Root); @@ -1534,9 +1523,11 @@ private static void MergeStyles(WordprocessingDocument sourceDocument, Wordproce var newIds = new Dictionary(); #endif if (fromStyles.Root == null) + { return; + } - foreach (XElement style in fromStyles.Root.Elements(W.style)) + foreach (var style in fromStyles.Root.Elements(W.style)) { var fromId = (string)style.Attribute(W.styleId); var fromName = (string)style.Elements(W.name).Attributes(W.val).FirstOrDefault(); @@ -1550,47 +1541,41 @@ private static void MergeStyles(WordprocessingDocument sourceDocument, Wordproce { #if MergeStylesWithSameNames var linkElement = style.Element(W.link); - string linkedId; - if (linkElement != null && newIds.TryGetValue(linkElement.Attribute(W.val).Value, out linkedId)) + if (linkElement != null && newIds.TryGetValue(linkElement.Attribute(W.val).Value, out var linkedId)) { var linkedStyle = toStyles.Root.Elements(W.style) .First(o => o.Attribute(W.styleId).Value == linkedId); if (linkedStyle.Element(W.link) != null) + { newIds.Add(fromId, linkedStyle.Element(W.link).Attribute(W.val).Value); + } + continue; } - //string name = (string)style.Elements(W.name).Attributes(W.val).FirstOrDefault(); - //var namedStyle = toStyles - // .Root - // .Elements(W.style) - // .Where(st => st.Element(W.name) != null) - // .FirstOrDefault(o => (string)o.Element(W.name).Attribute(W.val) == name); - //if (namedStyle != null) - //{ - // if (! newIds.ContainsKey(fromId)) - // newIds.Add(fromId, namedStyle.Attribute(W.styleId).Value); - // continue; - //} #endif - int number = 1; - int abstractNumber = 0; - XDocument oldNumbering = null; - XDocument newNumbering = null; - foreach (XElement numReference in style.Descendants(W.numPr)) + var number = 1; + var abstractNumber = 0; + XDocument? oldNumbering = null; + XDocument? newNumbering = null; + foreach (var numReference in style.Descendants(W.numPr)) { - XElement idElement = numReference.Descendants(W.numId).FirstOrDefault(); + var idElement = numReference.Descendants(W.numId).FirstOrDefault(); if (idElement != null) { if (oldNumbering == null) { if (sourceDocument.MainDocumentPart.NumberingDefinitionsPart != null) + { oldNumbering = sourceDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument(); + } else { - oldNumbering = new XDocument(); - oldNumbering.Declaration = new XDeclaration(OnePointZero, Utf8, Yes); + oldNumbering = new XDocument + { + Declaration = new XDeclaration(OnePointZero, Utf8, Yes) + }; oldNumbering.Add(new XElement(W.numbering, NamespaceAttributes)); } } @@ -1606,13 +1591,18 @@ private static void MergeStyles(WordprocessingDocument sourceDocument, Wordproce .Elements(W.num) .Select(f => (int)f.Attribute(W.numId)); if (numIds.Any()) + { number = numIds.Max() + 1; + } + numIds = newNumbering .Root .Elements(W.abstractNum) .Select(f => (int)f.Attribute(W.abstractNumId)); if (numIds.Any()) + { abstractNumber = numIds.Max() + 1; + } } else { @@ -1623,17 +1613,16 @@ private static void MergeStyles(WordprocessingDocument sourceDocument, Wordproce newNumbering.Add(new XElement(W.numbering, NamespaceAttributes)); } } - string numId = idElement.Attribute(W.val).Value; + var numId = idElement.Attribute(W.val).Value; if (numId != "0") { - XElement element = oldNumbering + var element = oldNumbering .Descendants() .Elements(W.num) - .Where(p => ((string)p.Attribute(W.numId)) == numId) - .FirstOrDefault(); +.FirstOrDefault(p => (string)p.Attribute(W.numId) == numId); // Copy abstract numbering element, if necessary (use matching NSID) - string abstractNumId = string.Empty; + var abstractNumId = string.Empty; if (element != null) { abstractNumId = element @@ -1642,71 +1631,80 @@ private static void MergeStyles(WordprocessingDocument sourceDocument, Wordproce .Attribute(W.val) .Value; - XElement abstractElement = oldNumbering + var abstractElement = oldNumbering .Descendants() .Elements(W.abstractNum) - .Where(p => ((string)p.Attribute(W.abstractNumId)) == abstractNumId) - .FirstOrDefault(); - string abstractNSID = string.Empty; +.FirstOrDefault(p => (string)p.Attribute(W.abstractNumId) == abstractNumId); + var abstractNSID = string.Empty; if (abstractElement != null) { - XElement nsidElement = abstractElement + var nsidElement = abstractElement .Element(W.nsid); abstractNSID = null; if (nsidElement != null) + { abstractNSID = (string)nsidElement .Attribute(W.val); + } - XElement newAbstractElement = newNumbering + var newAbstractElement = newNumbering .Descendants() .Elements(W.abstractNum) .Where(e => e.Annotation() == null) - .Where(p => +.FirstOrDefault(p => { var thisNsidElement = p.Element(W.nsid); if (thisNsidElement == null) + { return false; + } + return (string)thisNsidElement.Attribute(W.val) == abstractNSID; - }) - .FirstOrDefault(); + }); if (newAbstractElement == null) { newAbstractElement = new XElement(abstractElement); newAbstractElement.Attribute(W.abstractNumId).Value = abstractNumber.ToString(); abstractNumber++; if (newNumbering.Root.Elements(W.abstractNum).Any()) + { newNumbering.Root.Elements(W.abstractNum).Last().AddAfterSelf(newAbstractElement); + } else + { newNumbering.Root.Add(newAbstractElement); + } - foreach (XElement pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) + foreach (var pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) { - string bulletId = (string)pictId.Attribute(W.val); - XElement numPicBullet = oldNumbering + var bulletId = (string)pictId.Attribute(W.val); + var numPicBullet = oldNumbering .Descendants(W.numPicBullet) .FirstOrDefault(d => (string)d.Attribute(W.numPicBulletId) == bulletId); - int maxNumPicBulletId = new int[] { -1 }.Concat( + var maxNumPicBulletId = new int[] { -1 }.Concat( newNumbering.Descendants(W.numPicBullet) .Attributes(W.numPicBulletId) .Select(a => (int)a)) .Max() + 1; - XElement newNumPicBullet = new XElement(numPicBullet); + var newNumPicBullet = new XElement(numPicBullet); newNumPicBullet.Attribute(W.numPicBulletId).Value = maxNumPicBulletId.ToString(); pictId.Attribute(W.val).Value = maxNumPicBulletId.ToString(); newNumbering.Root.AddFirst(newNumPicBullet); } } - string newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; + var newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; // Copy numbering element, if necessary (use matching element with no overrides) - XElement newElement = null; + XElement? newElement = null; if (!element.Elements(W.lvlOverride).Any()) + { newElement = newNumbering .Descendants() .Elements(W.num) - .Where(p => !p.Elements(W.lvlOverride).Any() && - ((string)p.Elements(W.abstractNumId).First().Attribute(W.val)) == newAbstractId) - .FirstOrDefault(); +.FirstOrDefault(p => !p.Elements(W.lvlOverride).Any() && + (string)p.Elements(W.abstractNumId).First().Attribute(W.val) == newAbstractId); + } + if (newElement == null) { newElement = new XElement(element); @@ -1734,10 +1732,9 @@ private static void MergeStyles(WordprocessingDocument sourceDocument, Wordproce else { var toId = (string)toStyle.Attribute(W.styleId); - if (fromId != toId) + if (fromId != toId && !newIds.ContainsKey(fromId)) { - if (! newIds.ContainsKey(fromId)) - newIds.Add(fromId, toId); + newIds.Add(fromId, toId); } } } @@ -1754,9 +1751,7 @@ private static void MergeStyles(WordprocessingDocument sourceDocument, Wordproce } foreach (var item in newContent.DescendantsAndSelf() - .Where(d => d.Name == W.pStyle || - d.Name == W.rStyle || - d.Name == W.tblStyle)) + .Where(d => d.Name == W.pStyle || d.Name == W.rStyle || d.Name == W.tblStyle)) { ConvertToNewId(item, newIds); } @@ -1781,7 +1776,9 @@ private static void MergeLatentStyles(XDocument fromStyles, XDocument toStyles) { var fromLatentStyles = fromStyles.Descendants(W.latentStyles).FirstOrDefault(); if (fromLatentStyles == null) + { return; + } var toLatentStyles = toStyles.Descendants(W.latentStyles).FirstOrDefault(); if (toLatentStyles == null) @@ -1798,26 +1795,39 @@ private static void MergeLatentStyles(XDocument fromStyles, XDocument toStyles) .Elements(W.style) .FirstOrDefault(); if (firstStyle == null) + { toStyles.Root.Add(newLatentStylesElement); + } else + { firstStyle.AddBeforeSelf(newLatentStylesElement); + } } else + { globalDefaults.AddAfterSelf(newLatentStylesElement); + } } toLatentStyles = toStyles.Descendants(W.latentStyles).FirstOrDefault(); if (toLatentStyles == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } var toStylesHash = new HashSet(); foreach (var lse in toLatentStyles.Elements(W.lsdException)) + { toStylesHash.Add((string)lse.Attribute(W.name)); + } foreach (var fls in fromLatentStyles.Elements(W.lsdException)) { var name = (string)fls.Attribute(W.name); if (toStylesHash.Contains(name)) + { continue; + } + toLatentStyles.Add(fls); toStylesHash.Add(name); } @@ -1839,14 +1849,16 @@ private static void MergeDocDefaultStyles(XDocument xDocument, XDocument newXDoc } #if MergeStylesWithSameNames + private static void ConvertToNewId(XElement element, Dictionary newIds) { if (element == null) + { return; + } var valueAttribute = element.Attribute(W.val); - string newId; - if (newIds.TryGetValue(valueAttribute.Value, out newId)) + if (newIds.TryGetValue(valueAttribute.Value, out var newId)) { valueAttribute.Value = newId; } @@ -1871,19 +1883,21 @@ private static void ConvertNumberingPartToNewIds(XDocument newNumbering, Diction ConvertToNewId(item, newIds); } } + #endif private static void MergeFontTables(XDocument fromFontTable, XDocument toFontTable) { - foreach (XElement font in fromFontTable.Root.Elements(W.font)) + foreach (var font in fromFontTable.Root.Elements(W.font)) { - string name = font.Attribute(W.name).Value; - if (toFontTable + var name = font.Attribute(W.name).Value; + if (!toFontTable .Root .Elements(W.font) - .Where(o => o.Attribute(W.name).Value == name) - .Count() == 0) +.Any(o => o.Attribute(W.name).Value == name)) + { toFontTable.Root.Add(new XElement(font)); + } } } @@ -1893,18 +1907,18 @@ private static void CopyStylesAndFonts(WordprocessingDocument sourceDocument, Wo // Copy all styles to the new document if (sourceDocument.MainDocumentPart.StyleDefinitionsPart != null) { - XDocument oldStyles = sourceDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var oldStyles = sourceDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); if (newDocument.MainDocumentPart.StyleDefinitionsPart == null) { newDocument.MainDocumentPart.AddNewPart(); - XDocument newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); newStyles.Declaration.Standalone = Yes; newStyles.Declaration.Encoding = Utf8; newStyles.Add(oldStyles.Root); } else { - XDocument newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); MergeStyles(sourceDocument, newDocument, oldStyles, newStyles, newContent); MergeLatentStyles(oldStyles, newStyles); } @@ -1913,18 +1927,18 @@ private static void CopyStylesAndFonts(WordprocessingDocument sourceDocument, Wo // Copy all styles with effects to the new document if (sourceDocument.MainDocumentPart.StylesWithEffectsPart != null) { - XDocument oldStyles = sourceDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument(); + var oldStyles = sourceDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument(); if (newDocument.MainDocumentPart.StylesWithEffectsPart == null) { newDocument.MainDocumentPart.AddNewPart(); - XDocument newStyles = newDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument(); + var newStyles = newDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument(); newStyles.Declaration.Standalone = Yes; newStyles.Declaration.Encoding = Utf8; newStyles.Add(oldStyles.Root); } else { - XDocument newStyles = newDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument(); + var newStyles = newDocument.MainDocumentPart.StylesWithEffectsPart.GetXDocument(); MergeStyles(sourceDocument, newDocument, oldStyles, newStyles, newContent); MergeLatentStyles(oldStyles, newStyles); } @@ -1933,18 +1947,18 @@ private static void CopyStylesAndFonts(WordprocessingDocument sourceDocument, Wo // Copy fontTable to the new document if (sourceDocument.MainDocumentPart.FontTablePart != null) { - XDocument oldFontTable = sourceDocument.MainDocumentPart.FontTablePart.GetXDocument(); + var oldFontTable = sourceDocument.MainDocumentPart.FontTablePart.GetXDocument(); if (newDocument.MainDocumentPart.FontTablePart == null) { newDocument.MainDocumentPart.AddNewPart(); - XDocument newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); + var newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); newFontTable.Declaration.Standalone = Yes; newFontTable.Declaration.Encoding = Utf8; newFontTable.Add(oldFontTable.Root); } else { - XDocument newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); + var newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); MergeFontTables(oldFontTable, newFontTable); } } @@ -1953,14 +1967,17 @@ private static void CopyStylesAndFonts(WordprocessingDocument sourceDocument, Wo private static void CopyComments(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, IEnumerable newContent, List images) { - Dictionary commentIdMap = new Dictionary(); - int number = 0; - XDocument oldComments = null; - XDocument newComments = null; - foreach (XElement comment in newContent.DescendantsAndSelf(W.commentReference)) + var commentIdMap = new Dictionary(); + var number = 0; + XDocument? oldComments = null; + XDocument? newComments = null; + foreach (var comment in newContent.DescendantsAndSelf(W.commentReference)) { if (oldComments == null) + { oldComments = sourceDocument.MainDocumentPart.WordprocessingCommentsPart.GetXDocument(); + } + if (newComments == null) { if (newDocument.MainDocumentPart.WordprocessingCommentsPart != null) @@ -1970,7 +1987,9 @@ private static void CopyComments(WordprocessingDocument sourceDocument, Wordproc newComments.Declaration.Encoding = Utf8; var ids = newComments.Root.Elements(W.comment).Select(f => (int)f.Attribute(W.id)); if (ids.Any()) + { number = ids.Max() + 1; + } } else { @@ -1981,26 +2000,36 @@ private static void CopyComments(WordprocessingDocument sourceDocument, Wordproc newComments.Add(new XElement(W.comments, NamespaceAttributes)); } } - int id; - if (!int.TryParse((string)comment.Attribute(W.id), out id)) + if (!int.TryParse((string)comment.Attribute(W.id), out var id)) + { throw new DocumentBuilderException("Invalid document - invalid comment id"); - XElement element = oldComments + } + + var element = oldComments .Descendants() .Elements(W.comment) - .Where(p => { - int thisId; - if (! int.TryParse((string)p.Attribute(W.id), out thisId)) +.FirstOrDefault(p => + { + if (!int.TryParse((string)p.Attribute(W.id), out var thisId)) + { throw new DocumentBuilderException("Invalid document - invalid comment id"); + } + return thisId == id; - }) - .FirstOrDefault(); + }); if (element == null) + { throw new DocumentBuilderException("Invalid document - comment reference without associated comment in comments part"); - XElement newElement = new XElement(element); + } + + var newElement = new XElement(element); newElement.Attribute(W.id).Value = number.ToString(); newComments.Root.Add(newElement); - if (! commentIdMap.ContainsKey(id)) + if (!commentIdMap.ContainsKey(id)) + { commentIdMap.Add(id, number); + } + number++; } foreach (var item in newContent.DescendantsAndSelf() @@ -2010,7 +2039,9 @@ private static void CopyComments(WordprocessingDocument sourceDocument, Wordproc .ToList()) { if (commentIdMap.ContainsKey((int)item.Attribute(W.id))) + { item.Attribute(W.id).Value = commentIdMap[(int)item.Attribute(W.id)].ToString(); + } } if (sourceDocument.MainDocumentPart.WordprocessingCommentsPart != null && newDocument.MainDocumentPart.WordprocessingCommentsPart != null) @@ -2025,56 +2056,76 @@ private static void CopyComments(WordprocessingDocument sourceDocument, Wordproc } } - private static void AdjustUniqueIds(WordprocessingDocument sourceDocument, - WordprocessingDocument newDocument, IEnumerable newContent) + private static void AdjustUniqueIds(WordprocessingDocument newDocument, IEnumerable newContent) { // adjust bookmark unique ids - int maxId = 0; + var maxId = 0; if (newDocument.MainDocumentPart.GetXDocument().Descendants(W.bookmarkStart).Any()) + { maxId = newDocument.MainDocumentPart.GetXDocument().Descendants(W.bookmarkStart) .Select(d => (int)d.Attribute(W.id)).Max(); - Dictionary bookmarkIdMap = new Dictionary(); + } + + var bookmarkIdMap = new Dictionary(); foreach (var item in newContent.DescendantsAndSelf().Where(bm => bm.Name == W.bookmarkStart || bm.Name == W.bookmarkEnd)) { - int id; - if (!int.TryParse((string)item.Attribute(W.id), out id)) + if (!int.TryParse((string)item.Attribute(W.id), out var id)) + { throw new DocumentBuilderException("Invalid document - invalid value for bookmark ID"); + } + if (!bookmarkIdMap.ContainsKey(id)) + { bookmarkIdMap.Add(id, ++maxId); + } } foreach (var bookmarkElement in newContent.DescendantsAndSelf().Where(e => e.Name == W.bookmarkStart || e.Name == W.bookmarkEnd)) + { bookmarkElement.Attribute(W.id).Value = bookmarkIdMap[(int)bookmarkElement.Attribute(W.id)].ToString(); - - // adjust shape unique ids - // This doesn't work because OLEObjects refer to shapes by ID. - // Punting on this, because sooner or later, this will be a non-issue. - //foreach (var item in newContent.DescendantsAndSelf(VML.shape)) - //{ - // Guid g = Guid.NewGuid(); - // string s = "R" + g.ToString().Replace("-", ""); - // item.Attribute(NoNamespace.id).Value = s; - //} + } } private static void AdjustDocPrIds(WordprocessingDocument newDocument) { - int docPrId = 0; + var docPrId = 0; foreach (var item in newDocument.MainDocumentPart.GetXDocument().Descendants(WP.docPr)) + { item.Attribute(NoNamespace.id).Value = (++docPrId).ToString(); + } + foreach (var header in newDocument.MainDocumentPart.HeaderParts) + { foreach (var item in header.GetXDocument().Descendants(WP.docPr)) + { item.Attribute(NoNamespace.id).Value = (++docPrId).ToString(); + } + } + foreach (var footer in newDocument.MainDocumentPart.FooterParts) + { foreach (var item in footer.GetXDocument().Descendants(WP.docPr)) + { item.Attribute(NoNamespace.id).Value = (++docPrId).ToString(); + } + } + if (newDocument.MainDocumentPart.FootnotesPart != null) + { foreach (var item in newDocument.MainDocumentPart.FootnotesPart.GetXDocument().Descendants(WP.docPr)) + { item.Attribute(NoNamespace.id).Value = (++docPrId).ToString(); + } + } + if (newDocument.MainDocumentPart.EndnotesPart != null) + { foreach (var item in newDocument.MainDocumentPart.EndnotesPart.GetXDocument().Descendants(WP.docPr)) + { item.Attribute(NoNamespace.id).Value = (++docPrId).ToString(); + } + } } // This probably doesn't need to be done, except that the Open XML SDK will not validate @@ -2086,11 +2137,13 @@ private static void RemoveGfxdata(IEnumerable newContent) private static object InsertTransform(XNode node, List newContent) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Annotation() != null) + { return newContent; + } + return new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => InsertTransform(n, newContent))); @@ -2098,7 +2151,8 @@ private static object InsertTransform(XNode node, List newContent) return node; } - private class ReplaceSemaphore { } + private class ReplaceSemaphore + { } // Rules for sections // - if KeepSections for all documents in the source collection are false, then it takes the section @@ -2109,7 +2163,7 @@ private class ReplaceSemaphore { } // - if you specify true for any document, and there are no sections for any paragraphs, then no // sections are copied. private static void AppendDocument(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, - List newContent, bool keepSection, string insertId, List images) + List newContent, bool keepSection, string? insertId, List images) { FixRanges(sourceDocument.MainDocumentPart.GetXDocument(), newContent); AddRelationships(sourceDocument.MainDocumentPart, newDocument.MainDocumentPart, newContent); @@ -2117,45 +2171,59 @@ private static void AppendDocument(WordprocessingDocument sourceDocument, Wordpr newContent, images); // Append contents - XDocument newMainXDoc = newDocument.MainDocumentPart.GetXDocument(); + var newMainXDoc = newDocument.MainDocumentPart.GetXDocument(); newMainXDoc.Declaration.Standalone = Yes; newMainXDoc.Declaration.Encoding = Utf8; - if (keepSection == false) + if (!keepSection) { - List adjustedContents = newContent.Where(e => e.Name != W.sectPr).ToList(); + var adjustedContents = newContent.Where(e => e.Name != W.sectPr).ToList(); adjustedContents.DescendantsAndSelf(W.sectPr).Remove(); newContent = adjustedContents; } var listOfSectionProps = newContent.DescendantsAndSelf(W.sectPr).ToList(); foreach (var sectPr in listOfSectionProps) + { AddSectionAndDependencies(sourceDocument, newDocument, sectPr, images); + } + CopyStylesAndFonts(sourceDocument, newDocument, newContent); CopyNumbering(sourceDocument, newDocument, newContent, images); CopyComments(sourceDocument, newDocument, newContent, images); CopyFootnotes(sourceDocument, newDocument, newContent, images); CopyEndnotes(sourceDocument, newDocument, newContent, images); - AdjustUniqueIds(sourceDocument, newDocument, newContent); + AdjustUniqueIds(newDocument, newContent); RemoveGfxdata(newContent); CopyCustomXmlPartsForDataBoundContentControls(sourceDocument, newDocument, newContent); CopyWebExtensions(sourceDocument, newDocument); if (insertId != null) { - XElement insertElementToReplace = newMainXDoc + var insertElementToReplace = newMainXDoc .Descendants(PtOpenXml.Insert) .FirstOrDefault(i => (string)i.Attribute(PtOpenXml.Id) == insertId); if (insertElementToReplace != null) + { insertElementToReplace.AddAnnotation(new ReplaceSemaphore()); + } + newMainXDoc.Element(W.document).ReplaceWith((XElement)InsertTransform(newMainXDoc.Root, newContent)); } else + { newMainXDoc.Root.Element(W.body).Add(newContent); + } if (newMainXDoc.Descendants().Any(d => { if (d.Name.Namespace == PtOpenXml.pt || d.Name.Namespace == PtOpenXml.ptOpenXml) + { return true; + } + if (d.Attributes().Any(att => att.Name.Namespace == PtOpenXml.pt || att.Name.Namespace == PtOpenXml.ptOpenXml)) + { return true; + } + return false; })) { @@ -2201,13 +2269,12 @@ private static void AddToIgnorable(XElement root, string v) } } - /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// New method to support new functionality private static void AppendDocument(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, OpenXmlPart part, - List newContent, bool keepSection, string insertId, List images) + List newContent, string insertId, List images) { // Append contents - XDocument partXDoc = part.GetXDocument(); + var partXDoc = part.GetXDocument(); partXDoc.Declaration.Standalone = Yes; partXDoc.Declaration.Encoding = Utf8; @@ -2217,69 +2284,78 @@ private static void AppendDocument(WordprocessingDocument sourceDocument, Wordpr newContent, images); // never keep sections for content to be inserted into a header/footer - List adjustedContents = newContent.Where(e => e.Name != W.sectPr).ToList(); + var adjustedContents = newContent.Where(e => e.Name != W.sectPr).ToList(); adjustedContents.DescendantsAndSelf(W.sectPr).Remove(); newContent = adjustedContents; CopyNumbering(sourceDocument, newDocument, newContent, images); CopyComments(sourceDocument, newDocument, newContent, images); - AdjustUniqueIds(sourceDocument, newDocument, newContent); + AdjustUniqueIds(newDocument, newContent); RemoveGfxdata(newContent); if (insertId == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } - XElement insertElementToReplace = partXDoc + var insertElementToReplace = partXDoc .Descendants(PtOpenXml.Insert) .FirstOrDefault(i => (string)i.Attribute(PtOpenXml.Id) == insertId); if (insertElementToReplace != null) + { insertElementToReplace.AddAnnotation(new ReplaceSemaphore()); + } + partXDoc.Elements().First().ReplaceWith((XElement)InsertTransform(partXDoc.Root, newContent)); } - /// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - public static WmlDocument ExtractGlossaryDocument(WmlDocument wmlGlossaryDocument) + public static WmlDocument? ExtractGlossaryDocument(WmlDocument wmlGlossaryDocument) { if (RelationshipMarkup == null) + { InitRelationshipMarkup(); + } - using (MemoryStream ms = new MemoryStream()) + using var ms = new MemoryStream(); + ms.Write(wmlGlossaryDocument.DocumentByteArray, 0, wmlGlossaryDocument.DocumentByteArray.Length); + using var wDoc = WordprocessingDocument.Open(ms, false); + if (wDoc.MainDocumentPart.GlossaryDocumentPart == null) { - ms.Write(wmlGlossaryDocument.DocumentByteArray, 0, wmlGlossaryDocument.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false)) - { - if (wDoc.MainDocumentPart.GlossaryDocumentPart == null) - return null; - - var fromXd = wDoc.MainDocumentPart.GlossaryDocumentPart.GetXDocument(); - if (fromXd.Root == null) - return null; + return null; + } - using (MemoryStream outMs = new MemoryStream()) - { - using (WordprocessingDocument outWDoc = WordprocessingDocument.Create(outMs, DocumentFormat.OpenXml.WordprocessingDocumentType.Document)) - { - List images = new List(); + var fromXd = wDoc.MainDocumentPart.GlossaryDocumentPart.GetXDocument(); + if (fromXd.Root == null) + { + return null; + } - MainDocumentPart mdp = outWDoc.AddMainDocumentPart(); - var mdpXd = mdp.GetXDocument(); - XElement root = new XElement(W.document); - if (mdpXd.Root == null) - mdpXd.Add(root); - else - mdpXd.Root.ReplaceWith(root); - root.Add(new XElement(W.body, - fromXd.Root.Elements(W.docParts))); - mdp.PutXDocument(); + using var outMs = new MemoryStream(); + using (var outWDoc = WordprocessingDocument.Create(outMs, DocumentFormat.OpenXml.WordprocessingDocumentType.Document)) + { + var images = new List(); - var newContent = fromXd.Root.Elements(W.docParts); - CopyGlossaryDocumentPartsFromGD(wDoc, outWDoc, newContent, images); - CopyRelatedPartsForContentParts(wDoc.MainDocumentPart.GlossaryDocumentPart, mdp, newContent, images); - } - return new WmlDocument("Glossary.docx", outMs.ToArray()); - } + var mdp = outWDoc.AddMainDocumentPart(); + var mdpXd = mdp.GetXDocument(); + var root = new XElement(W.document); + if (mdpXd.Root == null) + { + mdpXd.Add(root); + } + else + { + mdpXd.Root.ReplaceWith(root); } + + root.Add(new XElement(W.body, + fromXd.Root.Elements(W.docParts))); + mdp.PutXDocument(); + + var newContent = fromXd.Root.Elements(W.docParts); + CopyGlossaryDocumentPartsFromGD(wDoc, outWDoc, newContent, images); + CopyRelatedPartsForContentParts(wDoc.MainDocumentPart.GlossaryDocumentPart, mdp, newContent, images); } + return new WmlDocument("Glossary.docx", outMs.ToArray()); } private static void CopyGlossaryDocumentPartsFromGD(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, @@ -2288,11 +2364,11 @@ private static void CopyGlossaryDocumentPartsFromGD(WordprocessingDocument sourc // Copy all styles to the new document if (sourceDocument.MainDocumentPart.GlossaryDocumentPart.StyleDefinitionsPart != null) { - XDocument oldStyles = sourceDocument.MainDocumentPart.GlossaryDocumentPart.StyleDefinitionsPart.GetXDocument(); + var oldStyles = sourceDocument.MainDocumentPart.GlossaryDocumentPart.StyleDefinitionsPart.GetXDocument(); if (newDocument.MainDocumentPart.StyleDefinitionsPart == null) { newDocument.MainDocumentPart.AddNewPart(); - XDocument newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); newStyles.Declaration.Standalone = Yes; newStyles.Declaration.Encoding = Utf8; newStyles.Add(oldStyles.Root); @@ -2300,7 +2376,7 @@ private static void CopyGlossaryDocumentPartsFromGD(WordprocessingDocument sourc } else { - XDocument newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var newStyles = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); MergeStyles(sourceDocument, newDocument, oldStyles, newStyles, newContent); newDocument.MainDocumentPart.StyleDefinitionsPart.PutXDocument(); } @@ -2309,11 +2385,11 @@ private static void CopyGlossaryDocumentPartsFromGD(WordprocessingDocument sourc // Copy fontTable to the new document if (sourceDocument.MainDocumentPart.GlossaryDocumentPart.FontTablePart != null) { - XDocument oldFontTable = sourceDocument.MainDocumentPart.GlossaryDocumentPart.FontTablePart.GetXDocument(); + var oldFontTable = sourceDocument.MainDocumentPart.GlossaryDocumentPart.FontTablePart.GetXDocument(); if (newDocument.MainDocumentPart.FontTablePart == null) { newDocument.MainDocumentPart.AddNewPart(); - XDocument newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); + var newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); newFontTable.Declaration.Standalone = Yes; newFontTable.Declaration.Encoding = Utf8; newFontTable.Add(oldFontTable.Root); @@ -2321,21 +2397,19 @@ private static void CopyGlossaryDocumentPartsFromGD(WordprocessingDocument sourc } else { - XDocument newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); + var newFontTable = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); MergeFontTables(oldFontTable, newFontTable); newDocument.MainDocumentPart.FontTablePart.PutXDocument(); } } - DocumentSettingsPart oldSettingsPart = sourceDocument.MainDocumentPart.GlossaryDocumentPart.DocumentSettingsPart; + var oldSettingsPart = sourceDocument.MainDocumentPart.GlossaryDocumentPart.DocumentSettingsPart; if (oldSettingsPart != null) { - DocumentSettingsPart newSettingsPart = newDocument.MainDocumentPart.AddNewPart(); - XDocument settingsXDoc = oldSettingsPart.GetXDocument(); + var newSettingsPart = newDocument.MainDocumentPart.AddNewPart(); + var settingsXDoc = oldSettingsPart.GetXDocument(); AddRelationships(oldSettingsPart, newSettingsPart, new[] { settingsXDoc.Root }); - //CopyFootnotesPart(sourceDocument, newDocument, settingsXDoc, images); - //CopyEndnotesPart(sourceDocument, newDocument, settingsXDoc, images); - XDocument newXDoc = newDocument.MainDocumentPart.DocumentSettingsPart.GetXDocument(); + var newXDoc = newDocument.MainDocumentPart.DocumentSettingsPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(settingsXDoc.Root); @@ -2343,20 +2417,20 @@ private static void CopyGlossaryDocumentPartsFromGD(WordprocessingDocument sourc newSettingsPart.PutXDocument(newXDoc); } - WebSettingsPart oldWebSettingsPart = sourceDocument.MainDocumentPart.GlossaryDocumentPart.WebSettingsPart; + var oldWebSettingsPart = sourceDocument.MainDocumentPart.GlossaryDocumentPart.WebSettingsPart; if (oldWebSettingsPart != null) { - WebSettingsPart newWebSettingsPart = newDocument.MainDocumentPart.AddNewPart(); - XDocument settingsXDoc = oldWebSettingsPart.GetXDocument(); + var newWebSettingsPart = newDocument.MainDocumentPart.AddNewPart(); + var settingsXDoc = oldWebSettingsPart.GetXDocument(); AddRelationships(oldWebSettingsPart, newWebSettingsPart, new[] { settingsXDoc.Root }); - XDocument newXDoc = newDocument.MainDocumentPart.WebSettingsPart.GetXDocument(); + var newXDoc = newDocument.MainDocumentPart.WebSettingsPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(settingsXDoc.Root); newWebSettingsPart.PutXDocument(newXDoc); } - NumberingDefinitionsPart oldNumberingDefinitionsPart = sourceDocument.MainDocumentPart.GlossaryDocumentPart.NumberingDefinitionsPart; + var oldNumberingDefinitionsPart = sourceDocument.MainDocumentPart.GlossaryDocumentPart.NumberingDefinitionsPart; if (oldNumberingDefinitionsPart != null) { CopyNumberingForGlossaryDocumentPartFromGD(oldNumberingDefinitionsPart, newDocument, newContent, images); @@ -2369,9 +2443,9 @@ private static void CopyGlossaryDocumentPartsToGD(WordprocessingDocument sourceD // Copy all styles to the new document if (sourceDocument.MainDocumentPart.StyleDefinitionsPart != null) { - XDocument oldStyles = sourceDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var oldStyles = sourceDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); newDocument.MainDocumentPart.GlossaryDocumentPart.AddNewPart(); - XDocument newStyles = newDocument.MainDocumentPart.GlossaryDocumentPart.StyleDefinitionsPart.GetXDocument(); + var newStyles = newDocument.MainDocumentPart.GlossaryDocumentPart.StyleDefinitionsPart.GetXDocument(); newStyles.Declaration.Standalone = Yes; newStyles.Declaration.Encoding = Utf8; newStyles.Add(oldStyles.Root); @@ -2381,24 +2455,22 @@ private static void CopyGlossaryDocumentPartsToGD(WordprocessingDocument sourceD // Copy fontTable to the new document if (sourceDocument.MainDocumentPart.FontTablePart != null) { - XDocument oldFontTable = sourceDocument.MainDocumentPart.FontTablePart.GetXDocument(); + var oldFontTable = sourceDocument.MainDocumentPart.FontTablePart.GetXDocument(); newDocument.MainDocumentPart.GlossaryDocumentPart.AddNewPart(); - XDocument newFontTable = newDocument.MainDocumentPart.GlossaryDocumentPart.FontTablePart.GetXDocument(); + var newFontTable = newDocument.MainDocumentPart.GlossaryDocumentPart.FontTablePart.GetXDocument(); newFontTable.Declaration.Standalone = Yes; newFontTable.Declaration.Encoding = Utf8; newFontTable.Add(oldFontTable.Root); newDocument.MainDocumentPart.FontTablePart.PutXDocument(); } - DocumentSettingsPart oldSettingsPart = sourceDocument.MainDocumentPart.DocumentSettingsPart; + var oldSettingsPart = sourceDocument.MainDocumentPart.DocumentSettingsPart; if (oldSettingsPart != null) { - DocumentSettingsPart newSettingsPart = newDocument.MainDocumentPart.GlossaryDocumentPart.AddNewPart(); - XDocument settingsXDoc = oldSettingsPart.GetXDocument(); + var newSettingsPart = newDocument.MainDocumentPart.GlossaryDocumentPart.AddNewPart(); + var settingsXDoc = oldSettingsPart.GetXDocument(); AddRelationships(oldSettingsPart, newSettingsPart, new[] { settingsXDoc.Root }); - //CopyFootnotesPart(sourceDocument, newDocument, settingsXDoc, images); - //CopyEndnotesPart(sourceDocument, newDocument, settingsXDoc, images); - XDocument newXDoc = newDocument.MainDocumentPart.GlossaryDocumentPart.DocumentSettingsPart.GetXDocument(); + var newXDoc = newDocument.MainDocumentPart.GlossaryDocumentPart.DocumentSettingsPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(settingsXDoc.Root); @@ -2406,27 +2478,26 @@ private static void CopyGlossaryDocumentPartsToGD(WordprocessingDocument sourceD newSettingsPart.PutXDocument(newXDoc); } - WebSettingsPart oldWebSettingsPart = sourceDocument.MainDocumentPart.WebSettingsPart; + var oldWebSettingsPart = sourceDocument.MainDocumentPart.WebSettingsPart; if (oldWebSettingsPart != null) { - WebSettingsPart newWebSettingsPart = newDocument.MainDocumentPart.GlossaryDocumentPart.AddNewPart(); - XDocument settingsXDoc = oldWebSettingsPart.GetXDocument(); + var newWebSettingsPart = newDocument.MainDocumentPart.GlossaryDocumentPart.AddNewPart(); + var settingsXDoc = oldWebSettingsPart.GetXDocument(); AddRelationships(oldWebSettingsPart, newWebSettingsPart, new[] { settingsXDoc.Root }); - XDocument newXDoc = newDocument.MainDocumentPart.GlossaryDocumentPart.WebSettingsPart.GetXDocument(); + var newXDoc = newDocument.MainDocumentPart.GlossaryDocumentPart.WebSettingsPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(settingsXDoc.Root); newWebSettingsPart.PutXDocument(newXDoc); } - NumberingDefinitionsPart oldNumberingDefinitionsPart = sourceDocument.MainDocumentPart.NumberingDefinitionsPart; + var oldNumberingDefinitionsPart = sourceDocument.MainDocumentPart.NumberingDefinitionsPart; if (oldNumberingDefinitionsPart != null) { CopyNumberingForGlossaryDocumentPartToGD(oldNumberingDefinitionsPart, newDocument, newContent, images); } } - #if false At various locations in Open-Xml-PowerTools, you will find examples of Open XML markup that is associated with code associated with querying or generating that markup. This is an example of the GlossaryDocument part. @@ -2475,29 +2546,33 @@ querying or generating that markup. This is an example of the GlossaryDocument private static void CopyCustomXmlPartsForDataBoundContentControls(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, IEnumerable newContent) { - List itemList = new List(); - foreach (string itemId in newContent + var itemList = new List(); + foreach (var itemId in newContent .Descendants(W.dataBinding) .Select(e => (string)e.Attribute(W.storeItemID))) + { if (!itemList.Contains(itemId)) + { itemList.Add(itemId); - foreach (CustomXmlPart customXmlPart in sourceDocument.MainDocumentPart.CustomXmlParts) + } + } + + foreach (var customXmlPart in sourceDocument.MainDocumentPart.CustomXmlParts) { - OpenXmlPart propertyPart = customXmlPart + var propertyPart = customXmlPart .Parts .Select(p => p.OpenXmlPart) - .Where(p => p.ContentType == "application/vnd.openxmlformats-officedocument.customXmlProperties+xml") - .FirstOrDefault(); +.FirstOrDefault(p => p.ContentType == "application/vnd.openxmlformats-officedocument.customXmlProperties+xml"); if (propertyPart != null) { - XDocument propertyPartDoc = propertyPart.GetXDocument(); + var propertyPartDoc = propertyPart.GetXDocument(); if (itemList.Contains(propertyPartDoc.Root.Attribute(DS.itemID).Value)) { - CustomXmlPart newPart = newDocument.MainDocumentPart.AddCustomXmlPart(customXmlPart.ContentType); + var newPart = newDocument.MainDocumentPart.AddCustomXmlPart(customXmlPart.ContentType); newPart.GetXDocument().Add(customXmlPart.GetXDocument().Root); - foreach (OpenXmlPart propPart in customXmlPart.Parts.Select(p => p.OpenXmlPart)) + foreach (var propPart in customXmlPart.Parts.Select(p => p.OpenXmlPart)) { - CustomXmlPropertiesPart newPropPart = newPart.AddNewPart(); + var newPropPart = newPart.AddNewPart(); newPropPart.GetXDocument().Add(propPart.GetXDocument().Root); } } @@ -2505,7 +2580,7 @@ private static void CopyCustomXmlPartsForDataBoundContentControls(Wordprocessing } } - private static Dictionary RelationshipMarkup = null; + private static Dictionary RelationshipMarkup; private static void UpdateContent(IEnumerable newContent, XName elementToModify, string oldRid, string newRid) { @@ -2515,7 +2590,9 @@ private static void UpdateContent(IEnumerable newContent, XName elemen .Descendants(elementToModify) .Where(e => (string)e.Attribute(attributeName) == oldRid); foreach (var element in elementsToUpdate) + { element.Attribute(attributeName).Value = newRid; + } } } @@ -2528,109 +2605,153 @@ private static void AddRelationships(OpenXmlPart oldPart, OpenXmlPart newPart, I { if (e.Name == W.hyperlink) { - string relId = (string)e.Attribute(R.id); + var relId = (string)e.Attribute(R.id); if (string.IsNullOrEmpty(relId)) + { continue; + } + var tempHyperlink = newPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId); if (tempHyperlink != null) + { continue; - Guid g = Guid.NewGuid(); - string newRid = $"R{g:N}"; + } + + var g = Guid.NewGuid(); + var newRid = $"R{g:N}"; var oldHyperlink = oldPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId); if (oldHyperlink == null) + { continue; - //throw new DocumentBuilderInternalException("Internal Error 0002"); + } newPart.AddHyperlinkRelationship(oldHyperlink.Uri, oldHyperlink.IsExternal, newRid); UpdateContent(newContent, e.Name, relId, newRid); } if (e.Name == W.attachedTemplate || e.Name == W.saveThroughXslt) { - string relId = (string)e.Attribute(R.id); + var relId = (string)e.Attribute(R.id); if (string.IsNullOrEmpty(relId)) + { continue; + } + var tempExternalRelationship = newPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId); if (tempExternalRelationship != null) + { continue; - Guid g = Guid.NewGuid(); - string newRid = $"R{g:N}"; + } + + var g = Guid.NewGuid(); + var newRid = $"R{g:N}"; var oldRel = oldPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId); if (oldRel == null) + { throw new DocumentBuilderInternalException("Source {0} is invalid document - hyperlink contains invalid references"); + } + newPart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid); UpdateContent(newContent, e.Name, relId, newRid); } if (e.Name == A.hlinkClick || e.Name == A.hlinkHover || e.Name == A.hlinkMouseOver) { - string relId = (string)e.Attribute(R.id); + var relId = (string)e.Attribute(R.id); if (string.IsNullOrEmpty(relId)) + { continue; + } + var tempHyperlink = newPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId); if (tempHyperlink != null) + { continue; - Guid g = Guid.NewGuid(); - string newRid = $"R{g:N}"; + } + + var g = Guid.NewGuid(); + var newRid = $"R{g:N}"; var oldHyperlink = oldPart.HyperlinkRelationships.FirstOrDefault(h => h.Id == relId); if (oldHyperlink == null) + { continue; + } + newPart.AddHyperlinkRelationship(oldHyperlink.Uri, oldHyperlink.IsExternal, newRid); UpdateContent(newContent, e.Name, relId, newRid); } if (e.Name == VML.imagedata) { - string relId = (string)e.Attribute(R.href); + var relId = (string)e.Attribute(R.href); if (string.IsNullOrEmpty(relId)) + { continue; + } + var tempExternalRelationship = newPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId); if (tempExternalRelationship != null) + { continue; - Guid g = Guid.NewGuid(); - string newRid = $"R{g:N}"; + } + + var g = Guid.NewGuid(); + var newRid = $"R{g:N}"; var oldRel = oldPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId); if (oldRel == null) + { throw new DocumentBuilderInternalException("Internal Error 0006"); + } + newPart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid); UpdateContent(newContent, e.Name, relId, newRid); } if (e.Name == A.blip) { - // - string relId = (string)e.Attribute(R.link); - //if (relId == null) - // relId = (string)e.Attribute(R.embed); + var relId = (string)e.Attribute(R.link); if (string.IsNullOrEmpty(relId)) + { continue; + } + var tempExternalRelationship = newPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId); if (tempExternalRelationship != null) + { continue; - Guid g = Guid.NewGuid(); - string newRid = $"R{g:N}"; + } + + var g = Guid.NewGuid(); + var newRid = $"R{g:N}"; var oldRel = oldPart.ExternalRelationships.FirstOrDefault(h => h.Id == relId); if (oldRel == null) + { continue; + } + newPart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid); UpdateContent(newContent, e.Name, relId, newRid); } } } - private class FromPreviousSourceSemaphore { }; + private class FromPreviousSourceSemaphore + { }; private static void CopyNumbering(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, IEnumerable newContent, List images) { - Dictionary numIdMap = new Dictionary(); - int number = 1; - int abstractNumber = 0; - XDocument oldNumbering = null; - XDocument newNumbering = null; + var numIdMap = new Dictionary(); + var number = 1; + var abstractNumber = 0; + XDocument? oldNumbering = null; + XDocument? newNumbering = null; - foreach (XElement numReference in newContent.DescendantsAndSelf(W.numPr)) + foreach (var numReference in newContent.DescendantsAndSelf(W.numPr)) { - XElement idElement = numReference.Descendants(W.numId).FirstOrDefault(); + var idElement = numReference.Descendants(W.numId).FirstOrDefault(); if (idElement != null) { if (oldNumbering == null) + { oldNumbering = sourceDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument(); + } + if (newNumbering == null) { if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null) @@ -2641,13 +2762,18 @@ private static void CopyNumbering(WordprocessingDocument sourceDocument, Wordpro .Elements(W.num) .Select(f => (int)f.Attribute(W.numId)); if (numIds.Any()) + { number = numIds.Max() + 1; + } + numIds = newNumbering .Root .Elements(W.abstractNum) .Select(f => (int)f.Attribute(W.abstractNumId)); if (numIds.Any()) + { abstractNumber = numIds.Max() + 1; + } } else { @@ -2658,76 +2784,86 @@ private static void CopyNumbering(WordprocessingDocument sourceDocument, Wordpro newNumbering.Add(new XElement(W.numbering, NamespaceAttributes)); } } - int numId = (int)idElement.Attribute(W.val); + var numId = (int)idElement.Attribute(W.val); if (numId != 0) { - XElement element = oldNumbering + var element = oldNumbering .Descendants(W.num) - .Where(p => ((int)p.Attribute(W.numId)) == numId) - .FirstOrDefault(); +.FirstOrDefault(p => (int)p.Attribute(W.numId) == numId); if (element == null) + { continue; + } // Copy abstract numbering element, if necessary (use matching NSID) - string abstractNumIdStr = (string)element + var abstractNumIdStr = (string)element .Elements(W.abstractNumId) .First() .Attribute(W.val); - int abstractNumId; - if (!int.TryParse(abstractNumIdStr, out abstractNumId)) + if (!int.TryParse(abstractNumIdStr, out var abstractNumId)) + { throw new DocumentBuilderException("Invalid document - invalid value for abstractNumId"); + } - XElement abstractElement = oldNumbering + var abstractElement = oldNumbering .Descendants() .Elements(W.abstractNum) - .Where(p => ((int)p.Attribute(W.abstractNumId)) == abstractNumId) - .First(); - XElement nsidElement = abstractElement +.First(p => (int)p.Attribute(W.abstractNumId) == abstractNumId); + var nsidElement = abstractElement .Element(W.nsid); - string abstractNSID = null; + string? abstractNSID = null; if (nsidElement != null) + { abstractNSID = (string)nsidElement .Attribute(W.val); - XElement newAbstractElement = newNumbering + } + + var newAbstractElement = newNumbering .Descendants() .Elements(W.abstractNum) .Where(e => e.Annotation() == null) - .Where(p => +.FirstOrDefault(p => { var thisNsidElement = p.Element(W.nsid); if (thisNsidElement == null) + { return false; + } + return (string)thisNsidElement.Attribute(W.val) == abstractNSID; - }) - .FirstOrDefault(); + }); if (newAbstractElement == null) { newAbstractElement = new XElement(abstractElement); newAbstractElement.Attribute(W.abstractNumId).Value = abstractNumber.ToString(); abstractNumber++; if (newNumbering.Root.Elements(W.abstractNum).Any()) + { newNumbering.Root.Elements(W.abstractNum).Last().AddAfterSelf(newAbstractElement); + } else + { newNumbering.Root.Add(newAbstractElement); + } - foreach (XElement pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) + foreach (var pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) { - string bulletId = (string)pictId.Attribute(W.val); - XElement numPicBullet = oldNumbering + var bulletId = (string)pictId.Attribute(W.val); + var numPicBullet = oldNumbering .Descendants(W.numPicBullet) .FirstOrDefault(d => (string)d.Attribute(W.numPicBulletId) == bulletId); - int maxNumPicBulletId = new int[] { -1 }.Concat( + var maxNumPicBulletId = new int[] { -1 }.Concat( newNumbering.Descendants(W.numPicBullet) .Attributes(W.numPicBulletId) .Select(a => (int)a)) .Max() + 1; - XElement newNumPicBullet = new XElement(numPicBullet); + var newNumPicBullet = new XElement(numPicBullet); newNumPicBullet.Attribute(W.numPicBulletId).Value = maxNumPicBulletId.ToString(); pictId.Attribute(W.val).Value = maxNumPicBulletId.ToString(); newNumbering.Root.AddFirst(newNumPicBullet); } } - string newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; + var newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; // Copy numbering element, if necessary (use matching element with no overrides) XElement newElement; @@ -2737,8 +2873,7 @@ private static void CopyNumbering(WordprocessingDocument sourceDocument, Wordpro .Descendants() .Elements(W.num) .Where(e => e.Annotation() == null) - .Where(p => ((int)p.Attribute(W.numId)) == numIdMap[numId]) - .First(); +.First(p => (int)p.Attribute(W.numId) == numIdMap[numId]); } else { @@ -2759,9 +2894,14 @@ private static void CopyNumbering(WordprocessingDocument sourceDocument, Wordpro if (newNumbering != null) { foreach (var abstractNum in newNumbering.Descendants(W.abstractNum)) + { abstractNum.AddAnnotation(new FromPreviousSourceSemaphore()); + } + foreach (var num in newNumbering.Descendants(W.num)) + { num.AddAnnotation(new FromPreviousSourceSemaphore()); + } } if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null && @@ -2780,19 +2920,22 @@ private static void CopyNumbering(WordprocessingDocument sourceDocument, Wordpro private static void CopyNumberingForGlossaryDocumentPartFromGD(NumberingDefinitionsPart sourceNumberingPart, WordprocessingDocument newDocument, IEnumerable newContent, List images) { - Dictionary numIdMap = new Dictionary(); - int number = 1; - int abstractNumber = 0; - XDocument oldNumbering = null; - XDocument newNumbering = null; + var numIdMap = new Dictionary(); + var number = 1; + var abstractNumber = 0; + XDocument? oldNumbering = null; + XDocument? newNumbering = null; - foreach (XElement numReference in newContent.DescendantsAndSelf(W.numPr)) + foreach (var numReference in newContent.DescendantsAndSelf(W.numPr)) { - XElement idElement = numReference.Descendants(W.numId).FirstOrDefault(); + var idElement = numReference.Descendants(W.numId).FirstOrDefault(); if (idElement != null) { if (oldNumbering == null) + { oldNumbering = sourceNumberingPart.GetXDocument(); + } + if (newNumbering == null) { if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null) @@ -2803,13 +2946,18 @@ private static void CopyNumberingForGlossaryDocumentPartFromGD(NumberingDefiniti .Elements(W.num) .Select(f => (int)f.Attribute(W.numId)); if (numIds.Any()) + { number = numIds.Max() + 1; + } + numIds = newNumbering .Root .Elements(W.abstractNum) .Select(f => (int)f.Attribute(W.abstractNumId)); if (numIds.Any()) + { abstractNumber = numIds.Max() + 1; + } } else { @@ -2820,75 +2968,86 @@ private static void CopyNumberingForGlossaryDocumentPartFromGD(NumberingDefiniti newNumbering.Add(new XElement(W.numbering, NamespaceAttributes)); } } - int numId = (int)idElement.Attribute(W.val); + var numId = (int)idElement.Attribute(W.val); if (numId != 0) { - XElement element = oldNumbering + var element = oldNumbering .Descendants(W.num) - .Where(p => ((int)p.Attribute(W.numId)) == numId) - .FirstOrDefault(); +.FirstOrDefault(p => (int)p.Attribute(W.numId) == numId); if (element == null) + { continue; + } // Copy abstract numbering element, if necessary (use matching NSID) - string abstractNumIdStr = (string)element + var abstractNumIdStr = (string)element .Elements(W.abstractNumId) .First() .Attribute(W.val); - int abstractNumId; - if (!int.TryParse(abstractNumIdStr, out abstractNumId)) + if (!int.TryParse(abstractNumIdStr, out var abstractNumId)) + { throw new DocumentBuilderException("Invalid document - invalid value for abstractNumId"); - XElement abstractElement = oldNumbering + } + + var abstractElement = oldNumbering .Descendants() .Elements(W.abstractNum) - .Where(p => ((int)p.Attribute(W.abstractNumId)) == abstractNumId) - .First(); - XElement nsidElement = abstractElement +.First(p => (int)p.Attribute(W.abstractNumId) == abstractNumId); + var nsidElement = abstractElement .Element(W.nsid); - string abstractNSID = null; + string? abstractNSID = null; if (nsidElement != null) + { abstractNSID = (string)nsidElement .Attribute(W.val); - XElement newAbstractElement = newNumbering + } + + var newAbstractElement = newNumbering .Descendants() .Elements(W.abstractNum) .Where(e => e.Annotation() == null) - .Where(p => +.FirstOrDefault(p => { var thisNsidElement = p.Element(W.nsid); if (thisNsidElement == null) + { return false; + } + return (string)thisNsidElement.Attribute(W.val) == abstractNSID; - }) - .FirstOrDefault(); + }); if (newAbstractElement == null) { newAbstractElement = new XElement(abstractElement); newAbstractElement.Attribute(W.abstractNumId).Value = abstractNumber.ToString(); abstractNumber++; if (newNumbering.Root.Elements(W.abstractNum).Any()) + { newNumbering.Root.Elements(W.abstractNum).Last().AddAfterSelf(newAbstractElement); + } else + { newNumbering.Root.Add(newAbstractElement); + } - foreach (XElement pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) + foreach (var pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) { - string bulletId = (string)pictId.Attribute(W.val); - XElement numPicBullet = oldNumbering + var bulletId = (string)pictId.Attribute(W.val); + var numPicBullet = oldNumbering .Descendants(W.numPicBullet) .FirstOrDefault(d => (string)d.Attribute(W.numPicBulletId) == bulletId); - int maxNumPicBulletId = new int[] { -1 }.Concat( + var maxNumPicBulletId = new int[] { -1 }.Concat( newNumbering.Descendants(W.numPicBullet) .Attributes(W.numPicBulletId) .Select(a => (int)a)) .Max() + 1; - XElement newNumPicBullet = new XElement(numPicBullet); + var newNumPicBullet = new XElement(numPicBullet); newNumPicBullet.Attribute(W.numPicBulletId).Value = maxNumPicBulletId.ToString(); pictId.Attribute(W.val).Value = maxNumPicBulletId.ToString(); newNumbering.Root.AddFirst(newNumPicBullet); } } - string newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; + var newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; // Copy numbering element, if necessary (use matching element with no overrides) XElement newElement; @@ -2898,8 +3057,7 @@ private static void CopyNumberingForGlossaryDocumentPartFromGD(NumberingDefiniti .Descendants() .Elements(W.num) .Where(e => e.Annotation() == null) - .Where(p => ((int)p.Attribute(W.numId)) == numIdMap[numId]) - .First(); +.First(p => (int)p.Attribute(W.numId) == numIdMap[numId]); } else { @@ -2920,9 +3078,14 @@ private static void CopyNumberingForGlossaryDocumentPartFromGD(NumberingDefiniti if (newNumbering != null) { foreach (var abstractNum in newNumbering.Descendants(W.abstractNum)) + { abstractNum.AddAnnotation(new FromPreviousSourceSemaphore()); + } + foreach (var num in newNumbering.Descendants(W.num)) + { num.AddAnnotation(new FromPreviousSourceSemaphore()); + } } if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null && @@ -2936,25 +3099,30 @@ private static void CopyNumberingForGlossaryDocumentPartFromGD(NumberingDefiniti new[] { newDocument.MainDocumentPart.NumberingDefinitionsPart.GetXDocument().Root }, images); } if (newDocument.MainDocumentPart.NumberingDefinitionsPart != null) + { newDocument.MainDocumentPart.NumberingDefinitionsPart.PutXDocument(); + } } private static void CopyNumberingForGlossaryDocumentPartToGD(NumberingDefinitionsPart sourceNumberingPart, WordprocessingDocument newDocument, IEnumerable newContent, List images) { - Dictionary numIdMap = new Dictionary(); - int number = 1; - int abstractNumber = 0; - XDocument oldNumbering = null; - XDocument newNumbering = null; + var numIdMap = new Dictionary(); + var number = 1; + var abstractNumber = 0; + XDocument? oldNumbering = null; + XDocument? newNumbering = null; - foreach (XElement numReference in newContent.DescendantsAndSelf(W.numPr)) + foreach (var numReference in newContent.DescendantsAndSelf(W.numPr)) { - XElement idElement = numReference.Descendants(W.numId).FirstOrDefault(); + var idElement = numReference.Descendants(W.numId).FirstOrDefault(); if (idElement != null) { if (oldNumbering == null) + { oldNumbering = sourceNumberingPart.GetXDocument(); + } + if (newNumbering == null) { if (newDocument.MainDocumentPart.GlossaryDocumentPart.NumberingDefinitionsPart != null) @@ -2965,13 +3133,18 @@ private static void CopyNumberingForGlossaryDocumentPartToGD(NumberingDefinition .Elements(W.num) .Select(f => (int)f.Attribute(W.numId)); if (numIds.Any()) + { number = numIds.Max() + 1; + } + numIds = newNumbering .Root .Elements(W.abstractNum) .Select(f => (int)f.Attribute(W.abstractNumId)); if (numIds.Any()) + { abstractNumber = numIds.Max() + 1; + } } else { @@ -2982,75 +3155,86 @@ private static void CopyNumberingForGlossaryDocumentPartToGD(NumberingDefinition newNumbering.Add(new XElement(W.numbering, NamespaceAttributes)); } } - int numId = (int)idElement.Attribute(W.val); + var numId = (int)idElement.Attribute(W.val); if (numId != 0) { - XElement element = oldNumbering + var element = oldNumbering .Descendants(W.num) - .Where(p => ((int)p.Attribute(W.numId)) == numId) - .FirstOrDefault(); +.FirstOrDefault(p => (int)p.Attribute(W.numId) == numId); if (element == null) + { continue; + } // Copy abstract numbering element, if necessary (use matching NSID) - string abstractNumIdStr = (string)element + var abstractNumIdStr = (string)element .Elements(W.abstractNumId) .First() .Attribute(W.val); - int abstractNumId; - if (!int.TryParse(abstractNumIdStr, out abstractNumId)) + if (!int.TryParse(abstractNumIdStr, out var abstractNumId)) + { throw new DocumentBuilderException("Invalid document - invalid value for abstractNumId"); - XElement abstractElement = oldNumbering + } + + var abstractElement = oldNumbering .Descendants() .Elements(W.abstractNum) - .Where(p => ((int)p.Attribute(W.abstractNumId)) == abstractNumId) - .First(); - XElement nsidElement = abstractElement +.First(p => (int)p.Attribute(W.abstractNumId) == abstractNumId); + var nsidElement = abstractElement .Element(W.nsid); - string abstractNSID = null; + string? abstractNSID = null; if (nsidElement != null) + { abstractNSID = (string)nsidElement .Attribute(W.val); - XElement newAbstractElement = newNumbering + } + + var newAbstractElement = newNumbering .Descendants() .Elements(W.abstractNum) .Where(e => e.Annotation() == null) - .Where(p => +.FirstOrDefault(p => { var thisNsidElement = p.Element(W.nsid); if (thisNsidElement == null) + { return false; + } + return (string)thisNsidElement.Attribute(W.val) == abstractNSID; - }) - .FirstOrDefault(); + }); if (newAbstractElement == null) { newAbstractElement = new XElement(abstractElement); newAbstractElement.Attribute(W.abstractNumId).Value = abstractNumber.ToString(); abstractNumber++; if (newNumbering.Root.Elements(W.abstractNum).Any()) + { newNumbering.Root.Elements(W.abstractNum).Last().AddAfterSelf(newAbstractElement); + } else + { newNumbering.Root.Add(newAbstractElement); + } - foreach (XElement pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) + foreach (var pictId in newAbstractElement.Descendants(W.lvlPicBulletId)) { - string bulletId = (string)pictId.Attribute(W.val); - XElement numPicBullet = oldNumbering + var bulletId = (string)pictId.Attribute(W.val); + var numPicBullet = oldNumbering .Descendants(W.numPicBullet) .FirstOrDefault(d => (string)d.Attribute(W.numPicBulletId) == bulletId); - int maxNumPicBulletId = new int[] { -1 }.Concat( + var maxNumPicBulletId = new int[] { -1 }.Concat( newNumbering.Descendants(W.numPicBullet) .Attributes(W.numPicBulletId) .Select(a => (int)a)) .Max() + 1; - XElement newNumPicBullet = new XElement(numPicBullet); + var newNumPicBullet = new XElement(numPicBullet); newNumPicBullet.Attribute(W.numPicBulletId).Value = maxNumPicBulletId.ToString(); pictId.Attribute(W.val).Value = maxNumPicBulletId.ToString(); newNumbering.Root.AddFirst(newNumPicBullet); } } - string newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; + var newAbstractId = newAbstractElement.Attribute(W.abstractNumId).Value; // Copy numbering element, if necessary (use matching element with no overrides) XElement newElement; @@ -3060,8 +3244,7 @@ private static void CopyNumberingForGlossaryDocumentPartToGD(NumberingDefinition .Descendants() .Elements(W.num) .Where(e => e.Annotation() == null) - .Where(p => ((int)p.Attribute(W.numId)) == numIdMap[numId]) - .First(); +.First(p => (int)p.Attribute(W.numId) == numIdMap[numId]); } else { @@ -3082,9 +3265,14 @@ private static void CopyNumberingForGlossaryDocumentPartToGD(NumberingDefinition if (newNumbering != null) { foreach (var abstractNum in newNumbering.Descendants(W.abstractNum)) + { abstractNum.AddAnnotation(new FromPreviousSourceSemaphore()); + } + foreach (var num in newNumbering.Descendants(W.num)) + { num.AddAnnotation(new FromPreviousSourceSemaphore()); + } } if (newDocument.MainDocumentPart.GlossaryDocumentPart.NumberingDefinitionsPart != null && @@ -3098,64 +3286,110 @@ private static void CopyNumberingForGlossaryDocumentPartToGD(NumberingDefinition new[] { newDocument.MainDocumentPart.GlossaryDocumentPart.NumberingDefinitionsPart.GetXDocument().Root }, images); } if (newDocument.MainDocumentPart.GlossaryDocumentPart.NumberingDefinitionsPart != null) + { newDocument.MainDocumentPart.GlossaryDocumentPart.NumberingDefinitionsPart.PutXDocument(); + } } private static void CopyRelatedImage(OpenXmlPart oldContentPart, OpenXmlPart newContentPart, XElement imageReference, XName attributeName, List images) { - string relId = (string)imageReference.Attribute(attributeName); + var relId = (string)imageReference.Attribute(attributeName); if (string.IsNullOrEmpty(relId)) + { return; + } // First look to see if this relId has already been added to the new document. // This is necessary for those parts that get processed with both old and new ids, such as the comments // part. This is not necessary for parts such as the main document part, but this code won't malfunction // in that case. var tempPartIdPair5 = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId); - if (tempPartIdPair5 != null) + if (tempPartIdPair5 != default) + { return; + } - ExternalRelationship tempEr5 = newContentPart.ExternalRelationships.FirstOrDefault(er => er.Id == relId); + var tempEr5 = newContentPart.ExternalRelationships.FirstOrDefault(er => er.Id == relId); if (tempEr5 != null) + { return; + } var ipp2 = oldContentPart.Parts.FirstOrDefault(ipp => ipp.RelationshipId == relId); - if (ipp2 != null) + if (ipp2 != default) { var oldPart2 = ipp2.OpenXmlPart; if (!(oldPart2 is ImagePart)) + { throw new DocumentBuilderException("Invalid document - target part is not ImagePart"); + } - ImagePart oldPart = (ImagePart)ipp2.OpenXmlPart; - ImageData temp = ManageImageCopy(oldPart, newContentPart, images); + var oldPart = (ImagePart)ipp2.OpenXmlPart; + var temp = ManageImageCopy(oldPart, newContentPart, images); if (temp.ImagePart == null) { - ImagePart newPart = null; + ImagePart? newPart = null; if (newContentPart is MainDocumentPart) + { newPart = ((MainDocumentPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is HeaderPart) + { newPart = ((HeaderPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is FooterPart) + { newPart = ((FooterPart)newContentPart).AddImagePart(oldPart.ContentType); - if (newContentPart is EndnotesPart) - newPart = ((EndnotesPart)newContentPart).AddImagePart(oldPart.ContentType); + } + + if (newContentPart is EndnotesPart part) + { + newPart = part.AddImagePart(oldPart.ContentType); + } + if (newContentPart is FootnotesPart) + { newPart = ((FootnotesPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is ThemePart) + { newPart = ((ThemePart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is WordprocessingCommentsPart) + { newPart = ((WordprocessingCommentsPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is DocumentSettingsPart) + { newPart = ((DocumentSettingsPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is ChartPart) + { newPart = ((ChartPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is NumberingDefinitionsPart) + { newPart = ((NumberingDefinitionsPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is DiagramDataPart) + { newPart = ((DiagramDataPart)newContentPart).AddImagePart(oldPart.ContentType); + } + if (newContentPart is ChartDrawingPart) + { newPart = ((ChartDrawingPart)newContentPart).AddImagePart(oldPart.ContentType); + } + temp.ImagePart = newPart; var id = newContentPart.GetIdOfPart(newPart); temp.AddContentPartRelTypeResourceIdTupple(newContentPart, newPart.RelationshipType, id); @@ -3190,10 +3424,10 @@ private static void CopyRelatedImage(OpenXmlPart oldContentPart, OpenXmlPart new } else { - ExternalRelationship er = oldContentPart.ExternalRelationships.FirstOrDefault(er1 => er1.Id == relId); + var er = oldContentPart.ExternalRelationships.FirstOrDefault(er1 => er1.Id == relId); if (er != null) { - ExternalRelationship newEr = newContentPart.AddExternalRelationship(er.RelationshipType, er.Uri); + var newEr = newContentPart.AddExternalRelationship(er.RelationshipType, er.Uri); imageReference.Attribute(R.id).Value = newEr.Id; } throw new DocumentBuilderInternalException("Source {0} is unsupported document - contains reference to NULL image"); @@ -3206,29 +3440,31 @@ private static void CopyRelatedPartsForContentParts(OpenXmlPart oldContentPart, var relevantElements = newContent.DescendantsAndSelf() .Where(d => d.Name == VML.imagedata || d.Name == VML.fill || d.Name == VML.stroke || d.Name == A.blip) .ToList(); - foreach (XElement imageReference in relevantElements) + foreach (var imageReference in relevantElements) { CopyRelatedImage(oldContentPart, newContentPart, imageReference, R.embed, images); CopyRelatedImage(oldContentPart, newContentPart, imageReference, R.pict, images); CopyRelatedImage(oldContentPart, newContentPart, imageReference, R.id, images); } - foreach (XElement diagramReference in newContent.DescendantsAndSelf().Where(d => d.Name == DGM.relIds || d.Name == A.relIds)) + foreach (var diagramReference in newContent.DescendantsAndSelf().Where(d => d.Name == DGM.relIds || d.Name == A.relIds)) { // dm attribute - string relId = diagramReference.Attribute(R.dm).Value; + var relId = diagramReference.Attribute(R.dm).Value; var ipp = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId); - if (ipp != null) + if (ipp != default) { - OpenXmlPart tempPart = ipp.OpenXmlPart; + var tempPart = ipp.OpenXmlPart; continue; } - ExternalRelationship tempEr = newContentPart.ExternalRelationships.FirstOrDefault(er2 => er2.Id == relId); + var tempEr = newContentPart.ExternalRelationships.FirstOrDefault(er2 => er2.Id == relId); if (tempEr != null) + { continue; + } - OpenXmlPart oldPart = oldContentPart.GetPartById(relId); + var oldPart = oldContentPart.GetPartById(relId); OpenXmlPart newPart = newContentPart.AddNewPart(); newPart.GetXDocument().Add(oldPart.GetXDocument().Root); diagramReference.Attribute(R.dm).Value = newContentPart.GetIdOfPart(newPart); @@ -3238,16 +3474,17 @@ private static void CopyRelatedPartsForContentParts(OpenXmlPart oldContentPart, // lo attribute relId = diagramReference.Attribute(R.lo).Value; var ipp2 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId); - if (ipp2 != null) + if (ipp2 != default) { - OpenXmlPart tempPart = ipp2.OpenXmlPart; + var tempPart = ipp2.OpenXmlPart; continue; } - - ExternalRelationship tempEr4 = newContentPart.ExternalRelationships.FirstOrDefault(er3 => er3.Id == relId); + var tempEr4 = newContentPart.ExternalRelationships.FirstOrDefault(er3 => er3.Id == relId); if (tempEr4 != null) + { continue; + } oldPart = oldContentPart.GetPartById(relId); newPart = newContentPart.AddNewPart(); @@ -3259,15 +3496,17 @@ private static void CopyRelatedPartsForContentParts(OpenXmlPart oldContentPart, // qs attribute relId = diagramReference.Attribute(R.qs).Value; var ipp5 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId); - if (ipp5 != null) + if (ipp5 != default) { - OpenXmlPart tempPart = ipp5.OpenXmlPart; + var tempPart = ipp5.OpenXmlPart; continue; } - ExternalRelationship tempEr5 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); + var tempEr5 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); if (tempEr5 != null) + { continue; + } oldPart = oldContentPart.GetPartById(relId); newPart = newContentPart.AddNewPart(); @@ -3279,15 +3518,17 @@ private static void CopyRelatedPartsForContentParts(OpenXmlPart oldContentPart, // cs attribute relId = diagramReference.Attribute(R.cs).Value; var ipp6 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId); - if (ipp6 != null) + if (ipp6 != default) { - OpenXmlPart tempPart = ipp6.OpenXmlPart; + var tempPart = ipp6.OpenXmlPart; continue; } - ExternalRelationship tempEr6 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); + var tempEr6 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); if (tempEr6 != null) + { continue; + } oldPart = oldContentPart.GetPartById(relId); newPart = newContentPart.AddNewPart(); @@ -3297,69 +3538,110 @@ private static void CopyRelatedPartsForContentParts(OpenXmlPart oldContentPart, CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newPart.GetXDocument().Root }, images); } - foreach (XElement oleReference in newContent.DescendantsAndSelf(O.OLEObject)) + foreach (var oleReference in newContent.DescendantsAndSelf(O.OLEObject)) { - string relId = (string)oleReference.Attribute(R.id); + var relId = (string)oleReference.Attribute(R.id); // First look to see if this relId has already been added to the new document. // This is necessary for those parts that get processed with both old and new ids, such as the comments // part. This is not necessary for parts such as the main document part, but this code won't malfunction // in that case. var ipp1 = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId); - if (ipp1 != null) + if (ipp1 != default) { - OpenXmlPart tempPart = ipp1.OpenXmlPart; + var tempPart = ipp1.OpenXmlPart; continue; } - ExternalRelationship tempEr1 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); + var tempEr1 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); if (tempEr1 != null) + { continue; + } var ipp4 = oldContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId); - if (ipp4 != null) + if (ipp4 != default) { - OpenXmlPart oldPart = oldContentPart.GetPartById(relId); - OpenXmlPart newPart = null; + var oldPart = oldContentPart.GetPartById(relId); + OpenXmlPart? newPart = null; if (oldPart is EmbeddedObjectPart) { if (newContentPart is HeaderPart) + { newPart = ((HeaderPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType); + } + if (newContentPart is FooterPart) + { newPart = ((FooterPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType); + } + if (newContentPart is MainDocumentPart) + { newPart = ((MainDocumentPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType); + } + if (newContentPart is FootnotesPart) + { newPart = ((FootnotesPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType); + } + if (newContentPart is EndnotesPart) + { newPart = ((EndnotesPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType); + } + if (newContentPart is WordprocessingCommentsPart) + { newPart = ((WordprocessingCommentsPart)newContentPart).AddEmbeddedObjectPart(oldPart.ContentType); + } } else if (oldPart is EmbeddedPackagePart) { if (newContentPart is HeaderPart) + { newPart = ((HeaderPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType); + } + if (newContentPart is FooterPart) + { newPart = ((FooterPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType); + } + if (newContentPart is MainDocumentPart) + { newPart = ((MainDocumentPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType); + } + if (newContentPart is FootnotesPart) + { newPart = ((FootnotesPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType); + } + if (newContentPart is EndnotesPart) + { newPart = ((EndnotesPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType); + } + if (newContentPart is WordprocessingCommentsPart) + { newPart = ((WordprocessingCommentsPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType); + } + if (newContentPart is ChartPart) + { newPart = ((ChartPart)newContentPart).AddEmbeddedPackagePart(oldPart.ContentType); + } } - using (Stream oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read)) - using (Stream newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) + using (var oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read)) + using (var newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) { int byteCount; - byte[] buffer = new byte[65536]; + var buffer = new byte[65536]; while ((byteCount = oldObject.Read(buffer, 0, 65536)) != 0) + { newObject.Write(buffer, 0, byteCount); + } } oleReference.Attribute(R.id).Value = newContentPart.GetIdOfPart(newPart); } @@ -3367,66 +3649,78 @@ private static void CopyRelatedPartsForContentParts(OpenXmlPart oldContentPart, { if (relId != null) { - ExternalRelationship er = oldContentPart.GetExternalRelationship(relId); - ExternalRelationship newEr = newContentPart.AddExternalRelationship(er.RelationshipType, er.Uri); + var er = oldContentPart.GetExternalRelationship(relId); + var newEr = newContentPart.AddExternalRelationship(er.RelationshipType, er.Uri); oleReference.Attribute(R.id).Value = newEr.Id; } } } - foreach (XElement chartReference in newContent.DescendantsAndSelf(C.chart)) + foreach (var chartReference in newContent.DescendantsAndSelf(C.chart)) { - string relId = (string)chartReference.Attribute(R.id); + var relId = (string)chartReference.Attribute(R.id); if (string.IsNullOrEmpty(relId)) + { continue; + } + var ipp2 = newContentPart.Parts.FirstOrDefault(z => z.RelationshipId == relId); - if (ipp2 != null) + if (ipp2 != default) { - OpenXmlPart tempPart = ipp2.OpenXmlPart; + var tempPart = ipp2.OpenXmlPart; continue; } - ExternalRelationship tempEr2 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); + var tempEr2 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); if (tempEr2 != null) + { continue; + } var ipp3 = oldContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId); - if (ipp3 == null) + if (ipp3 == default) + { continue; - ChartPart oldPart = (ChartPart)ipp3.OpenXmlPart; - XDocument oldChart = oldPart.GetXDocument(); - ChartPart newPart = newContentPart.AddNewPart(); - XDocument newChart = newPart.GetXDocument(); + } + + var oldPart = (ChartPart)ipp3.OpenXmlPart; + var oldChart = oldPart.GetXDocument(); + var newPart = newContentPart.AddNewPart(); + var newChart = newPart.GetXDocument(); newChart.Add(oldChart.Root); chartReference.Attribute(R.id).Value = newContentPart.GetIdOfPart(newPart); CopyChartObjects(oldPart, newPart); CopyRelatedPartsForContentParts(oldPart, newPart, new[] { newChart.Root }, images); } - foreach (XElement userShape in newContent.DescendantsAndSelf(C.userShapes)) + foreach (var userShape in newContent.DescendantsAndSelf(C.userShapes)) { - string relId = (string)userShape.Attribute(R.id); + var relId = (string)userShape.Attribute(R.id); if (string.IsNullOrEmpty(relId)) + { continue; + } var ipp4 = newContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId); - if (ipp4 != null) + if (ipp4 != default) { - OpenXmlPart tempPart = ipp4.OpenXmlPart; + var tempPart = ipp4.OpenXmlPart; continue; } - ExternalRelationship tempEr4 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); + var tempEr4 = newContentPart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); if (tempEr4 != null) + { continue; + } var ipp5 = oldContentPart.Parts.FirstOrDefault(p => p.RelationshipId == relId); - if (ipp5 != null) + if (ipp5 != default) { - ChartDrawingPart oldPart = (ChartDrawingPart)ipp5.OpenXmlPart; - XDocument oldXDoc = oldPart.GetXDocument(); - ChartDrawingPart newPart = newContentPart.AddNewPart(); - XDocument newXDoc = newPart.GetXDocument(); + var oldPart = (ChartDrawingPart)ipp5.OpenXmlPart; + var oldXDoc = oldPart.GetXDocument(); + var newPart = newContentPart.AddNewPart(); + var newXDoc = newPart.GetXDocument(); newXDoc.Add(oldXDoc.Root); userShape.Attribute(R.id).Value = newContentPart.GetIdOfPart(newPart); AddRelationships(oldPart, newPart, newContent); @@ -3439,37 +3733,45 @@ private static void CopyFontTable(FontTablePart oldFontTablePart, FontTablePart { var relevantElements = oldFontTablePart.GetXDocument().Descendants().Where(d => d.Name == W.embedRegular || d.Name == W.embedBold || d.Name == W.embedItalic || d.Name == W.embedBoldItalic).ToList(); - foreach (XElement fontReference in relevantElements) + foreach (var fontReference in relevantElements) { - string relId = (string)fontReference.Attribute(R.id); + var relId = (string)fontReference.Attribute(R.id); if (string.IsNullOrEmpty(relId)) + { continue; + } var ipp1 = newFontTablePart.Parts.FirstOrDefault(z => z.RelationshipId == relId); - if (ipp1 != null) + if (ipp1 != default) { - OpenXmlPart tempPart = ipp1.OpenXmlPart; + var tempPart = ipp1.OpenXmlPart; continue; } - ExternalRelationship tempEr1 = newFontTablePart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); + var tempEr1 = newFontTablePart.ExternalRelationships.FirstOrDefault(z => z.Id == relId); if (tempEr1 != null) + { continue; + } var oldPart2 = oldFontTablePart.GetPartById(relId); - if (oldPart2 == null || (!(oldPart2 is FontPart))) + if (oldPart2 == null || !(oldPart2 is FontPart)) + { throw new DocumentBuilderException("Invalid document - FontTablePart contains invalid relationship"); + } - FontPart oldPart = (FontPart)oldPart2; - FontPart newPart = newFontTablePart.AddFontPart(oldPart.ContentType); + var oldPart = (FontPart)oldPart2; + var newPart = newFontTablePart.AddFontPart(oldPart.ContentType); var ResourceID = newFontTablePart.GetIdOfPart(newPart); - using (Stream oldFont = oldPart.GetStream(FileMode.Open, FileAccess.Read)) - using (Stream newFont = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) + using (var oldFont = oldPart.GetStream(FileMode.Open, FileAccess.Read)) + using (var newFont = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) { int byteCount; - byte[] buffer = new byte[65536]; + var buffer = new byte[65536]; while ((byteCount = oldFont.Read(buffer, 0, 65536)) != 0) + { newFont.Write(buffer, 0, byteCount); + } } fontReference.Attribute(R.id).Value = ResourceID; } @@ -3477,55 +3779,62 @@ private static void CopyFontTable(FontTablePart oldFontTablePart, FontTablePart private static void CopyChartObjects(ChartPart oldChart, ChartPart newChart) { - foreach (XElement dataReference in newChart.GetXDocument().Descendants(C.externalData)) + foreach (var dataReference in newChart.GetXDocument().Descendants(C.externalData)) { - string relId = dataReference.Attribute(R.id).Value; + var relId = dataReference.Attribute(R.id).Value; var ipp1 = oldChart.Parts.FirstOrDefault(z => z.RelationshipId == relId); - if (ipp1 != null) + if (ipp1 != default) { var oldRelatedPart = ipp1.OpenXmlPart; if (oldRelatedPart is EmbeddedPackagePart) { - EmbeddedPackagePart oldPart = (EmbeddedPackagePart)ipp1.OpenXmlPart; - EmbeddedPackagePart newPart = newChart.AddEmbeddedPackagePart(oldPart.ContentType); - using (Stream oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read)) - using (Stream newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) + var oldPart = (EmbeddedPackagePart)ipp1.OpenXmlPart; + var newPart = newChart.AddEmbeddedPackagePart(oldPart.ContentType); + using (var oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read)) + using (var newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) { int byteCount; - byte[] buffer = new byte[65536]; + var buffer = new byte[65536]; while ((byteCount = oldObject.Read(buffer, 0, 65536)) != 0) + { newObject.Write(buffer, 0, byteCount); + } } dataReference.Attribute(R.id).Value = newChart.GetIdOfPart(newPart); } else if (oldRelatedPart is EmbeddedObjectPart) { - EmbeddedObjectPart oldPart = (EmbeddedObjectPart)ipp1.OpenXmlPart; + var oldPart = (EmbeddedObjectPart)ipp1.OpenXmlPart; var relType = oldRelatedPart.RelationshipType; var conType = oldRelatedPart.ContentType; var g = new Guid(); - string id = $"R{g:N}".Substring(0, 8); + var id = $"R{g:N}".Substring(0, 8); var newPart = newChart.AddExtendedPart(relType, conType, ".bin", id); - using (Stream oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read)) - using (Stream newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) + using (var oldObject = oldPart.GetStream(FileMode.Open, FileAccess.Read)) + using (var newObject = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) { int byteCount; - byte[] buffer = new byte[65536]; + var buffer = new byte[65536]; while ((byteCount = oldObject.Read(buffer, 0, 65536)) != 0) + { newObject.Write(buffer, 0, byteCount); + } } dataReference.Attribute(R.id).Value = newChart.GetIdOfPart(newPart); } } else { - ExternalRelationship oldRelationship = oldChart.GetExternalRelationship(relId); - Guid g = Guid.NewGuid(); - string newRid = $"R{g:N}"; + var oldRelationship = oldChart.GetExternalRelationship(relId); + var g = Guid.NewGuid(); + var newRid = $"R{g:N}"; var oldRel = oldChart.ExternalRelationships.FirstOrDefault(h => h.Id == relId); if (oldRel == null) + { throw new DocumentBuilderInternalException("Internal Error 0007"); + } + newChart.AddExternalRelationship(oldRel.RelationshipType, oldRel.Uri, newRid); dataReference.Attribute(R.id).Value = newRid; } @@ -3536,71 +3845,71 @@ private static void CopyStartingParts(WordprocessingDocument sourceDocument, Wor List images) { // A Core File Properties part does not have implicit or explicit relationships to other parts. - CoreFilePropertiesPart corePart = sourceDocument.CoreFilePropertiesPart; + var corePart = sourceDocument.CoreFilePropertiesPart; if (corePart != null && corePart.GetXDocument().Root != null) { newDocument.AddCoreFilePropertiesPart(); - XDocument newXDoc = newDocument.CoreFilePropertiesPart.GetXDocument(); + var newXDoc = newDocument.CoreFilePropertiesPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; - XDocument sourceXDoc = corePart.GetXDocument(); + var sourceXDoc = corePart.GetXDocument(); newXDoc.Add(sourceXDoc.Root); } // An application attributes part does not have implicit or explicit relationships to other parts. - ExtendedFilePropertiesPart extPart = sourceDocument.ExtendedFilePropertiesPart; + var extPart = sourceDocument.ExtendedFilePropertiesPart; if (extPart != null) { OpenXmlPart newPart = newDocument.AddExtendedFilePropertiesPart(); - XDocument newXDoc = newDocument.ExtendedFilePropertiesPart.GetXDocument(); + var newXDoc = newDocument.ExtendedFilePropertiesPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(extPart.GetXDocument().Root); } // An custom file properties part does not have implicit or explicit relationships to other parts. - CustomFilePropertiesPart customPart = sourceDocument.CustomFilePropertiesPart; + var customPart = sourceDocument.CustomFilePropertiesPart; if (customPart != null) { newDocument.AddCustomFilePropertiesPart(); - XDocument newXDoc = newDocument.CustomFilePropertiesPart.GetXDocument(); + var newXDoc = newDocument.CustomFilePropertiesPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(customPart.GetXDocument().Root); } - DocumentSettingsPart oldSettingsPart = sourceDocument.MainDocumentPart.DocumentSettingsPart; + var oldSettingsPart = sourceDocument.MainDocumentPart.DocumentSettingsPart; if (oldSettingsPart != null) { - DocumentSettingsPart newSettingsPart = newDocument.MainDocumentPart.AddNewPart(); - XDocument settingsXDoc = oldSettingsPart.GetXDocument(); + var newSettingsPart = newDocument.MainDocumentPart.AddNewPart(); + var settingsXDoc = oldSettingsPart.GetXDocument(); AddRelationships(oldSettingsPart, newSettingsPart, new[] { settingsXDoc.Root }); - CopyFootnotesPart(sourceDocument, newDocument, settingsXDoc, images); - CopyEndnotesPart(sourceDocument, newDocument, settingsXDoc, images); - XDocument newXDoc = newDocument.MainDocumentPart.DocumentSettingsPart.GetXDocument(); + CopyFootnotesPart(sourceDocument, newDocument, settingsXDoc); + CopyEndnotesPart(sourceDocument, newDocument, settingsXDoc); + var newXDoc = newDocument.MainDocumentPart.DocumentSettingsPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(settingsXDoc.Root); CopyRelatedPartsForContentParts(oldSettingsPart, newSettingsPart, new[] { newXDoc.Root }, images); } - WebSettingsPart oldWebSettingsPart = sourceDocument.MainDocumentPart.WebSettingsPart; + var oldWebSettingsPart = sourceDocument.MainDocumentPart.WebSettingsPart; if (oldWebSettingsPart != null) { - WebSettingsPart newWebSettingsPart = newDocument.MainDocumentPart.AddNewPart(); - XDocument settingsXDoc = oldWebSettingsPart.GetXDocument(); + var newWebSettingsPart = newDocument.MainDocumentPart.AddNewPart(); + var settingsXDoc = oldWebSettingsPart.GetXDocument(); AddRelationships(oldWebSettingsPart, newWebSettingsPart, new[] { settingsXDoc.Root }); - XDocument newXDoc = newDocument.MainDocumentPart.WebSettingsPart.GetXDocument(); + var newXDoc = newDocument.MainDocumentPart.WebSettingsPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(settingsXDoc.Root); } - ThemePart themePart = sourceDocument.MainDocumentPart.ThemePart; + var themePart = sourceDocument.MainDocumentPart.ThemePart; if (themePart != null) { - ThemePart newThemePart = newDocument.MainDocumentPart.AddNewPart(); - XDocument newXDoc = newDocument.MainDocumentPart.ThemePart.GetXDocument(); + var newThemePart = newDocument.MainDocumentPart.AddNewPart(); + var newXDoc = newDocument.MainDocumentPart.ThemePart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(themePart.GetXDocument().Root); @@ -3613,22 +3922,22 @@ private static void CopyStartingParts(WordprocessingDocument sourceDocument, Wor // MainDocumentPart.GlossaryDocumentPart.StylesWithEffectsPart // A Style Definitions part shall not have implicit or explicit relationships to any other part. - StyleDefinitionsPart stylesPart = sourceDocument.MainDocumentPart.StyleDefinitionsPart; + var stylesPart = sourceDocument.MainDocumentPart.StyleDefinitionsPart; if (stylesPart != null) { newDocument.MainDocumentPart.AddNewPart(); - XDocument newXDoc = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var newXDoc = newDocument.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; newXDoc.Add(new XElement(W.styles, new XAttribute(XNamespace.Xmlns + "w", W.w) - + //, //stylesPart.GetXDocument().Descendants(W.docDefaults) - + //, //new XElement(W.latentStyles, stylesPart.GetXDocument().Descendants(W.latentStyles).Attributes()) - + )); MergeDocDefaultStyles(stylesPart.GetXDocument(), newXDoc); MergeStyles(sourceDocument, newDocument, stylesPart.GetXDocument(), newXDoc, Enumerable.Empty()); @@ -3636,11 +3945,11 @@ private static void CopyStartingParts(WordprocessingDocument sourceDocument, Wor } // A Font Table part shall not have any implicit or explicit relationships to any other part. - FontTablePart fontTablePart = sourceDocument.MainDocumentPart.FontTablePart; + var fontTablePart = sourceDocument.MainDocumentPart.FontTablePart; if (fontTablePart != null) { newDocument.MainDocumentPart.AddNewPart(); - XDocument newXDoc = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); + var newXDoc = newDocument.MainDocumentPart.FontTablePart.GetXDocument(); newXDoc.Declaration.Standalone = Yes; newXDoc.Declaration.Encoding = Utf8; CopyFontTable(sourceDocument.MainDocumentPart.FontTablePart, newDocument.MainDocumentPart.FontTablePart); @@ -3649,20 +3958,29 @@ private static void CopyStartingParts(WordprocessingDocument sourceDocument, Wor } private static void CopyFootnotesPart(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, - XDocument settingsXDoc, List images) + XDocument settingsXDoc) { - int number = 0; - XDocument oldFootnotes = null; - XDocument newFootnotes = null; - XElement footnotePr = settingsXDoc.Root.Element(W.footnotePr); + var number = 0; + XDocument? oldFootnotes = null; + XDocument? newFootnotes = null; + var footnotePr = settingsXDoc.Root.Element(W.footnotePr); if (footnotePr == null) + { return; + } + if (sourceDocument.MainDocumentPart.FootnotesPart == null) + { return; - foreach (XElement footnote in footnotePr.Elements(W.footnote)) + } + + foreach (var footnote in footnotePr.Elements(W.footnote)) { if (oldFootnotes == null) + { oldFootnotes = sourceDocument.MainDocumentPart.FootnotesPart.GetXDocument(); + } + if (newFootnotes == null) { if (newDocument.MainDocumentPart.FootnotesPart != null) @@ -3672,7 +3990,9 @@ private static void CopyFootnotesPart(WordprocessingDocument sourceDocument, Wor newFootnotes.Declaration.Encoding = Utf8; var ids = newFootnotes.Root.Elements(W.footnote).Select(f => (int)f.Attribute(W.id)); if (ids.Any()) + { number = ids.Max() + 1; + } } else { @@ -3683,14 +4003,13 @@ private static void CopyFootnotesPart(WordprocessingDocument sourceDocument, Wor newFootnotes.Add(new XElement(W.footnotes, NamespaceAttributes)); } } - string id = (string)footnote.Attribute(W.id); - XElement element = oldFootnotes.Descendants() + var id = (string)footnote.Attribute(W.id); + var element = oldFootnotes.Descendants() .Elements(W.footnote) - .Where(p => ((string)p.Attribute(W.id)) == id) - .FirstOrDefault(); +.FirstOrDefault(p => (string)p.Attribute(W.id) == id); if (element != null) { - XElement newElement = new XElement(element); + var newElement = new XElement(element); // the following adds the footnote into the new settings part newElement.Attribute(W.id).Value = number.ToString(); newFootnotes.Root.Add(newElement); @@ -3701,20 +4020,29 @@ private static void CopyFootnotesPart(WordprocessingDocument sourceDocument, Wor } private static void CopyEndnotesPart(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, - XDocument settingsXDoc, List images) + XDocument settingsXDoc) { - int number = 0; - XDocument oldEndnotes = null; - XDocument newEndnotes = null; - XElement endnotePr = settingsXDoc.Root.Element(W.endnotePr); + var number = 0; + XDocument? oldEndnotes = null; + XDocument? newEndnotes = null; + var endnotePr = settingsXDoc.Root.Element(W.endnotePr); if (endnotePr == null) + { return; + } + if (sourceDocument.MainDocumentPart.EndnotesPart == null) + { return; - foreach (XElement endnote in endnotePr.Elements(W.endnote)) + } + + foreach (var endnote in endnotePr.Elements(W.endnote)) { if (oldEndnotes == null) + { oldEndnotes = sourceDocument.MainDocumentPart.EndnotesPart.GetXDocument(); + } + if (newEndnotes == null) { if (newDocument.MainDocumentPart.EndnotesPart != null) @@ -3726,7 +4054,9 @@ private static void CopyEndnotesPart(WordprocessingDocument sourceDocument, Word .Elements(W.endnote) .Select(f => (int)f.Attribute(W.id)); if (ids.Any()) + { number = ids.Max() + 1; + } } else { @@ -3737,14 +4067,13 @@ private static void CopyEndnotesPart(WordprocessingDocument sourceDocument, Word newEndnotes.Add(new XElement(W.endnotes, NamespaceAttributes)); } } - string id = (string)endnote.Attribute(W.id); - XElement element = oldEndnotes.Descendants() + var id = (string)endnote.Attribute(W.id); + var element = oldEndnotes.Descendants() .Elements(W.endnote) - .Where(p => ((string)p.Attribute(W.id)) == id) - .FirstOrDefault(); +.FirstOrDefault(p => (string)p.Attribute(W.id) == id); if (element != null) { - XElement newElement = new XElement(element); + var newElement = new XElement(element); newElement.Attribute(W.id).Value = number.ToString(); newEndnotes.Root.Add(newElement); endnote.Attribute(W.id).Value = number.ToString(); @@ -3785,15 +4114,13 @@ public static void FixRanges(XDocument sourceDocument, IEnumerable new W.moveToRangeEnd, W.id, null); - DeleteUnmatchedRange(sourceDocument, - newContent, + DeleteUnmatchedRange(newContent, W.moveFromRangeStart, W.moveFromRangeEnd, W.moveToRangeStart, W.name, W.id); - DeleteUnmatchedRange(sourceDocument, - newContent, + DeleteUnmatchedRange(newContent, W.moveToRangeStart, W.moveToRangeEnd, W.moveFromRangeStart, @@ -3804,77 +4131,85 @@ public static void FixRanges(XDocument sourceDocument, IEnumerable new private static void AddAtBeginning(IEnumerable newContent, XElement contentToAdd) { if (newContent.First().Element(W.pPr) != null) + { newContent.First().Element(W.pPr).AddAfterSelf(contentToAdd); + } else + { newContent.First().AddFirst(new XElement(contentToAdd)); + } } private static void AddAtEnd(IEnumerable newContent, XElement contentToAdd) { if (newContent.Last().Element(W.pPr) != null) + { newContent.Last().Element(W.pPr).AddAfterSelf(new XElement(contentToAdd)); + } else + { newContent.Last().Add(new XElement(contentToAdd)); + } } // If the set of paragraphs from sourceDocument don't have a complete start/end for bookmarks, // comments, etc., then this adds them to the paragraph. Note that this adds them to // sourceDocument, and is impure. private static void FixRange(XDocument sourceDocument, IEnumerable newContent, - XName startElement, XName endElement, XName idAttribute, XName refElement) + XName startElement, XName endElement, XName idAttribute, XName? refElement) { - foreach (XElement start in newContent.DescendantsAndSelf(startElement)) + foreach (var start in newContent.DescendantsAndSelf(startElement)) { - string rangeId = start.Attribute(idAttribute).Value; - if (newContent + var rangeId = start.Attribute(idAttribute).Value; + if (!newContent .DescendantsAndSelf(endElement) - .Where(e => e.Attribute(idAttribute).Value == rangeId) - .Count() == 0) +.Any(e => e.Attribute(idAttribute).Value == rangeId)) { - XElement end = sourceDocument + var end = sourceDocument .Descendants(endElement) - .Where(o => o.Attribute(idAttribute).Value == rangeId) - .FirstOrDefault(); +.FirstOrDefault(o => o.Attribute(idAttribute).Value == rangeId); if (end != null) { AddAtEnd(newContent, new XElement(end)); if (refElement != null) { - XElement newRef = new XElement(refElement, new XAttribute(idAttribute, rangeId)); + var newRef = new XElement(refElement, new XAttribute(idAttribute, rangeId)); AddAtEnd(newContent, new XElement(newRef)); } } } } - foreach (XElement end in newContent.Elements(endElement)) + foreach (var end in newContent.Elements(endElement)) { - string rangeId = end.Attribute(idAttribute).Value; - if (newContent + var rangeId = end.Attribute(idAttribute).Value; + if (!newContent .DescendantsAndSelf(startElement) - .Where(s => s.Attribute(idAttribute).Value == rangeId) - .Count() == 0) +.Any(s => s.Attribute(idAttribute).Value == rangeId)) { - XElement start = sourceDocument + var start = sourceDocument .Descendants(startElement) - .Where(o => o.Attribute(idAttribute).Value == rangeId) - .FirstOrDefault(); +.FirstOrDefault(o => o.Attribute(idAttribute).Value == rangeId); if (start != null) + { AddAtBeginning(newContent, new XElement(start)); + } } } } - private static void DeleteUnmatchedRange(XDocument sourceDocument, IEnumerable newContent, + private static void DeleteUnmatchedRange(IEnumerable newContent, XName startElement, XName endElement, XName matchTo, XName matchAttr, XName idAttr) { - List deleteList = new List(); - foreach (XElement start in newContent.Elements(startElement)) + var deleteList = new List(); + foreach (var start in newContent.Elements(startElement)) { - string id = start.Attribute(matchAttr).Value; - if (!newContent.Elements(matchTo).Where(n => n.Attribute(matchAttr).Value == id).Any()) + var id = start.Attribute(matchAttr).Value; + if (!newContent.Elements(matchTo).Any(n => n.Attribute(matchAttr).Value == id)) + { deleteList.Add(start.Attribute(idAttr).Value); + } } - foreach (string item in deleteList) + foreach (var item in deleteList) { newContent.Elements(startElement).Where(n => n.Attribute(idAttr).Value == item).Remove(); newContent.Elements(endElement).Where(n => n.Attribute(idAttr).Value == item).Remove(); @@ -3886,13 +4221,16 @@ private static void DeleteUnmatchedRange(XDocument sourceDocument, IEnumerable newContent, List images) { - int number = 0; - XDocument oldFootnotes = null; - XDocument newFootnotes = null; - foreach (XElement footnote in newContent.DescendantsAndSelf(W.footnoteReference)) + var number = 0; + XDocument? oldFootnotes = null; + XDocument? newFootnotes = null; + foreach (var footnote in newContent.DescendantsAndSelf(W.footnoteReference)) { if (oldFootnotes == null) + { oldFootnotes = sourceDocument.MainDocumentPart.FootnotesPart.GetXDocument(); + } + if (newFootnotes == null) { if (newDocument.MainDocumentPart.FootnotesPart != null) @@ -3903,7 +4241,9 @@ private static void CopyFootnotes(WordprocessingDocument sourceDocument, Wordpro .Elements(W.footnote) .Select(f => (int)f.Attribute(W.id)); if (ids.Any()) + { number = ids.Max() + 1; + } } else { @@ -3914,15 +4254,14 @@ private static void CopyFootnotes(WordprocessingDocument sourceDocument, Wordpro newFootnotes.Add(new XElement(W.footnotes, NamespaceAttributes)); } } - string id = (string)footnote.Attribute(W.id); - XElement element = oldFootnotes + var id = (string)footnote.Attribute(W.id); + var element = oldFootnotes .Descendants() .Elements(W.footnote) - .Where(p => ((string)p.Attribute(W.id)) == id) - .FirstOrDefault(); +.FirstOrDefault(p => (string)p.Attribute(W.id) == id); if (element != null) { - XElement newElement = new XElement(element); + var newElement = new XElement(element); newElement.Attribute(W.id).Value = number.ToString(); newFootnotes.Root.Add(newElement); footnote.Attribute(W.id).Value = number.ToString(); @@ -3944,13 +4283,16 @@ private static void CopyFootnotes(WordprocessingDocument sourceDocument, Wordpro private static void CopyEndnotes(WordprocessingDocument sourceDocument, WordprocessingDocument newDocument, IEnumerable newContent, List images) { - int number = 0; - XDocument oldEndnotes = null; - XDocument newEndnotes = null; - foreach (XElement endnote in newContent.DescendantsAndSelf(W.endnoteReference)) + var number = 0; + XDocument? oldEndnotes = null; + XDocument? newEndnotes = null; + foreach (var endnote in newContent.DescendantsAndSelf(W.endnoteReference)) { if (oldEndnotes == null) + { oldEndnotes = sourceDocument.MainDocumentPart.EndnotesPart.GetXDocument(); + } + if (newEndnotes == null) { if (newDocument.MainDocumentPart.EndnotesPart != null) @@ -3964,7 +4306,9 @@ private static void CopyEndnotes(WordprocessingDocument sourceDocument, Wordproc .Elements(W.endnote) .Select(f => (int)f.Attribute(W.id)); if (ids.Any()) + { number = ids.Max() + 1; + } } else { @@ -3975,13 +4319,12 @@ private static void CopyEndnotes(WordprocessingDocument sourceDocument, Wordproc newEndnotes.Add(new XElement(W.endnotes, NamespaceAttributes)); } } - string id = (string)endnote.Attribute(W.id); - XElement element = oldEndnotes + var id = (string)endnote.Attribute(W.id); + var element = oldEndnotes .Descendants() .Elements(W.endnote) - .Where(p => ((string)p.Attribute(W.id)) == id) - .First(); - XElement newElement = new XElement(element); +.First(p => (string)p.Attribute(W.id) == id); + var newElement = new XElement(element); newElement.Attribute(W.id).Value = number.ToString(); newEndnotes.Root.Add(newElement); endnote.Attribute(W.id).Value = number.ToString(); @@ -4002,19 +4345,24 @@ private static void CopyEndnotes(WordprocessingDocument sourceDocument, Wordproc // General function for handling images that tries to use an existing image if they are the same private static ImageData ManageImageCopy(ImagePart oldImage, OpenXmlPart newContentPart, List images) { - ImageData oldImageData = new ImageData(oldImage); - foreach (ImageData item in images) + var oldImageData = new ImageData(oldImage); + foreach (var item in images) { if (newContentPart != item.ImagePart) + { continue; + } + if (item.Compare(oldImageData)) + { return item; + } } images.Add(oldImageData); return oldImageData; } - private static XAttribute[] NamespaceAttributes = + private static readonly XAttribute[] NamespaceAttributes = { new XAttribute(XNamespace.Xmlns + "wpc", WPC.wpc), new XAttribute(XNamespace.Xmlns + "mc", MC.mc), @@ -4034,14 +4382,4 @@ private static ImageData ManageImageCopy(ImagePart oldImage, OpenXmlPart newCont new XAttribute(MC.Ignorable, "w14 wp14"), }; } - - public class DocumentBuilderException : Exception - { - public DocumentBuilderException(string message) : base(message) { } - } - - public class DocumentBuilderInternalException : Exception - { - public DocumentBuilderInternalException(string message) : base(message) { } - } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/DocumentBuilder/Source.cs b/OpenXmlPowerTools/DocumentBuilder/Source.cs new file mode 100644 index 00000000..7faa1c47 --- /dev/null +++ b/OpenXmlPowerTools/DocumentBuilder/Source.cs @@ -0,0 +1,141 @@ +#define TestForUnsupportedDocuments +#define MergeStylesWithSameNames + +namespace Codeuctivity.OpenXmlPowerTools.DocumentBuilder +{ + public class Source + { + public WmlDocument WmlDocument { get; set; } + public int Start { get; set; } + public int Count { get; set; } + public bool KeepSections { get; set; } + public bool DiscardHeadersAndFootersInKeptSections { get; set; } + public string? InsertId { get; set; } + + public Source(string fileName) + { + WmlDocument = new WmlDocument(fileName); + Start = 0; + Count = int.MaxValue; + KeepSections = false; + InsertId = null; + } + + public Source(WmlDocument source) + { + WmlDocument = source; + Start = 0; + Count = int.MaxValue; + KeepSections = false; + InsertId = null; + } + + public Source(string fileName, bool keepSections) + { + WmlDocument = new WmlDocument(fileName); + Start = 0; + Count = int.MaxValue; + KeepSections = keepSections; + InsertId = null; + } + + public Source(WmlDocument source, bool keepSections) + { + WmlDocument = source; + Start = 0; + Count = int.MaxValue; + KeepSections = keepSections; + InsertId = null; + } + + public Source(string fileName, string insertId) + { + WmlDocument = new WmlDocument(fileName); + Start = 0; + Count = int.MaxValue; + KeepSections = false; + InsertId = insertId; + } + + public Source(WmlDocument source, string insertId) + { + WmlDocument = source; + Start = 0; + Count = int.MaxValue; + KeepSections = false; + InsertId = insertId; + } + + public Source(string fileName, int start, bool keepSections) + { + WmlDocument = new WmlDocument(fileName); + Start = start; + Count = int.MaxValue; + KeepSections = keepSections; + InsertId = null; + } + + public Source(WmlDocument source, int start, bool keepSections) + { + WmlDocument = source; + Start = start; + Count = int.MaxValue; + KeepSections = keepSections; + InsertId = null; + } + + public Source(string fileName, int start, string insertId) + { + WmlDocument = new WmlDocument(fileName); + Start = start; + Count = int.MaxValue; + KeepSections = false; + InsertId = insertId; + } + + public Source(WmlDocument source, int start, string insertId) + { + WmlDocument = source; + Start = start; + Count = int.MaxValue; + KeepSections = false; + InsertId = insertId; + } + + public Source(string fileName, int start, int count, bool keepSections) + { + WmlDocument = new WmlDocument(fileName); + Start = start; + Count = count; + KeepSections = keepSections; + InsertId = null; + } + + public Source(WmlDocument source, int start, int count, bool keepSections) + { + WmlDocument = source; + Start = start; + Count = count; + KeepSections = keepSections; + InsertId = null; + } + + public Source(string fileName, int start, int count, string insertId) + { + WmlDocument = new WmlDocument(fileName); + Start = start; + Count = count; + KeepSections = false; + InsertId = insertId; + } + + public Source(WmlDocument source, int start, int count, string insertId) + { + WmlDocument = source; + Start = start; + Count = count; + KeepSections = false; + InsertId = insertId; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/DocumentBuilder/WmlDocument.cs b/OpenXmlPowerTools/DocumentBuilder/WmlDocument.cs new file mode 100644 index 00000000..b2b150e1 --- /dev/null +++ b/OpenXmlPowerTools/DocumentBuilder/WmlDocument.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Codeuctivity.OpenXmlPowerTools +{ + public partial class WmlDocument : OpenXmlPowerToolsDocument + { + public IEnumerable SplitOnSections() => DocumentBuilder.DocumentBuilder.SplitOnSections(this); + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/ExcelFormula.cs b/OpenXmlPowerTools/ExcelFormula.cs index f8949e3d..ba788ee8 100644 --- a/OpenXmlPowerTools/ExcelFormula.cs +++ b/OpenXmlPowerTools/ExcelFormula.cs @@ -1,833 +1,911 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. + /* created on 9/8/2012 9:28:14 AM from peg generator V1.0 using 'ExcelFormula.txt' as input*/ -using Peg.Base; using System; using System.IO; -using System.Text; -namespace ExcelFormula + +namespace Codeuctivity.OpenXmlPowerTools { - - enum EExcelFormula{Formula= 1, Expression= 2, InfixTerms= 3, PreAndPostTerm= 4, - Term= 5, RefInfixTerms= 6, RefTerm= 7, Constant= 8, RefConstant= 9, - ErrorConstant= 10, LogicalConstant= 11, NumericalConstant= 12, - SignificandPart= 13, WholeNumberPart= 14, FractionalPart= 15, - ExponentPart= 16, StringConstant= 17, StringCharacter= 18, HighCharacter= 19, - ArrayConstant= 20, ConstantListRows= 21, ConstantListRow= 22, - InfixOperator= 23, ValueInfixOperator= 24, RefInfixOperator= 25, - UnionOperator= 26, IntersectionOperator= 27, RangeOperator= 28, - PostfixOperator= 29, PrefixOperator= 30, CellReference= 31, LocalCellReference= 32, - ExternalCellReference= 33, BookPrefix= 34, BangReference= 35, - SheetRangeReference= 36, SingleSheetPrefix= 37, SingleSheetReference= 38, - SingleSheetArea= 39, SingleSheet= 40, SheetRange= 41, WorkbookIndex= 42, - SheetName= 43, SheetNameCharacter= 44, SheetNameSpecial= 45, - SheetNameBaseCharacter= 46, A1Reference= 47, A1Cell= 48, A1Area= 49, - A1Column= 50, A1AbsoluteColumn= 51, A1RelativeColumn= 52, A1Row= 53, - A1AbsoluteRow= 54, A1RelativeRow= 55, CellFunctionCall= 56, UserDefinedFunctionCall= 57, - UserDefinedFunctionName= 58, ArgumentList= 59, Argument= 60, - ArgumentExpression= 61, ArgumentInfixTerms= 62, ArgumentPreAndPostTerm= 63, - ArgumentTerm= 64, ArgumentRefInfixTerms= 65, ArgumentRefTerm= 66, - ArgumentInfixOperator= 67, RefArgumentInfixOperator= 68, NameReference= 69, - ExternalName= 70, BangName= 71, Name= 72, NameStartCharacter= 73, - NameCharacter= 74, StructureReference= 75, TableIdentifier= 76, - TableName= 77, IntraTableReference= 78, InnerReference= 79, Keyword= 80, - KeywordList= 81, ColumnRange= 82, Column= 83, SimpleColumnName= 84, - EscapeColumnCharacter= 85, UnescapedColumnCharacter= 86, AnyNoSpaceColumnCharacter= 87, - SpacedComma= 88, SpacedLBracket= 89, SpacedRBracket= 90, ws= 91}; - class ExcelFormula : PegCharParser - { - - #region Input Properties - public static EncodingClass encodingClass = EncodingClass.ascii; - public static UnicodeDetection unicodeDetection = UnicodeDetection.notApplicable; - #endregion Input Properties - #region Constructors + internal enum EExcelFormula + { + Formula = 1, Expression = 2, InfixTerms = 3, PreAndPostTerm = 4, + Term = 5, RefInfixTerms = 6, RefTerm = 7, Constant = 8, RefConstant = 9, + ErrorConstant = 10, LogicalConstant = 11, NumericalConstant = 12, + SignificandPart = 13, WholeNumberPart = 14, FractionalPart = 15, + ExponentPart = 16, StringConstant = 17, StringCharacter = 18, HighCharacter = 19, + ArrayConstant = 20, ConstantListRows = 21, ConstantListRow = 22, + InfixOperator = 23, ValueInfixOperator = 24, RefInfixOperator = 25, + UnionOperator = 26, IntersectionOperator = 27, RangeOperator = 28, + PostfixOperator = 29, PrefixOperator = 30, CellReference = 31, LocalCellReference = 32, + ExternalCellReference = 33, BookPrefix = 34, BangReference = 35, + SheetRangeReference = 36, SingleSheetPrefix = 37, SingleSheetReference = 38, + SingleSheetArea = 39, SingleSheet = 40, SheetRange = 41, WorkbookIndex = 42, + SheetName = 43, SheetNameCharacter = 44, SheetNameSpecial = 45, + SheetNameBaseCharacter = 46, A1Reference = 47, A1Cell = 48, A1Area = 49, + A1Column = 50, A1AbsoluteColumn = 51, A1RelativeColumn = 52, A1Row = 53, + A1AbsoluteRow = 54, A1RelativeRow = 55, CellFunctionCall = 56, UserDefinedFunctionCall = 57, + UserDefinedFunctionName = 58, ArgumentList = 59, Argument = 60, + ArgumentExpression = 61, ArgumentInfixTerms = 62, ArgumentPreAndPostTerm = 63, + ArgumentTerm = 64, ArgumentRefInfixTerms = 65, ArgumentRefTerm = 66, + ArgumentInfixOperator = 67, RefArgumentInfixOperator = 68, NameReference = 69, + ExternalName = 70, BangName = 71, Name = 72, NameStartCharacter = 73, + NameCharacter = 74, StructureReference = 75, TableIdentifier = 76, + TableName = 77, IntraTableReference = 78, InnerReference = 79, Keyword = 80, + KeywordList = 81, ColumnRange = 82, Column = 83, SimpleColumnName = 84, + EscapeColumnCharacter = 85, UnescapedColumnCharacter = 86, AnyNoSpaceColumnCharacter = 87, + SpacedComma = 88, SpacedLBracket = 89, SpacedRBracket = 90, ws = 91 + }; + + internal class ExcelFormula : PegCharParser + { + private static readonly OptimizedCharset optimizedCharset0 = new OptimizedCharset(new[] { new OptimizedCharset.Range('A', 'Z'), new OptimizedCharset.Range('a', 'z'), new OptimizedCharset.Range('0', '9'), new OptimizedCharset.Range(',', '.'), }, new char[] { '!', '"', '#', '$', '%', '&', '(', ')', '+', ';', '<', '=', '>', '@', '^', '_', '`', '{', '|', '}', '~', ' ' }); + + private static readonly OptimizedCharset optimizedCharset1 = new OptimizedCharset(new[] { new OptimizedCharset.Range('A', 'Z'), new OptimizedCharset.Range('a', 'z'), new OptimizedCharset.Range('0', '9'), new OptimizedCharset.Range(',', '.'), }, new char[] { '!', '"', '#', '$', '%', '&', '(', ')', '*', '+', '/', ':', ';', '<', '=', '>', '?', '@', '\\', '^', '_', '`', '{', '|', '}', '~' }); + + private static readonly OptimizedLiterals optimizedLiterals0 = new OptimizedLiterals(new[] { "<>", ">=", "<=", "^", "*", "/", "+", "-", "&", "=", "<", ">" }); + private static readonly EncodingClass encodingClass = EncodingClass.ascii; + private static readonly UnicodeDetection unicodeDetection = UnicodeDetection.notApplicable; + public ExcelFormula() - : base() { - } - public ExcelFormula(string src,TextWriter FerrOut) - : base(src,FerrOut) + + public ExcelFormula(string src, TextWriter FerrOut) : base(src, FerrOut) { - } - #endregion Constructors - #region Overrides + public override string GetRuleNameFromId(int id) { try { - EExcelFormula ruleEnum = (EExcelFormula)id; - string s= ruleEnum.ToString(); - int val; - if( int.TryParse(s,out val) ){ - return base.GetRuleNameFromId(id); - }else{ - return s; - } + var ruleEnum = (EExcelFormula)id; + var s = ruleEnum.ToString(); + if (int.TryParse(s, out var val)) + { + return base.GetRuleNameFromId(id); + } + else + { + return s; + } } catch (Exception) { return base.GetRuleNameFromId(id); } } + public override void GetProperties(out EncodingClass encoding, out UnicodeDetection detection) { encoding = encodingClass; detection = unicodeDetection; - } - #endregion Overrides - #region Grammar Rules - public bool Formula() /*Formula: Expression (!./FATAL<"end of line expected">);*/ - { - - return And(()=> - Expression() - && ( Not(()=> Any() ) || Fatal("end of line expected")) ); - } - public bool Expression() /*Expression: ws InfixTerms;*/ - { - - return And(()=> ws() && InfixTerms() ); - } - public bool InfixTerms() /*InfixTerms: PreAndPostTerm (InfixOperator ws PreAndPostTerm)*;*/ - { - - return And(()=> - PreAndPostTerm() - && OptRepeat(()=> - And(()=> - InfixOperator() - && ws() - && PreAndPostTerm() ) ) ); - } - public bool PreAndPostTerm() /*PreAndPostTerm: (PrefixOperator ws)* Term (PostfixOperator ws)*;*/ - { - - return And(()=> - OptRepeat(()=> And(()=> PrefixOperator() && ws() ) ) - && Term() - && OptRepeat(()=> And(()=> PostfixOperator() && ws() ) ) ); - } - public bool Term() /*Term: (RefInfixTerms / '(' Expression ')' / Constant) ws;*/ - { - - return And(()=> - ( - RefInfixTerms() - || And(()=> Char('(') && Expression() && Char(')') ) - || Constant()) - && ws() ); - } - public bool RefInfixTerms() /*RefInfixTerms: RefTerm (RefInfixOperator ws RefTerm)*;*/ - { - - return And(()=> - RefTerm() - && OptRepeat(()=> - And(()=> RefInfixOperator() && ws() && RefTerm() ) ) ); - } - public bool RefTerm() /*RefTerm: '(' ws RefInfixTerms ')' / RefConstant / CellFunctionCall / CellReference / UserDefinedFunctionCall - / NameReference / StructureReference;*/ - { - - return - And(()=> - Char('(') - && ws() - && RefInfixTerms() - && Char(')') ) - || RefConstant() - || CellFunctionCall() - || CellReference() - || UserDefinedFunctionCall() - || NameReference() - || StructureReference(); - } - public bool Constant() /*^^Constant: ErrorConstant / LogicalConstant / NumericalConstant / StringConstant / ArrayConstant;*/ + } + + /// + /// Formula: Expression (!./FATAL\<"end of line expected">) + /// + /// + public bool Formula() + { + return And(() => Expression() && (Not(() => Any()) || Fatal("end of line expected"))); + } + + /// + /// Expression: ws InfixTerms; + /// + /// + public bool Expression() + { + return And(() => ws() && InfixTerms()); + } + + /// + /// InfixTerms: PreAndPostTerm (InfixOperator ws PreAndPostTerm)*; + /// + /// + public bool InfixTerms() + { + return And(() => PreAndPostTerm() && OptRepeat(() => And(() => InfixOperator() && ws() && PreAndPostTerm()))); + } + + /// + /// PreAndPostTerm: (PrefixOperator ws)* Term (PostfixOperator ws)*; + /// + /// + public bool PreAndPostTerm() + { + return And(() => OptRepeat(() => And(() => PrefixOperator() && ws())) && Term() && OptRepeat(() => And(() => PostfixOperator() && ws()))); + } + + /// + /// Term: (RefInfixTerms / '(' Expression ')' / Constant) ws; + /// + /// + public bool Term() + { + return And(() => (RefInfixTerms() || And(() => Char('(') && Expression() && Char(')')) || Constant()) && ws()); + } + + /// + /// RefInfixTerms: RefTerm (RefInfixOperator ws RefTerm)*; + /// + /// + public bool RefInfixTerms() + { + return And(() => RefTerm() && OptRepeat(() => And(() => RefInfixOperator() && ws() && RefTerm()))); + } + + /// + /// RefTerm: '(' ws RefInfixTerms ')' / RefConstant / CellFunctionCall / CellReference / UserDefinedFunctionCall / NameReference / StructureReference; + /// + /// + public bool RefTerm() + { + return And(() => Char('(') && ws() && RefInfixTerms() && Char(')')) || RefConstant() || CellFunctionCall() || CellReference() || UserDefinedFunctionCall() || NameReference() || StructureReference(); + } + + /// + /// ^^Constant: ErrorConstant / LogicalConstant / NumericalConstant / StringConstant / ArrayConstant; + /// + /// + public bool Constant() + { + return TreeNT((int)EExcelFormula.Constant, () => ErrorConstant() || LogicalConstant() || NumericalConstant() || StringConstant() || ArrayConstant()); + } + + /// + /// RefConstant: '#REF!'; + /// + /// + public bool RefConstant() + { + return Char('#', 'R', 'E', 'F', '!'); + } + + /// + /// ErrorConstant: RefConstant / '#DIV/0!' / '#N/A' / '#NAME?' / '#NULL!' / '#NUM!' / '#VALUE!' / '#GETTING_DATA'; + /// + /// + public bool ErrorConstant() + { + return RefConstant() || Char('#', 'D', 'I', 'V', '/', '0', '!') || Char('#', 'N', '/', 'A') || Char('#', 'N', 'A', 'M', 'E', '?') || Char('#', 'N', 'U', 'L', 'L', '!') || Char('#', 'N', 'U', 'M', '!') || Char('#', 'V', 'A', 'L', 'U', 'E', '!') || Char("#GETTING_DATA"); + } + + /// + /// LogicalConstant: 'FALSE' / 'TRUE'; + /// + /// + public bool LogicalConstant() { - - return TreeNT((int)EExcelFormula.Constant,()=> - - ErrorConstant() - || LogicalConstant() - || NumericalConstant() - || StringConstant() - || ArrayConstant() ); - } - public bool RefConstant() /*RefConstant: '#REF!';*/ - { - - return Char('#','R','E','F','!'); - } - public bool ErrorConstant() /*ErrorConstant: RefConstant / '#DIV/0!' / '#N/A' / '#NAME?' / '#NULL!' / '#NUM!' / '#VALUE!' / '#GETTING_DATA';*/ + return Char('F', 'A', 'L', 'S', 'E') || Char('T', 'R', 'U', 'E'); + } + + /// + /// NumericalConstant: '-'? SignificandPart ExponentPart?; + /// + /// + public bool NumericalConstant() { - - return - RefConstant() - || Char('#','D','I','V','/','0','!') - || Char('#','N','/','A') - || Char('#','N','A','M','E','?') - || Char('#','N','U','L','L','!') - || Char('#','N','U','M','!') - || Char('#','V','A','L','U','E','!') - || Char("#GETTING_DATA"); - } - public bool LogicalConstant() /*LogicalConstant: 'FALSE' / 'TRUE';*/ + return And(() => Option(() => Char('-')) && SignificandPart() && Option(() => ExponentPart())); + } + + /// + /// SignificandPart: WholeNumberPart FractionalPart? / FractionalPart; + /// + /// + public bool SignificandPart() { + return And(() => WholeNumberPart() && Option(() => FractionalPart())) || FractionalPart(); + } - return Char('F','A','L','S','E') || Char('T','R','U','E'); - } - public bool NumericalConstant() /*NumericalConstant: '-'? SignificandPart ExponentPart?;*/ + /// + /// WholeNumberPart: [0-9]+; + /// + /// + public bool WholeNumberPart() { + return PlusRepeat(() => In('0', '9')); + } - return And(()=> - Option(()=> Char('-') ) - && SignificandPart() - && Option(()=> ExponentPart() ) ); - } - public bool SignificandPart() /*SignificandPart: WholeNumberPart FractionalPart? / FractionalPart;*/ + /// + /// FractionalPart: '.' [0-9]*; + /// + /// + public bool FractionalPart() { + return And(() => Char('.') && OptRepeat(() => In('0', '9'))); + } - return - And(()=> - WholeNumberPart() - && Option(()=> FractionalPart() ) ) - || FractionalPart(); - } - public bool WholeNumberPart() /*WholeNumberPart: [0-9]+;*/ + /// + /// ExponentPart: 'E' ('+' / '-')? [0-9]*; + /// + /// + public bool ExponentPart() { + return And(() => Char('E') && Option(() => Char('+') || Char('-')) && OptRepeat(() => In('0', '9'))); + } - return PlusRepeat(()=> In('0','9') ); - } - public bool FractionalPart() /*FractionalPart: '.' [0-9]*;*/ + /// + /// StringConstant: '"' ('""'/StringCharacter)* '"'; + /// + /// + public bool StringConstant() { + return And(() => Char('"') && OptRepeat(() => Char('"', '"') || StringCharacter()) && Char('"')); + } - return And(()=> Char('.') && OptRepeat(()=> In('0','9') ) ); - } - public bool ExponentPart() /*ExponentPart: 'E' ('+' / '-')? [0-9]*;*/ + /// + /// StringCharacter: [#-~] / '!' / ' ' / HighCharacter; + /// + /// + public bool StringCharacter() { + return In('#', '~') || Char('!') || Char(' ') || HighCharacter(); + } + + /// + /// *HighCharacter: [#x80-#xFFFF]; + /// + /// + public bool HighCharacter() + { + return In('\u0080', '\uffff'); + } + + /// + /// ^^ArrayConstant: '{' ConstantListRows '}'; + /// + /// + public bool ArrayConstant() + { + return TreeNT((int)EExcelFormula.ArrayConstant, () => And(() => Char('{') && ConstantListRows() && Char('}'))); + } + + /// + /// ConstantListRows: ConstantListRow (';' ConstantListRow)*; + /// + /// + public bool ConstantListRows() + { + return And(() => ConstantListRow() && OptRepeat(() => And(() => Char(';') && ConstantListRow()))); + } + + /// + /// ^^ConstantListRow: Constant (',' Constant)*; + /// + /// + public bool ConstantListRow() + { + return TreeNT((int)EExcelFormula.ConstantListRow, () => And(() => Constant() && OptRepeat(() => And(() => Char(',') && Constant())))); + } + + /// + /// InfixOperator: RefInfixOperator / ValueInfixOperator; + /// + /// + public bool InfixOperator() + { + return RefInfixOperator() || ValueInfixOperator(); + } + + /// + /// ^^ValueInfixOperator: '<>' / '>=' / '<=' / '^' / '*' / '/' / '+' / '-' / '&' / '=' / '<' / '>'; + /// + /// + public bool ValueInfixOperator() + { + return TreeNT((int)EExcelFormula.ValueInfixOperator, () => OneOfLiterals(optimizedLiterals0)); + } + + /// + /// RefInfixOperator: RangeOperator / UnionOperator / IntersectionOperator; + /// + /// + public bool RefInfixOperator() + { + return RangeOperator() || UnionOperator() || IntersectionOperator(); + } + + /// + /// ^^UnionOperator: ','; + /// + /// + public bool UnionOperator() + { + return TreeNT((int)EExcelFormula.UnionOperator, () => Char(',')); + } + + /// + /// ^^IntersectionOperator: ' '; + /// + /// + public bool IntersectionOperator() + { + return TreeNT((int)EExcelFormula.IntersectionOperator, () => Char(' ')); + } - return And(()=> - Char('E') - && Option(()=> Char('+') || Char('-') ) - && OptRepeat(()=> In('0','9') ) ); - } - public bool StringConstant() /*StringConstant: '"' ('""'/StringCharacter)* '"';*/ + /// + /// ^^RangeOperator: ':'; + /// + /// + public bool RangeOperator() { + return TreeNT((int)EExcelFormula.RangeOperator, () => Char(':')); + } - return And(()=> - Char('"') - && OptRepeat(()=> Char('"','"') || StringCharacter() ) - && Char('"') ); - } - public bool StringCharacter() /*StringCharacter: [#-~] / '!' / ' ' / HighCharacter;*/ + /// + /// ^^PostfixOperator: '%'; + /// + /// + public bool PostfixOperator() { + return TreeNT((int)EExcelFormula.PostfixOperator, () => Char('%')); + } - return - In('#','~') - || Char('!') - || Char(' ') - || HighCharacter(); - } - public bool HighCharacter() /*HighCharacter: [#x80-#xFFFF];*/ + /// + /// ^^PrefixOperator: '+' / '-'; + /// + /// + public bool PrefixOperator() { + return TreeNT((int)EExcelFormula.PrefixOperator, () => Char('+') || Char('-')); + } - return In('\u0080','\uffff'); - } - public bool ArrayConstant() /*^^ArrayConstant: '{' ConstantListRows '}';*/ + /// + /// CellReference: ExternalCellReference / LocalCellReference; + /// + /// + public bool CellReference() { + return ExternalCellReference() || LocalCellReference(); + } - return TreeNT((int)EExcelFormula.ArrayConstant,()=> - And(()=> Char('{') && ConstantListRows() && Char('}') ) ); - } - public bool ConstantListRows() /*ConstantListRows: ConstantListRow (';' ConstantListRow)*;*/ + /// + /// LocalCellReference: A1Reference; + /// + /// + public bool LocalCellReference() { + return A1Reference(); + } - return And(()=> - ConstantListRow() - && OptRepeat(()=> - And(()=> Char(';') && ConstantListRow() ) ) ); - } - public bool ConstantListRow() /*^^ConstantListRow: Constant (',' Constant)*;*/ + /// + /// ExternalCellReference: BangReference / SheetRangeReference / SingleSheetReference; + /// + /// + public bool ExternalCellReference() { + return BangReference() || SheetRangeReference() || SingleSheetReference(); + } - return TreeNT((int)EExcelFormula.ConstantListRow,()=> - And(()=> - Constant() - && OptRepeat(()=> And(()=> Char(',') && Constant() ) ) ) ); - } - public bool InfixOperator() /*InfixOperator: RefInfixOperator / ValueInfixOperator;*/ + /// + /// BookPrefix: WorkbookIndex '!'; + /// + /// + public bool BookPrefix() { + return And(() => WorkbookIndex() && Char('!')); + } - return RefInfixOperator() || ValueInfixOperator(); - } - public bool ValueInfixOperator() /*^^ValueInfixOperator: '<>' / '>=' / '<=' / '^' / '*' / '/' / '+' / '-' / '&' / '=' / '<' / '>';*/ + /// + /// BangReference: '!' (A1Reference / '#REF!'); + /// + /// + public bool BangReference() { + return And(() => Char('!') && (A1Reference() || Char('#', 'R', 'E', 'F', '!'))); + } - return TreeNT((int)EExcelFormula.ValueInfixOperator,()=> - OneOfLiterals(optimizedLiterals0) ); - } - public bool RefInfixOperator() /*RefInfixOperator: RangeOperator / UnionOperator / IntersectionOperator;*/ + /// + /// SheetRangeReference: SheetRange '!' A1Reference; + /// + /// + public bool SheetRangeReference() { + return And(() => SheetRange() && Char('!') && A1Reference()); + } - return - RangeOperator() - || UnionOperator() - || IntersectionOperator(); - } - public bool UnionOperator() /*^^UnionOperator: ',';*/ + /// + /// SingleSheetPrefix: SingleSheet '!'; + /// + /// + public bool SingleSheetPrefix() { + return And(() => SingleSheet() && Char('!')); + } - return TreeNT((int)EExcelFormula.UnionOperator,()=> - Char(',') ); - } - public bool IntersectionOperator() /*^^IntersectionOperator: ' ';*/ + /// + /// SingleSheetReference: SingleSheetPrefix (A1Reference / '#REF!'); + /// + /// + public bool SingleSheetReference() { + return And(() => SingleSheetPrefix() && (A1Reference() || Char('#', 'R', 'E', 'F', '!'))); + } - return TreeNT((int)EExcelFormula.IntersectionOperator,()=> - Char(' ') ); - } - public bool RangeOperator() /*^^RangeOperator: ':';*/ + /// + /// SingleSheetArea: SingleSheetPrefix A1Area; + /// + /// + public bool SingleSheetArea() { + return And(() => SingleSheetPrefix() && A1Area()); + } - return TreeNT((int)EExcelFormula.RangeOperator,()=> - Char(':') ); - } - public bool PostfixOperator() /*^^PostfixOperator: '%';*/ + /// + /// SingleSheet: WorkbookIndex? SheetName / '\'' WorkbookIndex? SheetNameSpecial '\''; + /// + /// + public bool SingleSheet() { + return + And(() => Option(() => WorkbookIndex()) && SheetName()) || And(() => Char('\'') && Option(() => WorkbookIndex()) && SheetNameSpecial() && Char('\'')); + } - return TreeNT((int)EExcelFormula.PostfixOperator,()=> - Char('%') ); - } - public bool PrefixOperator() /*^^PrefixOperator: '+' / '-';*/ + /// + /// SheetRange: WorkbookIndex? SheetName ':' SheetName / '\'' WorkbookIndex? SheetNameSpecial ':' SheetNameSpecial '\''; + /// + /// + public bool SheetRange() { + return And(() => Option(() => WorkbookIndex()) && SheetName() && Char(':') && SheetName()) || And(() => Char('\'') && Option(() => WorkbookIndex()) && SheetNameSpecial() && Char(':') && SheetNameSpecial() && Char('\'')); + } - return TreeNT((int)EExcelFormula.PrefixOperator,()=> - Char('+') || Char('-') ); - } - public bool CellReference() /*CellReference: ExternalCellReference / LocalCellReference;*/ + /// + /// ^^WorkbookIndex: '[' WholeNumberPart ']'; + /// + /// + public bool WorkbookIndex() { + return TreeNT((int)EExcelFormula.WorkbookIndex, () => And(() => Char('[') && WholeNumberPart() && Char(']'))); + } - return ExternalCellReference() || LocalCellReference(); - } - public bool LocalCellReference() /*LocalCellReference: A1Reference;*/ + /// + /// ^^SheetName: SheetNameCharacter+; + /// + /// + public bool SheetName() { + return TreeNT((int)EExcelFormula.SheetName, () => PlusRepeat(() => SheetNameCharacter())); + } - return A1Reference(); - } - public bool ExternalCellReference() /*ExternalCellReference: BangReference / SheetRangeReference / SingleSheetReference;*/ + /// + /// SheetNameCharacter: [A-Za-z0-9._] / HighCharacter; + /// + /// + public bool SheetNameCharacter() { + return In('A', 'Z', 'a', 'z', '0', '9') || OneOf("._") || HighCharacter(); + } - return - BangReference() - || SheetRangeReference() - || SingleSheetReference(); - } - public bool BookPrefix() /*BookPrefix: WorkbookIndex '!';*/ + /// + /// ^^SheetNameSpecial: SheetNameBaseCharacter ('\'\''* SheetNameBaseCharacter)*; + /// + /// + public bool SheetNameSpecial() { + return TreeNT((int)EExcelFormula.SheetNameSpecial, () => And(() => SheetNameBaseCharacter() && OptRepeat(() => And(() => OptRepeat(() => Char('\'', '\'')) && SheetNameBaseCharacter())))); + } - return And(()=> WorkbookIndex() && Char('!') ); - } - public bool BangReference() /*BangReference: '!' (A1Reference / '#REF!');*/ + /// + /// SheetNameBaseCharacter: [A-Za-z0-9!"#$%&()+,-.;<=>@^_`{|}~ ] / HighCharacter; + /// + /// + public bool SheetNameBaseCharacter() { + return OneOf(optimizedCharset0) || HighCharacter(); + } - return And(()=> - Char('!') - && ( A1Reference() || Char('#','R','E','F','!')) ); - } - public bool SheetRangeReference() /*SheetRangeReference: SheetRange '!' A1Reference;*/ + /// + /// ^^A1Reference: (A1Column ':' A1Column) / (A1Row ':' A1Row) / A1Area / A1Cell; + /// + /// + public bool A1Reference() { + return TreeNT((int)EExcelFormula.A1Reference, () => And(() => A1Column() && Char(':') && A1Column()) || And(() => A1Row() && Char(':') && A1Row()) || A1Area() || A1Cell()); + } - return And(()=> SheetRange() && Char('!') && A1Reference() ); - } - public bool SingleSheetPrefix() /*SingleSheetPrefix: SingleSheet '!';*/ + /// + /// A1Cell: A1Column A1Row !NameCharacter; + /// + /// + public bool A1Cell() { + return And(() => A1Column() && A1Row() && Not(() => NameCharacter())); + } - return And(()=> SingleSheet() && Char('!') ); - } - public bool SingleSheetReference() /*SingleSheetReference: SingleSheetPrefix (A1Reference / '#REF!');*/ + /// + /// A1Area: A1Cell ':' A1Cell; + /// + /// + public bool A1Area() { + return And(() => A1Cell() && Char(':') && A1Cell()); + } - return And(()=> - SingleSheetPrefix() - && ( A1Reference() || Char('#','R','E','F','!')) ); - } - public bool SingleSheetArea() /*SingleSheetArea: SingleSheetPrefix A1Area;*/ + /// + /// ^^A1Column: A1AbsoluteColumn / A1RelativeColumn; + /// + /// + public bool A1Column() { + return TreeNT((int)EExcelFormula.A1Column, () => A1AbsoluteColumn() || A1RelativeColumn()); + } - return And(()=> SingleSheetPrefix() && A1Area() ); - } - public bool SingleSheet() /*SingleSheet: WorkbookIndex? SheetName / '\'' WorkbookIndex? SheetNameSpecial '\'';*/ + /// + /// A1AbsoluteColumn: '$' A1RelativeColumn; + /// + /// + public bool A1AbsoluteColumn() { + return And(() => Char('$') && A1RelativeColumn()); + } - return - And(()=> - Option(()=> WorkbookIndex() ) - && SheetName() ) - || And(()=> - Char('\'') - && Option(()=> WorkbookIndex() ) - && SheetNameSpecial() - && Char('\'') ); - } - public bool SheetRange() /*SheetRange: WorkbookIndex? SheetName ':' SheetName / '\'' WorkbookIndex? SheetNameSpecial ':' SheetNameSpecial '\'';*/ + /// + /// A1RelativeColumn: 'XF' [A-D] / 'X' [A-E] [A-Z] / [A-W][A-Z][A-Z] / [A-Z][A-Z] / [A-Z]; + /// + /// + public bool A1RelativeColumn() { + return + And(() => Char('X', 'F') && In('A', 'D')) || And(() => Char('X') && In('A', 'E') && In('A', 'Z')) || And(() => In('A', 'W') && In('A', 'Z') && In('A', 'Z')) || And(() => In('A', 'Z') && In('A', 'Z')) || In('A', 'Z'); + } - return - And(()=> - Option(()=> WorkbookIndex() ) - && SheetName() - && Char(':') - && SheetName() ) - || And(()=> - Char('\'') - && Option(()=> WorkbookIndex() ) - && SheetNameSpecial() - && Char(':') - && SheetNameSpecial() - && Char('\'') ); - } - public bool WorkbookIndex() /*^^WorkbookIndex: '[' WholeNumberPart ']';*/ + /// + /// ^^A1Row: A1AbsoluteRow / A1RelativeRow; + /// + /// + public bool A1Row() { + return TreeNT((int)EExcelFormula.A1Row, () => A1AbsoluteRow() || A1RelativeRow()); + } - return TreeNT((int)EExcelFormula.WorkbookIndex,()=> - And(()=> Char('[') && WholeNumberPart() && Char(']') ) ); - } - public bool SheetName() /*^^SheetName: SheetNameCharacter+;*/ + /// + /// A1AbsoluteRow: '$' A1RelativeRow; + /// + /// + public bool A1AbsoluteRow() { + return And(() => Char('$') && A1RelativeRow()); + } - return TreeNT((int)EExcelFormula.SheetName,()=> - PlusRepeat(()=> SheetNameCharacter() ) ); - } - public bool SheetNameCharacter() /*SheetNameCharacter: [A-Za-z0-9._] / HighCharacter;*/ + /// + /// A1RelativeRow: [1-9][0-9]*; + /// + /// + public bool A1RelativeRow() { + return And(() => In('1', '9') && OptRepeat(() => In('0', '9'))); + } - return - (In('A','Z', 'a','z', '0','9')||OneOf("._")) - || HighCharacter(); - } - public bool SheetNameSpecial() /*^^SheetNameSpecial: SheetNameBaseCharacter ('\'\''* SheetNameBaseCharacter)*;*/ + /// + /// ^^CellFunctionCall: A1Cell '(' ArgumentList ')'; + /// + /// + public bool CellFunctionCall() { + return TreeNT((int)EExcelFormula.CellFunctionCall, () => And(() => A1Cell() && Char('(') && ArgumentList() && Char(')'))); + } - return TreeNT((int)EExcelFormula.SheetNameSpecial,()=> - And(()=> - SheetNameBaseCharacter() - && OptRepeat(()=> - And(()=> - OptRepeat(()=> Char('\'','\'') ) - && SheetNameBaseCharacter() ) ) ) ); - } - public bool SheetNameBaseCharacter() /*SheetNameBaseCharacter: [A-Za-z0-9!"#$%&()+,-.;<=>@^_`{|}~ ] / HighCharacter;*/ + /// + /// ^^UserDefinedFunctionCall: UserDefinedFunctionName '(' ArgumentList ')'; + /// + /// + public bool UserDefinedFunctionCall() { + return TreeNT((int)EExcelFormula.UserDefinedFunctionCall, () => And(() => UserDefinedFunctionName() && Char('(') && ArgumentList() && Char(')'))); + } - return OneOf(optimizedCharset0) || HighCharacter(); - } - public bool A1Reference() /*^^A1Reference: (A1Column ':' A1Column) / (A1Row ':' A1Row) / A1Area / A1Cell;*/ + /// + /// UserDefinedFunctionName: NameReference; + /// + /// + public bool UserDefinedFunctionName() { + return NameReference(); + } - return TreeNT((int)EExcelFormula.A1Reference,()=> - - And(()=> A1Column() && Char(':') && A1Column() ) - || And(()=> A1Row() && Char(':') && A1Row() ) - || A1Area() - || A1Cell() ); - } - public bool A1Cell() /*A1Cell: A1Column A1Row !NameCharacter;*/ + /// + /// ArgumentList: Argument (',' Argument)*; + /// + /// + public bool ArgumentList() { + return And(() => Argument() && OptRepeat(() => And(() => Char(',') && Argument()))); + } - return And(()=> - A1Column() - && A1Row() - && Not(()=> NameCharacter() ) ); - } - public bool A1Area() /*A1Area: A1Cell ':' A1Cell;*/ + /// + /// Argument: ArgumentExpression / ws; + /// + /// + public bool Argument() { + return ArgumentExpression() || ws(); + } - return And(()=> A1Cell() && Char(':') && A1Cell() ); - } - public bool A1Column() /*^^A1Column: A1AbsoluteColumn / A1RelativeColumn;*/ + /// + /// ^^ArgumentExpression: ws ArgumentInfixTerms; + /// + /// + public bool ArgumentExpression() { + return TreeNT((int)EExcelFormula.ArgumentExpression, () => And(() => ws() && ArgumentInfixTerms())); + } - return TreeNT((int)EExcelFormula.A1Column,()=> - A1AbsoluteColumn() || A1RelativeColumn() ); - } - public bool A1AbsoluteColumn() /*A1AbsoluteColumn: '$' A1RelativeColumn;*/ + /// + /// ArgumentInfixTerms: ArgumentPreAndPostTerm (ArgumentInfixOperator ws ArgumentPreAndPostTerm)*; + /// + /// + public bool ArgumentInfixTerms() { + return And(() => ArgumentPreAndPostTerm() && OptRepeat(() => And(() => ArgumentInfixOperator() && ws() && ArgumentPreAndPostTerm()))); + } - return And(()=> Char('$') && A1RelativeColumn() ); - } - public bool A1RelativeColumn() /*A1RelativeColumn: 'XF' [A-D] / 'X' [A-E] [A-Z] / [A-W][A-Z][A-Z] / [A-Z][A-Z] / [A-Z];*/ + /// + /// ArgumentPreAndPostTerm: (PrefixOperator ws)* ArgumentTerm (PostfixOperator ws)*; + /// + /// + public bool ArgumentPreAndPostTerm() { + return And(() => OptRepeat(() => And(() => PrefixOperator() && ws())) && ArgumentTerm() && OptRepeat(() => And(() => PostfixOperator() && ws()))); + } - return - And(()=> Char('X','F') && In('A','D') ) - || And(()=> Char('X') && In('A','E') && In('A','Z') ) - || And(()=> In('A','W') && In('A','Z') && In('A','Z') ) - || And(()=> In('A','Z') && In('A','Z') ) - || In('A','Z'); - } - public bool A1Row() /*^^A1Row: A1AbsoluteRow / A1RelativeRow;*/ + /// + /// ArgumentTerm: (ArgumentRefInfixTerms / '(' Expression ')' / Constant) ws; + /// + /// + public bool ArgumentTerm() { + return And(() => (ArgumentRefInfixTerms() || And(() => Char('(') && Expression() && Char(')')) || Constant()) && ws()); + } - return TreeNT((int)EExcelFormula.A1Row,()=> - A1AbsoluteRow() || A1RelativeRow() ); - } - public bool A1AbsoluteRow() /*A1AbsoluteRow: '$' A1RelativeRow;*/ + /// + /// ArgumentRefInfixTerms: ArgumentRefTerm (RefArgumentInfixOperator ws ArgumentRefTerm)*; + /// + /// + public bool ArgumentRefInfixTerms() { + return And(() => ArgumentRefTerm() && OptRepeat(() => And(() => RefArgumentInfixOperator() && ws() && ArgumentRefTerm()))); + } - return And(()=> Char('$') && A1RelativeRow() ); - } - public bool A1RelativeRow() /*A1RelativeRow: [1-9][0-9]*;*/ + /// + /// ArgumentRefTerm: '(' ws RefInfixTerms ')' / RefConstant / CellFunctionCall / CellReference / UserDefinedFunctionCall / NameReference / StructureReference; + /// + /// + public bool ArgumentRefTerm() { + return And(() => Char('(') && ws() && RefInfixTerms() && Char(')')) || RefConstant() || CellFunctionCall() || CellReference() || UserDefinedFunctionCall() || NameReference() || StructureReference(); + } - return And(()=> In('1','9') && OptRepeat(()=> In('0','9') ) ); - } - public bool CellFunctionCall() /*^^CellFunctionCall: A1Cell '(' ArgumentList ')';*/ + /// + /// ArgumentInfixOperator: RefArgumentInfixOperator / ValueInfixOperator; + /// + /// + public bool ArgumentInfixOperator() { + return RefArgumentInfixOperator() || ValueInfixOperator(); + } - return TreeNT((int)EExcelFormula.CellFunctionCall,()=> - And(()=> - A1Cell() - && Char('(') - && ArgumentList() - && Char(')') ) ); - } - public bool UserDefinedFunctionCall() /*^^UserDefinedFunctionCall: UserDefinedFunctionName '(' ArgumentList ')';*/ + /// + /// RefArgumentInfixOperator: RangeOperator / IntersectionOperator; + /// + /// + public bool RefArgumentInfixOperator() { + return RangeOperator() || IntersectionOperator(); + } - return TreeNT((int)EExcelFormula.UserDefinedFunctionCall,()=> - And(()=> - UserDefinedFunctionName() - && Char('(') - && ArgumentList() - && Char(')') ) ); - } - public bool UserDefinedFunctionName() /*UserDefinedFunctionName: NameReference;*/ + /// + /// ^^NameReference: (ExternalName / Name) !'['; + /// + /// + public bool NameReference() { + return TreeNT((int)EExcelFormula.NameReference, () => And(() => (ExternalName() || Name()) && Not(() => Char('[')))); + } - return NameReference(); - } - public bool ArgumentList() /*ArgumentList: Argument (',' Argument)*;*/ + /// + /// ExternalName: BangName / (SingleSheetPrefix / BookPrefix) Name; + /// + /// + public bool ExternalName() { + return BangName() || And(() => (SingleSheetPrefix() || BookPrefix()) && Name()); + } - return And(()=> - Argument() - && OptRepeat(()=> And(()=> Char(',') && Argument() ) ) ); - } - public bool Argument() /*Argument: ArgumentExpression / ws;*/ + /// + /// BangName: '!' Name; + /// + /// + public bool BangName() { + return And(() => Char('!') && Name()); + } - return ArgumentExpression() || ws(); - } - public bool ArgumentExpression() /*^^ArgumentExpression: ws ArgumentInfixTerms;*/ + /// + /// Name: NameStartCharacter NameCharacter*; + /// + /// + public bool Name() { + return And(() => NameStartCharacter() && OptRepeat(() => NameCharacter())); + } - return TreeNT((int)EExcelFormula.ArgumentExpression,()=> - And(()=> ws() && ArgumentInfixTerms() ) ); - } - public bool ArgumentInfixTerms() /*ArgumentInfixTerms: ArgumentPreAndPostTerm (ArgumentInfixOperator ws ArgumentPreAndPostTerm)*;*/ + /// + /// NameStartCharacter: [_\\A-Za-z] / HighCharacter; + /// + /// + public bool NameStartCharacter() { + return In('A', 'Z', 'a', 'z') || OneOf("_\\") || HighCharacter(); + } - return And(()=> - ArgumentPreAndPostTerm() - && OptRepeat(()=> - And(()=> - ArgumentInfixOperator() - && ws() - && ArgumentPreAndPostTerm() ) ) ); - } - public bool ArgumentPreAndPostTerm() /*ArgumentPreAndPostTerm: (PrefixOperator ws)* ArgumentTerm (PostfixOperator ws)*;*/ + /// + /// NameCharacter: NameStartCharacter / [0-9] / '.' / '?' / HighCharacter; + /// + /// + public bool NameCharacter() { + return NameStartCharacter() || In('0', '9') || Char('.') || Char('?') || HighCharacter(); + } - return And(()=> - OptRepeat(()=> And(()=> PrefixOperator() && ws() ) ) - && ArgumentTerm() - && OptRepeat(()=> And(()=> PostfixOperator() && ws() ) ) ); - } - public bool ArgumentTerm() /*ArgumentTerm: (ArgumentRefInfixTerms / '(' Expression ')' / Constant) ws;*/ + /// + /// ^^StructureReference: TableIdentifier? IntraTableReference; + /// + /// + public bool StructureReference() { + return TreeNT((int)EExcelFormula.StructureReference, () => And(() => Option(() => TableIdentifier()) && IntraTableReference())); + } - return And(()=> - ( - ArgumentRefInfixTerms() - || And(()=> Char('(') && Expression() && Char(')') ) - || Constant()) - && ws() ); - } - public bool ArgumentRefInfixTerms() /*ArgumentRefInfixTerms: ArgumentRefTerm (RefArgumentInfixOperator ws ArgumentRefTerm)*;*/ + /// + /// TableIdentifier: BookPrefix? TableName; + /// + /// + public bool TableIdentifier() { + return And(() => Option(() => BookPrefix()) && TableName()); + } - return And(()=> - ArgumentRefTerm() - && OptRepeat(()=> - And(()=> - RefArgumentInfixOperator() - && ws() - && ArgumentRefTerm() ) ) ); - } - public bool ArgumentRefTerm() /*ArgumentRefTerm: '(' ws RefInfixTerms ')' / RefConstant / CellFunctionCall / CellReference / UserDefinedFunctionCall - / NameReference / StructureReference;*/ + /// + /// TableName: Name; + /// + /// + public bool TableName() { + return Name(); + } - return - And(()=> - Char('(') - && ws() - && RefInfixTerms() - && Char(')') ) - || RefConstant() - || CellFunctionCall() - || CellReference() - || UserDefinedFunctionCall() - || NameReference() - || StructureReference(); - } - public bool ArgumentInfixOperator() /*ArgumentInfixOperator: RefArgumentInfixOperator / ValueInfixOperator;*/ + /// + /// IntraTableReference: SpacedLBracket InnerReference SpacedRBracket / Keyword / '[' SimpleColumnName ']'; + /// + /// + public bool IntraTableReference() { - - return RefArgumentInfixOperator() || ValueInfixOperator(); - } - public bool RefArgumentInfixOperator() /*RefArgumentInfixOperator: RangeOperator / IntersectionOperator;*/ - { - - return RangeOperator() || IntersectionOperator(); - } - public bool NameReference() /*^^NameReference: (ExternalName / Name) !'[';*/ + return And(() => SpacedLBracket() && InnerReference() && SpacedRBracket()) || Keyword() || And(() => Char('[') && SimpleColumnName() && Char(']')); + } + + /// + /// InnerReference: (KeywordList SpacedComma)? ColumnRange / KeywordList; + /// + /// + public bool InnerReference() + { + return + And(() => + Option(() => + And(() => KeywordList() && SpacedComma())) + && ColumnRange()) + || KeywordList(); + } + + /// + /// Keyword: '[#All]' / '[#Data]' / '[#Headers]' / '[#Totals]' / '[#This Row]'; + /// + /// + public bool Keyword() { - - return TreeNT((int)EExcelFormula.NameReference,()=> - And(()=> - ( ExternalName() || Name()) - && Not(()=> Char('[') ) ) ); - } - public bool ExternalName() /*ExternalName: BangName / (SingleSheetPrefix / BookPrefix) Name;*/ - { - - return - BangName() - || And(()=> - ( SingleSheetPrefix() || BookPrefix()) - && Name() ); - } - public bool BangName() /*BangName: '!' Name;*/ - { - - return And(()=> Char('!') && Name() ); - } - public bool Name() /*Name: NameStartCharacter NameCharacter*;*/ - { - - return And(()=> - NameStartCharacter() - && OptRepeat(()=> NameCharacter() ) ); - } - public bool NameStartCharacter() /*NameStartCharacter: [_\\A-Za-z] / HighCharacter;*/ - { - - return - (In('A','Z', 'a','z')||OneOf("_\\")) - || HighCharacter(); - } - public bool NameCharacter() /*NameCharacter: NameStartCharacter / [0-9] / '.' / '?' / HighCharacter;*/ - { - - return - NameStartCharacter() - || In('0','9') - || Char('.') - || Char('?') - || HighCharacter(); - } - public bool StructureReference() /*^^StructureReference: TableIdentifier? IntraTableReference;*/ - { - - return TreeNT((int)EExcelFormula.StructureReference,()=> - And(()=> - Option(()=> TableIdentifier() ) - && IntraTableReference() ) ); - } - public bool TableIdentifier() /*TableIdentifier: BookPrefix? TableName;*/ - { - - return And(()=> Option(()=> BookPrefix() ) && TableName() ); - } - public bool TableName() /*TableName: Name;*/ - { - - return Name(); - } - public bool IntraTableReference() /*IntraTableReference: SpacedLBracket InnerReference SpacedRBracket / Keyword / '[' SimpleColumnName ']';*/ - { - - return - And(()=> - SpacedLBracket() - && InnerReference() - && SpacedRBracket() ) - || Keyword() - || And(()=> - Char('[') - && SimpleColumnName() - && Char(']') ); - } - public bool InnerReference() /*InnerReference: (KeywordList SpacedComma)? ColumnRange / KeywordList;*/ + return Char('[', '#', 'A', 'l', 'l', ']') || Char('[', '#', 'D', 'a', 't', 'a', ']') || Char("[#Headers]") || Char("[#Totals]") || Char("[#This Row]"); + } + + /// + /// KeywordList: '[#Headers]' SpacedComma '[#Data]' / '[#Data]' SpacedComma '[#Totals]' / Keyword; + /// + /// + public bool KeywordList() { + return And(() => Char("[#Headers]") && SpacedComma() && Char('[', '#', 'D', 'a', 't', 'a', ']')) || And(() => Char('[', '#', 'D', 'a', 't', 'a', ']') && SpacedComma() && Char("[#Totals]")) || Keyword(); + } - return - And(()=> - Option(()=> - And(()=> KeywordList() && SpacedComma() ) ) - && ColumnRange() ) - || KeywordList(); - } - public bool Keyword() /*Keyword: '[#All]' / '[#Data]' / '[#Headers]' / '[#Totals]' / '[#This Row]';*/ + /// + /// ColumnRange: Column (':' Column)?; + /// + /// + public bool ColumnRange() { + return And(() => Column() && Option(() => And(() => Char(':') && Column()))); + } - return - Char('[','#','A','l','l',']') - || Char('[','#','D','a','t','a',']') - || Char("[#Headers]") - || Char("[#Totals]") - || Char("[#This Row]"); - } - public bool KeywordList() /*KeywordList: '[#Headers]' SpacedComma '[#Data]' / '[#Data]' SpacedComma '[#Totals]' / Keyword;*/ + /// + /// Column: '[' ws SimpleColumnName ws ']' / SimpleColumnName; + /// + /// + public bool Column() { + return And(() => Char('[') && ws() && SimpleColumnName() && ws() && Char(']')) || SimpleColumnName(); + } - return - And(()=> - Char("[#Headers]") - && SpacedComma() - && Char('[','#','D','a','t','a',']') ) - || And(()=> - Char('[','#','D','a','t','a',']') - && SpacedComma() - && Char("[#Totals]") ) - || Keyword(); - } - public bool ColumnRange() /*ColumnRange: Column (':' Column)?;*/ + /// + /// SimpleColumnName: AnyNoSpaceColumnCharacter+ (ws AnyNoSpaceColumnCharacter+)*; + /// + /// + public bool SimpleColumnName() { + return And(() => PlusRepeat(() => AnyNoSpaceColumnCharacter()) && OptRepeat(() => And(() => ws() && PlusRepeat(() => AnyNoSpaceColumnCharacter())))); + } - return And(()=> - Column() - && Option(()=> And(()=> Char(':') && Column() ) ) ); - } - public bool Column() /*Column: '[' ws SimpleColumnName ws ']' / SimpleColumnName;*/ + /// + /// EscapeColumnCharacter: '\'' / '#' / '[' / ']'; + /// + /// + public bool EscapeColumnCharacter() { + return Char('\'') || Char('#') || Char('[') || Char(']'); + } - return - And(()=> - Char('[') - && ws() - && SimpleColumnName() - && ws() - && Char(']') ) - || SimpleColumnName(); - } - public bool SimpleColumnName() /*SimpleColumnName: AnyNoSpaceColumnCharacter+ (ws AnyNoSpaceColumnCharacter+)*;*/ - { - - return And(()=> - PlusRepeat(()=> AnyNoSpaceColumnCharacter() ) - && OptRepeat(()=> - And(()=> - ws() - && PlusRepeat(()=> AnyNoSpaceColumnCharacter() ) ) ) ); - } - public bool EscapeColumnCharacter() /*EscapeColumnCharacter: '\'' / '#' / '[' / ']';*/ - { - - return Char('\'') || Char('#') || Char('[') || Char(']'); - } - public bool UnescapedColumnCharacter() /*UnescapedColumnCharacter: [A-Za-z0-9!"#$%&()*+,-./:;<=>?@\\^_`{|}~] / HighCharacter;*/ + /// + /// UnescapedColumnCharacter: [A-Za-z0-9!"#$%&()*+,-./:;<=>?@\\^_`{|}~] / HighCharacter; + /// + /// + public bool UnescapedColumnCharacter() { + return OneOf(optimizedCharset1) || HighCharacter(); + } - return OneOf(optimizedCharset1) || HighCharacter(); - } - public bool AnyNoSpaceColumnCharacter() /*AnyNoSpaceColumnCharacter: ('\'' EscapeColumnCharacter) / UnescapedColumnCharacter;*/ + /// + /// AnyNoSpaceColumnCharacter: ('\'' EscapeColumnCharacter) / UnescapedColumnCharacter; + /// + /// + public bool AnyNoSpaceColumnCharacter() { + return And(() => Char('\'') && EscapeColumnCharacter()) || UnescapedColumnCharacter(); + } - return - And(()=> Char('\'') && EscapeColumnCharacter() ) - || UnescapedColumnCharacter(); - } - public bool SpacedComma() /*SpacedComma: ' '? ',' ' '?;*/ - { + /// + /// SpacedComma: ' '? ',' ' '?; + /// + /// + public bool SpacedComma() + { + return And(() => Option(() => Char(' ')) && Char(',') && Option(() => Char(' '))); + } - return And(()=> - Option(()=> Char(' ') ) - && Char(',') - && Option(()=> Char(' ') ) ); - } - public bool SpacedLBracket() /*SpacedLBracket: '[' ' '?;*/ - { - - return And(()=> Char('[') && Option(()=> Char(' ') ) ); - } - public bool SpacedRBracket() /*SpacedRBracket: ' '? ']';*/ - { - - return And(()=> Option(()=> Char(' ') ) && Char(']') ); - } - public bool ws() /*ws: ' '*;*/ + /// + /// SpacedLBracket: '[' ' '?; + /// + /// + public bool SpacedLBracket() { + return And(() => Char('[') && Option(() => Char(' '))); + } - return OptRepeat(()=> Char(' ') ); - } - #endregion Grammar Rules - - #region Optimization Data - internal static OptimizedCharset optimizedCharset0; - internal static OptimizedCharset optimizedCharset1; - - internal static OptimizedLiterals optimizedLiterals0; - - static ExcelFormula() + /// + /// SpacedRBracket: ' '? ']'; + /// + /// + public bool SpacedRBracket() { - { - OptimizedCharset.Range[] ranges = new OptimizedCharset.Range[] - {new OptimizedCharset.Range('A','Z'), - new OptimizedCharset.Range('a','z'), - new OptimizedCharset.Range('0','9'), - new OptimizedCharset.Range(',','.'), - }; - char[] oneOfChars = new char[] {'!','"','#','$','%' - ,'&','(',')','+',';' - ,'<','=','>','@','^' - ,'_','`','{','|','}' - ,'~',' '}; - optimizedCharset0= new OptimizedCharset(ranges,oneOfChars); - } - - { - OptimizedCharset.Range[] ranges = new OptimizedCharset.Range[] - {new OptimizedCharset.Range('A','Z'), - new OptimizedCharset.Range('a','z'), - new OptimizedCharset.Range('0','9'), - new OptimizedCharset.Range(',','.'), - }; - char[] oneOfChars = new char[] {'!','"','#','$','%' - ,'&','(',')','*','+' - ,'/',':',';','<','=' - ,'>','?','@','\\','^' - ,'_','`','{','|','}' - ,'~'}; - optimizedCharset1= new OptimizedCharset(ranges,oneOfChars); - } - - - { - string[] literals= - { "<>",">=","<=","^","*","/","+","-", - "&","=","<",">" }; - optimizedLiterals0= new OptimizedLiterals(literals); - } + return And(() => Option(() => Char(' ')) && Char(']')); + } - + /// + /// ws: ' '*; + /// + /// + public bool ws() + { + return OptRepeat(() => Char(' ')); } - #endregion Optimization Data - } -} + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/Exceptions/DocumentBuilderException.cs b/OpenXmlPowerTools/Exceptions/DocumentBuilderException.cs new file mode 100644 index 00000000..2b0506ad --- /dev/null +++ b/OpenXmlPowerTools/Exceptions/DocumentBuilderException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Codeuctivity.OpenXmlPowerTools.Exceptions +{ + public class DocumentBuilderException : Exception + { + public DocumentBuilderException(string message) : base(message) + { + } + + public DocumentBuilderException(string message, Exception innerException) : base(message, innerException) + { + } + + public DocumentBuilderException() + { + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/Exceptions/DocumentBuilderInternalException.cs b/OpenXmlPowerTools/Exceptions/DocumentBuilderInternalException.cs new file mode 100644 index 00000000..7becb627 --- /dev/null +++ b/OpenXmlPowerTools/Exceptions/DocumentBuilderInternalException.cs @@ -0,0 +1,22 @@ +#define TestForUnsupportedDocuments +#define MergeStylesWithSameNames + +using System; + +namespace Codeuctivity.OpenXmlPowerTools.Exceptions +{ + public class DocumentBuilderInternalException : Exception + { + public DocumentBuilderInternalException(string message) : base(message) + { + } + + public DocumentBuilderInternalException(string message, Exception innerException) : base(message, innerException) + { + } + + public DocumentBuilderInternalException() + { + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/Exceptions/PowerToolsDocumentException.cs b/OpenXmlPowerTools/Exceptions/PowerToolsDocumentException.cs new file mode 100644 index 00000000..8e73ada3 --- /dev/null +++ b/OpenXmlPowerTools/Exceptions/PowerToolsDocumentException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Codeuctivity.OpenXmlPowerTools.Exceptions +{ + public class PowerToolsDocumentException : Exception + { + public PowerToolsDocumentException(string message) : base(message) + { + } + + public PowerToolsDocumentException() + { + } + + public PowerToolsDocumentException(string message, Exception innerException) : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/Exceptions/PowerToolsInvalidDataException.cs b/OpenXmlPowerTools/Exceptions/PowerToolsInvalidDataException.cs new file mode 100644 index 00000000..591c32c0 --- /dev/null +++ b/OpenXmlPowerTools/Exceptions/PowerToolsInvalidDataException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Codeuctivity.OpenXmlPowerTools.Exceptions +{ + public class PowerToolsInvalidDataException : Exception + { + public PowerToolsInvalidDataException(string message) : base(message) + { + } + + public PowerToolsInvalidDataException() + { + } + + public PowerToolsInvalidDataException(string message, Exception innerException) : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/FieldRetriever.cs b/OpenXmlPowerTools/FieldRetriever.cs index c45fed7c..ab1432ef 100644 --- a/OpenXmlPowerTools/FieldRetriever.cs +++ b/OpenXmlPowerTools/FieldRetriever.cs @@ -1,20 +1,14 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; +using DocumentFormat.OpenXml.Packaging; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class FieldRetriever { public static string InstrText(XElement root, int id) { - XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; #if false @@ -32,18 +26,23 @@ public static string InstrText(XElement root, int id) #else var cachedAnnotationInformation = root.Annotation>>(); if (cachedAnnotationInformation == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } // it is possible that a field code contains no instr text if (!cachedAnnotationInformation.ContainsKey(id)) + { return ""; + } + var relevantElements = cachedAnnotationInformation[id]; #endif var groupedSubFields = relevantElements .GroupAdjacent(e => { - Stack s = e.Annotation>(); + var s = e.Annotation>(); var stackElement = s.FirstOrDefault(z => z.Id == id); var elementsBefore = s.TakeWhile(z => z != stackElement); return elementsBefore.Any(); @@ -57,18 +56,21 @@ public static string InstrText(XElement root, int id) { return g.Select(e => { - Stack s = e.Annotation>(); + var s = e.Annotation>(); var stackElement = s.FirstOrDefault(z => z.Id == id); if (stackElement.FieldElementType == FieldElementTypeEnum.InstrText && e.Name == w + "instrText") + { return e.Value; + } + return ""; }) .StringConcatenate(); } else { - Stack s = g.First().Annotation>(); + var s = g.First().Annotation>(); var stackElement = s.FirstOrDefault(z => z.Id == id); var elementBefore = s.TakeWhile(z => z != stackElement).Last(); var subFieldId = elementBefore.Id; @@ -80,12 +82,12 @@ public static string InstrText(XElement root, int id) return "{" + instrText + "}"; } - public static void AnnotateWithFieldInfo(OpenXmlPart part) + public static void AnnotateWithFieldInfo(OpenXmlPart? part) { XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; - XElement root = part.GetXDocument().Root; - var r = root.DescendantsAndSelf() + var root = part?.GetXDocument().Root; + var r = root?.DescendantsAndSelf() .Rollup( new FieldElementTypeStack { @@ -100,9 +102,14 @@ public static void AnnotateWithFieldInfo(OpenXmlPart part) { Stack fis; if (s.FiStack == null) + { fis = new Stack(); + } else + { fis = new Stack(s.FiStack.Reverse()); + } + fis.Push( new FieldElementTypeInfo { @@ -114,11 +121,12 @@ public static void AnnotateWithFieldInfo(OpenXmlPart part) Id = s.Id + 1, FiStack = fis, }; - }; + } + if (e.Attribute(w + "fldCharType").Value == "separate") { - Stack fis = new Stack(s.FiStack.Reverse()); - FieldElementTypeInfo wfi = fis.Pop(); + var fis = new Stack(s.FiStack.Reverse()); + var wfi = fis.Pop(); fis.Push( new FieldElementTypeInfo { @@ -133,8 +141,8 @@ public static void AnnotateWithFieldInfo(OpenXmlPart part) } if (e.Attribute(w + "fldCharType").Value == "end") { - Stack fis = new Stack(s.FiStack.Reverse()); - FieldElementTypeInfo wfi = fis.Pop(); + var fis = new Stack(s.FiStack.Reverse()); + _ = fis.Pop(); return new FieldElementTypeStack { Id = s.Id, @@ -142,13 +150,16 @@ public static void AnnotateWithFieldInfo(OpenXmlPart part) }; } } - if (s.FiStack == null || s.FiStack.Count() == 0) + if (s.FiStack == null || !s.FiStack.Any()) + { return s; - FieldElementTypeInfo wfi3 = s.FiStack.Peek(); + } + + var wfi3 = s.FiStack.Peek(); if (wfi3.FieldElementType == FieldElementTypeEnum.Begin) { - Stack fis = new Stack(s.FiStack.Reverse()); - FieldElementTypeInfo wfi2 = fis.Pop(); + var fis = new Stack(s.FiStack.Reverse()); + var wfi2 = fis.Pop(); fis.Push( new FieldElementTypeInfo { @@ -163,8 +174,8 @@ public static void AnnotateWithFieldInfo(OpenXmlPart part) } if (wfi3.FieldElementType == FieldElementTypeEnum.Separate) { - Stack fis = new Stack(s.FiStack.Reverse()); - FieldElementTypeInfo wfi2 = fis.Pop(); + var fis = new Stack(s.FiStack.Reverse()); + var wfi2 = fis.Pop(); fis.Push( new FieldElementTypeInfo { @@ -179,10 +190,13 @@ public static void AnnotateWithFieldInfo(OpenXmlPart part) } if (wfi3.FieldElementType == FieldElementTypeEnum.End) { - Stack fis = new Stack(s.FiStack.Reverse()); + var fis = new Stack(s.FiStack.Reverse()); fis.Pop(); if (!fis.Any()) + { fis = null; + } + return new FieldElementTypeStack { Id = s.Id, @@ -191,43 +205,35 @@ public static void AnnotateWithFieldInfo(OpenXmlPart part) } return s; }); + + if (root is null) + { + return; + } + var elementPlusInfo = root.DescendantsAndSelf().PtZip(r, (t1, t2) => { return new { Element = t1, - Id = t2.Id, + t2.Id, WmlFieldInfoStack = t2.FiStack, }; }); foreach (var item in elementPlusInfo) { if (item.WmlFieldInfoStack != null) + { item.Element.AddAnnotation(item.WmlFieldInfoStack); + } } - //This code is useful when you want to take a look at the annotations, making sure that they are made correctly. - // - //foreach (var desc in root.DescendantsAndSelf()) - //{ - // Stack s = desc.Annotation>(); - // if (s != null) - // { - // Console.WriteLine(desc.Name.LocalName.PadRight(20)); - // foreach (var item in s) - // { - // Console.WriteLine(" {0:0000} {1}", item.Id, item.FieldElementType.ToString()); - // Console.ReadKey(); - // } - // } - //} - var cachedAnnotationInformation = new Dictionary>(); foreach (var desc in root.DescendantsTrimmed(d => d.Name == W.rPr || d.Name == W.pPr)) { - Stack s = desc.Annotation>(); + var s = desc.Annotation>(); - if (s != null ) + if (s != null) { foreach (var item in s) { @@ -260,13 +266,13 @@ private enum State private static string[] GetTokens(string field) { - State state = State.InWhiteSpace; - int tokenStart = 0; - char quoteStart = char.MinValue; - List tokens = new List(); - for (int c = 0; c < field.Length; c++) + var state = State.InWhiteSpace; + var tokenStart = 0; + var quoteStart = char.MinValue; + var tokens = new List(); + for (var c = 0; c < field.Length; c++) { - if (Char.IsWhiteSpace(field[c])) + if (char.IsWhiteSpace(field[c])) { if (state == State.InToken) { @@ -280,16 +286,16 @@ private static string[] GetTokens(string field) state = State.InQuotedToken; } if (state == State.OnClosingQuote) + { state = State.InWhiteSpace; + } + continue; } - if (field[c] == '\\') + if (field[c] == '\\' && state == State.InQuotedToken) { - if (state == State.InQuotedToken) - { - state = State.OnBackslash; - continue; - } + state = State.OnBackslash; + continue; } if (state == State.OnBackslash) { @@ -350,13 +356,16 @@ private static string[] GetTokens(string field) } } if (state == State.InToken) + { tokens.Add(field.Substring(tokenStart, field.Length - tokenStart)); + } + return tokens.ToArray(); } public static FieldInfo ParseField(string field) { - FieldInfo emptyField = new FieldInfo + var emptyField = new FieldInfo { FieldType = "", Arguments = new string[] { }, @@ -364,21 +373,33 @@ public static FieldInfo ParseField(string field) }; if (field.Length == 0) + { return emptyField; - string fieldType = field.TrimStart().Split(' ').FirstOrDefault(); + } + + var fieldType = field.TrimStart().Split(' ').FirstOrDefault(); if (fieldType == null) + { return emptyField; + } + if (fieldType.ToUpper() != "HYPERLINK" && fieldType.ToUpper() != "REF" && fieldType.ToUpper() != "SEQ" && fieldType.ToUpper() != "STYLEREF" && fieldType.ToUpper() != "LISTNUM" && fieldType.ToUpper() != "PAGE") + { return emptyField; - string[] tokens = GetTokens(field); + } + + var tokens = GetTokens(field); if (tokens.Length == 0) + { return emptyField; - FieldInfo fieldInfo = new FieldInfo() + } + + var fieldInfo = new FieldInfo() { FieldType = tokens[0], Switches = tokens.Where(t => t[0] == '\\').ToArray(), @@ -412,7 +433,7 @@ public class FieldElementTypeInfo public class FieldElementTypeStack { public int Id; - public Stack FiStack; + public Stack? FiStack; } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/FormattingAssembler.cs b/OpenXmlPowerTools/FormattingAssembler.cs index 4eb2b6bb..d84b9c0a 100644 --- a/OpenXmlPowerTools/FormattingAssembler.cs +++ b/OpenXmlPowerTools/FormattingAssembler.cs @@ -1,25 +1,20 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using System.Drawing; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class FormattingAssemblerSettings { - public bool RemoveStyleNamesFromParagraphAndRunProperties; - public bool ClearStyles; - public bool OrderElementsPerStandard; - public bool CreateHtmlConverterAnnotationAttributes; - public bool RestrictToSupportedNumberingFormats; - public bool RestrictToSupportedLanguages; - public ListItemRetrieverSettings ListItemRetrieverSettings; + public bool RemoveStyleNamesFromParagraphAndRunProperties { get; set; } + public bool ClearStyles { get; set; } + public bool OrderElementsPerStandard { get; set; } + public bool CreateHtmlConverterAnnotationAttributes { get; set; } + public bool RestrictToSupportedNumberingFormats { get; set; } + public bool RestrictToSupportedLanguages { get; set; } + public ListItemRetrieverSettings ListItemRetrieverSettings { get; set; } public FormattingAssemblerSettings() { @@ -37,42 +32,49 @@ public static class FormattingAssembler { public static WmlDocument AssembleFormatting(WmlDocument document, FormattingAssemblerSettings settings) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(document)) + using var streamDoc = new OpenXmlMemoryStreamDocument(document); + using (var doc = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument doc = streamDoc.GetWordprocessingDocument()) - { - AssembleFormatting(doc, settings); - } - return streamDoc.GetModifiedWmlDocument(); + AssembleFormatting(doc, settings); } + return streamDoc.GetModifiedWmlDocument(); } public static void AssembleFormatting(WordprocessingDocument wDoc, FormattingAssemblerSettings settings) { - FormattingAssemblerInfo fai = new FormattingAssemblerInfo(); - XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); - XElement defaultParagraphStyle = sXDoc + var fai = new FormattingAssemblerInfo(); + var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var defaultParagraphStyle = sXDoc .Root .Elements(W.style) .FirstOrDefault(st => st.Attribute(W._default).ToBoolean() == true && (string)st.Attribute(W.type) == "paragraph"); if (defaultParagraphStyle != null) + { fai.DefaultParagraphStyleName = (string)defaultParagraphStyle.Attribute(W.styleId); - XElement defaultCharacterStyle = sXDoc + } + + var defaultCharacterStyle = sXDoc .Root .Elements(W.style) .FirstOrDefault(st => st.Attribute(W._default).ToBoolean() == true && (string)st.Attribute(W.type) == "character"); if (defaultCharacterStyle != null) + { fai.DefaultCharacterStyleName = (string)defaultCharacterStyle.Attribute(W.styleId); - XElement defaultTableStyle = sXDoc + } + + var defaultTableStyle = sXDoc .Root .Elements(W.style) .FirstOrDefault(st => st.Attribute(W._default).ToBoolean() == true && (string)st.Attribute(W.type) == "table"); if (defaultTableStyle != null) + { fai.DefaultTableStyleName = (string)defaultTableStyle.Attribute(W.styleId); - ListItemRetrieverSettings listItemRetrieverSettings = new ListItemRetrieverSettings(); + } + + var listItemRetrieverSettings = new ListItemRetrieverSettings(); AssembleListItemInformation(wDoc, settings.ListItemRetrieverSettings); foreach (var part in wDoc.ContentParts()) { @@ -85,13 +87,16 @@ public static void AssembleFormatting(WordprocessingDocument wDoc, FormattingAss } NormalizeListItems(fai, wDoc, settings); if (settings.ClearStyles) + { ClearStyles(wDoc); + } + foreach (var part in wDoc.ContentParts()) { var pxd = part.GetXDocument(); pxd.Root.Descendants().Attributes().Where(a => a.IsNamespaceDeclaration).Remove(); - FormattingAssembler.NormalizePropsForPart(pxd, settings); - var newRoot = (XElement)CleanupTransform(pxd.Root); + NormalizePropsForPart(pxd, settings); + var newRoot = CleanupTransform(pxd.Root) as XElement; pxd.Root.ReplaceWith(newRoot); part.PutXDocument(); } @@ -102,11 +107,17 @@ private static void FixNonconformantHexValues(XElement root) foreach (var tblLook in root.Descendants(W.tblLook)) { if (tblLook.Attributes().Any(a => a.Name != W.val)) + { continue; + } + if (tblLook.Attribute(W.val) == null) + { continue; - string hexValue = tblLook.Attribute(W.val).Value; - int val = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber); + } + + var hexValue = tblLook.Attribute(W.val).Value; + var val = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber); tblLook.Add(new XAttribute(W.firstRow, (val & 0x0020) != 0 ? "1" : "0")); tblLook.Add(new XAttribute(W.lastRow, (val & 0x0040) != 0 ? "1" : "0")); tblLook.Add(new XAttribute(W.firstColumn, (val & 0x0080) != 0 ? "1" : "0")); @@ -117,9 +128,15 @@ private static void FixNonconformantHexValues(XElement root) foreach (var cnfStyle in root.Descendants(W.cnfStyle)) { if (cnfStyle.Attributes().Any(a => a.Name != W.val)) + { continue; + } + if (cnfStyle.Attribute(W.val) == null) + { continue; + } + var va = cnfStyle.Attribute(W.val).Value.ToArray(); cnfStyle.Add(new XAttribute(W.firstRow, va[0])); cnfStyle.Add(new XAttribute(W.lastRow, va[1])); @@ -136,23 +153,24 @@ private static void FixNonconformantHexValues(XElement root) } } - private static object CleanupTransform(XNode node) + private static object? CleanupTransform(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.tabs && element.Element(W.tab) == null) + { return null; + } if (element.Name == W.tblStyleRowBandSize || element.Name == W.tblStyleColBandSize) + { return null; + } - // a cleaner solution would be to not include the w:ins and w:del elements when rolling up the paragraph run properties into - // the run properties. - if ((element.Name == W.ins || element.Name == W.del) && element.Parent.Name == W.rPr) + // a cleaner solution would be to not include the w:ins and w:del elements when rolling up the paragraph run properties into the run properties. + if ((element.Name == W.ins || element.Name == W.del) && element.Parent.Name == W.rPr && (element.Parent.Parent.Name == W.r || element.Parent.Parent.Name == W.rPrChange)) { - if (element.Parent.Parent.Name == W.r || element.Parent.Parent.Name == W.rPrChange) - return null; + return null; } return new XElement(element.Name, @@ -172,7 +190,10 @@ private static void ClearStyles(WordprocessingDocument wDoc) sXDoc.Root.Elements().Select(e => { if (e.Name != W.style) + { return e; + } + return new XElement(e.Name, e.Attributes(), e.Element(W.name), @@ -186,7 +207,9 @@ private static void ClearStyles(WordprocessingDocument wDoc) .Elements(W.rPr) .FirstOrDefault(); if (globalrPr != null) + { globalrPr.ReplaceWith(new XElement(W.rPr)); + } var globalpPr = newRoot .Elements(W.docDefaults) @@ -194,7 +217,9 @@ private static void ClearStyles(WordprocessingDocument wDoc) .Elements(W.pPr) .FirstOrDefault(); if (globalpPr != null) + { globalpPr.ReplaceWith(new XElement(W.pPr)); + } sXDoc.Root.ReplaceWith(newRoot); @@ -206,33 +231,38 @@ private static void NormalizeListItems(FormattingAssemblerInfo fai, Wordprocessi foreach (var part in wDoc.ContentParts()) { var pxd = part.GetXDocument(); - XElement newRoot = (XElement)NormalizeListItemsTransform(fai, wDoc, pxd.Root, settings); + var newRoot = (XElement)NormalizeListItemsTransform(fai, wDoc, pxd.Root, settings); if (newRoot.Attribute(XNamespace.Xmlns + "pt14") == null) + { newRoot.Add(new XAttribute(XNamespace.Xmlns + "pt14", PtOpenXml.pt.NamespaceName)); + } + if (newRoot.Attribute(XNamespace.Xmlns + "mc") == null) + { newRoot.Add(new XAttribute(XNamespace.Xmlns + "mc", MC.mc.NamespaceName)); + } + pxd.Root.ReplaceWith(newRoot); } } private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XNode node, FormattingAssemblerSettings settings) { - var element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.p) { var li = ListItemRetriever.RetrieveListItem(wDoc, element, settings.ListItemRetrieverSettings); if (li != null) { - ListItemRetriever.ListItemInfo listItemInfo = element.Annotation(); + var listItemInfo = element.Annotation(); var newParaProps = new XElement(W.pPr, element.Elements(W.pPr).Elements().Where(e => e.Name != W.numPr) ); - XElement listItemRunProps = null; - List listItemHtmlAttributes = new List(); + XElement? listItemRunProps = null; + var listItemHtmlAttributes = new List(); int? abstractNumId = null; if (listItemInfo != null) { @@ -246,7 +276,7 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W .Attributes(W.val) .FirstOrDefault(); - string defaultStyleName = (string)wDoc + var defaultStyleName = (string)wDoc .MainDocumentPart .StyleDefinitionsPart .GetXDocument() @@ -257,16 +287,18 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W .FirstOrDefault(); if (paragraphStyleName == null) + { paragraphStyleName = defaultStyleName; + } - XDocument stylesXDoc = wDoc + var stylesXDoc = wDoc .MainDocumentPart .StyleDefinitionsPart .GetXDocument(); // put together run props for list item. - XElement lvlStyleRpr = ParaStyleRunPropsStack(wDoc, paragraphStyleName) + var lvlStyleRpr = ParaStyleRunPropsStack(wDoc, paragraphStyleName) .Aggregate(new XElement(W.rPr), (r, s) => { @@ -278,14 +310,16 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W var accumulatedRunProps = element.Elements(PtOpenXml.pPr).Elements(W.rPr).FirstOrDefault(); if (accumulatedRunProps != null) + { mergedRunProps = MergeStyleElement(accumulatedRunProps, mergedRunProps); + } var listItemLvl = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(element)); var listItemLvlRunProps = listItemLvl.Elements(W.rPr).FirstOrDefault(); listItemRunProps = MergeStyleElement(listItemLvlRunProps, mergedRunProps); - string numFmt = null; - string format = null; + string? numFmt = null; + string? format = null; numFmt = (string)listItemLvl.Elements(W.numFmt).Attributes(W.val).FirstOrDefault(); if (numFmt == null) @@ -317,8 +351,8 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W var pPr = element.Element(PtOpenXml.pPr); if (pPr != null) { - XElement bidiel = pPr.Element(W.bidi); - bool bidi = bidiel != null && (bidiel.Attribute(W.val) == null || bidiel.Attribute(W.val).ToBoolean() == true); + var bidiel = pPr.Element(W.bidi); + var bidi = bidiel != null && (bidiel.Attribute(W.val) == null || bidiel.Attribute(W.val).ToBoolean() == true); if (bidi) { listItemRunProps = MergeStyleElement(new XElement(W.rPr, @@ -369,15 +403,15 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W } var paragraphLevel = ListItemRetriever.GetParagraphLevel(element); - ListItemRetriever.LevelNumbers levelNums = element.Annotation(); - string levelNumsString = levelNums + var levelNums = element.Annotation(); + var levelNumsString = levelNums .LevelNumbersArray .Take(paragraphLevel + 1) .Select(i => i.ToString() + ".") .StringConcatenate() .TrimEnd('.'); - ListItemRetriever.ListItemInfo listItemInfo2 = element.Annotation(); + var listItemInfo2 = element.Annotation(); var listItemRun = new XElement(W.r, new XAttribute(PtOpenXml.ListItemRun, levelNumsString), @@ -392,14 +426,18 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W AdjustFontAttributes(wDoc, listItemRun, null, listItemRunProps, settings); var lvl = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(element)); - XElement suffix = new XElement(W.tab); + var suffix = new XElement(W.tab); var su = (string)lvl.Elements(W.suff).Attributes(W.val).FirstOrDefault(); if (su == "space") + { suffix = new XElement(W.t, new XAttribute(XNamespace.Xml + "space", "preserve"), " "); + } else if (su == "nothing") + { suffix = null; + } var jc = (string)lvl.Elements(W.lvlJc).Attributes(W.val).FirstOrDefault(); if (jc == "right") @@ -446,22 +484,22 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W { var hanging = (int)hangingAtt; var listItemRunLength = WordprocessingMLUtil.CalcWidthOfRunInTwips(listItemRun); - hanging += (listItemRunLength / 2); // should be half of width of list item, in twips + hanging += listItemRunLength / 2; // should be half of width of list item, in twips hangingAtt.Value = hanging.ToString(); } } AddTabAtLeftIndent(element.Element(PtOpenXml.pPr)); - XElement tabRun = suffix != null ? + var tabRun = suffix != null ? new XElement(W.r, new XAttribute(PtOpenXml.ListItemRun, levelNumsString), listItemRunProps, suffix) : null; - bool isDeleted = false; - bool isInserted = false; - XAttribute authorAtt = null; - XAttribute dateAtt = null; + var isDeleted = false; + var isInserted = false; + XAttribute? authorAtt = null; + XAttribute? dateAtt = null; var paraDelElement = newParaProps .Elements(W.rPr) @@ -503,30 +541,6 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W } } -#if false - - - - - - - - - - - - - When you click Online Video, you can paste in the embed code for the video you want to add. - - -#endif - var pPrChange = element .Elements(W.pPr) .Elements(W.pPrChange) @@ -547,10 +561,14 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W .FirstOrDefault(); if (thisNumPr != null && thisNumPrChange == null) + { isInserted = true; + } if (thisNumPr == null && thisNumPrChange != null) + { isDeleted = true; + } } if (isDeleted) @@ -563,11 +581,14 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W .Attributes(W.id) .Select(id => { - int numId; - if (int.TryParse((string)id, out numId)) + if (int.TryParse((string)id, out var numId)) + { return numId; + } else + { return 0; + } }) .Max(); @@ -594,11 +615,14 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W .Attributes(W.id) .Select(id => { - int numId; - if (int.TryParse((string)id, out numId)) + if (int.TryParse((string)id, out var numId)) + { return numId; + } else + { return 0; + } }) .Max(); @@ -615,7 +639,7 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W } } - XElement newPara = new XElement(W.p, + var newPara = new XElement(W.p, element.Attribute(PtOpenXml.FontName), element.Attribute(PtOpenXml.LanguageType), element.Attribute(PtOpenXml.Unid), @@ -626,7 +650,6 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W tabRun, element.Elements().Where(e => e.Name != W.pPr).Select(n => NormalizeListItemsTransform(fai, wDoc, n, settings))); return newPara; - } } @@ -639,11 +662,12 @@ private static object NormalizeListItemsTransform(FormattingAssemblerInfo fai, W private static object TransformToDeleted(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.t) + { return new XElement(W.delText, element.Value); + } return new XElement(element.Name, element.Attributes(), @@ -654,12 +678,15 @@ private static object TransformToDeleted(XNode node) private static void AddTabAtLeftIndent(XElement pPr) { - int left = 0; + var left = 0; var ind = pPr.Element(W.ind); // todo need to handle W.start if (pPr.Attribute(W.left) != null) + { left = (int)pPr.Attribute(W.left); + } + var tabs = pPr.Element(W.tabs); if (tabs == null) { @@ -694,10 +721,16 @@ public static void NormalizePropsForPart(XDocument pxd, FormattingAssemblerSetti pxd.Root.Descendants().Attributes().Where(d => d.Name.Namespace == PtOpenXml.pt && !PtNamesToKeep.Contains(d.Name)).Remove(); if (pxd.Root.Attribute(XNamespace.Xmlns + "pt14") == null) + { pxd.Root.Add(new XAttribute(XNamespace.Xmlns + "pt14", PtOpenXml.pt.NamespaceName)); + } + if (pxd.Root.Attribute(XNamespace.Xmlns + "mc") == null) + { pxd.Root.Add(new XAttribute(XNamespace.Xmlns + "mc", MC.mc.NamespaceName)); - XAttribute mci = pxd.Root.Attribute(MC.Ignorable); + } + + var mci = pxd.Root.Attribute(MC.Ignorable); if (mci != null) { if (!pxd.Root.Attribute(MC.Ignorable).Value.Contains("pt14")) @@ -718,45 +751,47 @@ public static void NormalizePropsForPart(XDocument pxd, FormattingAssemblerSetti var runProps = pxd.Root.Descendants(PtOpenXml.rPr).ToList(); foreach (var item in runProps) { - XElement newRunProps = new XElement(W.rPr, + var newRunProps = new XElement(W.rPr, item.Attributes(), item.Elements()); - XElement parent = item.Parent; + var parent = item.Parent; if (parent.Name == W.p) { - XElement existingParaProps = parent.Element(W.pPr); + var existingParaProps = parent.Element(W.pPr); if (existingParaProps == null) { existingParaProps = new XElement(W.pPr); parent.Add(existingParaProps); } - XElement existingRunProps = existingParaProps.Element(W.rPr); + var existingRunProps = existingParaProps.Element(W.rPr); if (existingRunProps != null) { - if (!settings.RemoveStyleNamesFromParagraphAndRunProperties) + if (!settings.RemoveStyleNamesFromParagraphAndRunProperties && newRunProps.Element(W.rStyle) == null) { - if (newRunProps.Element(W.rStyle) == null) - newRunProps.Add(existingRunProps.Element(W.rStyle)); + newRunProps.Add(existingRunProps.Element(W.rStyle)); } existingRunProps.ReplaceWith(newRunProps); } else + { existingParaProps.Add(newRunProps); + } } else { - XElement existingRunProps = parent.Element(W.rPr); + var existingRunProps = parent.Element(W.rPr); if (existingRunProps != null) { - if (!settings.RemoveStyleNamesFromParagraphAndRunProperties) + if (!settings.RemoveStyleNamesFromParagraphAndRunProperties && newRunProps.Element(W.rStyle) == null) { - if (newRunProps.Element(W.rStyle) == null) - newRunProps.Add(existingRunProps.Element(W.rStyle)); + newRunProps.Add(existingRunProps.Element(W.rStyle)); } existingRunProps.ReplaceWith(newRunProps); } else + { parent.Add(newRunProps); + } } } var paraProps = pxd.Root.Descendants(PtOpenXml.pPr).ToList(); @@ -764,67 +799,83 @@ public static void NormalizePropsForPart(XDocument pxd, FormattingAssemblerSetti { var paraRunProps = item.Parent.Elements(W.pPr).Elements(W.rPr).FirstOrDefault(); var merged = MergeStyleElement(item.Element(W.rPr), paraRunProps); - if (!settings.RemoveStyleNamesFromParagraphAndRunProperties) + if (!settings.RemoveStyleNamesFromParagraphAndRunProperties && merged.Element(W.rStyle) == null) { - if (merged.Element(W.rStyle) == null) - { - merged.Add(paraRunProps.Element(W.rStyle)); - } + merged.Add(paraRunProps.Element(W.rStyle)); } - XElement newParaProps = new XElement(W.pPr, + var newParaProps = new XElement(W.pPr, item.Attributes(), item.Elements().Where(e => e.Name != W.rPr), merged); - XElement para = item.Parent; - XElement existingParaProps = para.Element(W.pPr); + var para = item.Parent; + var existingParaProps = para.Element(W.pPr); if (existingParaProps != null) { - if (!settings.RemoveStyleNamesFromParagraphAndRunProperties) + if (!settings.RemoveStyleNamesFromParagraphAndRunProperties && newParaProps.Element(W.pStyle) == null) { - if (newParaProps.Element(W.pStyle) == null) - newParaProps.Add(existingParaProps.Element(W.pStyle)); + newParaProps.Add(existingParaProps.Element(W.pStyle)); } existingParaProps.ReplaceWith(newParaProps); } else + { para.Add(newParaProps); + } } var tblProps = pxd.Root.Descendants(PtOpenXml.tblPr).ToList(); foreach (var item in tblProps) { - XElement newTblProps = new XElement(item); - newTblProps.Name = W.tblPr; - XElement table = item.Parent; - XElement existingTableProps = table.Element(W.tblPr); + var newTblProps = new XElement(item) + { + Name = W.tblPr + }; + var table = item.Parent; + var existingTableProps = table.Element(W.tblPr); if (existingTableProps != null) + { existingTableProps.ReplaceWith(newTblProps); + } else + { table.AddFirst(newTblProps); + } } var trProps = pxd.Root.Descendants(PtOpenXml.trPr).ToList(); foreach (var item in trProps) { - XElement newTrProps = new XElement(item); - newTrProps.Name = W.trPr; - XElement row = item.Parent; - XElement existingRowProps = row.Element(W.trPr); + var newTrProps = new XElement(item) + { + Name = W.trPr + }; + var row = item.Parent; + var existingRowProps = row.Element(W.trPr); if (existingRowProps != null) + { existingRowProps.ReplaceWith(newTrProps); + } else + { row.AddFirst(newTrProps); + } } var tcProps = pxd.Root.Descendants(PtOpenXml.tcPr).ToList(); foreach (var item in tcProps) { - XElement newTcProps = new XElement(item); - newTcProps.Name = W.tcPr; - XElement row = item.Parent; - XElement existingRowProps = row.Element(W.tcPr); + var newTcProps = new XElement(item) + { + Name = W.tcPr + }; + var row = item.Parent; + var existingRowProps = row.Element(W.tcPr); if (existingRowProps != null) + { existingRowProps.ReplaceWith(newTcProps); + } else + { row.AddFirst(newTcProps); + } } pxd.Root.Descendants(W.numPr).Remove(); if (settings.RemoveStyleNamesFromParagraphAndRunProperties) @@ -836,7 +887,7 @@ public static void NormalizePropsForPart(XDocument pxd, FormattingAssemblerSetti pxd.Root.Descendants().Where(d => d.Name.Namespace == PtOpenXml.pt).Remove(); if (settings.OrderElementsPerStandard) { - XElement newRoot = (XElement)WordprocessingMLUtil.WmlOrderElementsPerStandard(pxd.Root); + var newRoot = (XElement)WordprocessingMLUtil.WmlOrderElementsPerStandard(pxd.Root); pxd.Root.ReplaceWith(newRoot); } } @@ -845,7 +896,7 @@ private static void AssembleListItemInformation(WordprocessingDocument wordDoc, { foreach (var part in wordDoc.ContentParts()) { - XDocument xDoc = part.GetXDocument(); + var xDoc = part.GetXDocument(); foreach (var para in xDoc.Descendants(W.p)) { ListItemRetriever.RetrieveListItem(wordDoc, para, settings); @@ -855,11 +906,11 @@ private static void AssembleListItemInformation(WordprocessingDocument wordDoc, private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XElement rootElement, FormattingAssemblerSettings settings) { - XElement globalDefaultParaProps = null; - XElement globalDefaultParaPropsAsDefined = null; - XElement globalDefaultRunProps = null; - XElement globalDefaultRunPropsAsDefined = null; - XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + XElement? globalDefaultParaProps = null; + XElement? globalDefaultParaPropsAsDefined = null; + XElement? globalDefaultRunProps = null; + XElement? globalDefaultRunPropsAsDefined = null; + var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); var defaultParaStyleName = (string)sXDoc .Root .Elements(W.style) @@ -872,32 +923,46 @@ private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XEle .Where(st => (string)st.Attribute(W.type) == "character" && st.Attribute(W._default).ToBoolean() == true) .Attributes(W.styleId) .FirstOrDefault(); - XElement docDefaults = sXDoc.Root.Element(W.docDefaults); + var docDefaults = sXDoc.Root.Element(W.docDefaults); if (docDefaults != null) { globalDefaultParaPropsAsDefined = docDefaults.Elements(W.pPrDefault).Elements(W.pPr) .FirstOrDefault(); if (globalDefaultParaPropsAsDefined == null) + { globalDefaultParaPropsAsDefined = new XElement(W.pPr, new XElement(W.rPr)); + } + globalDefaultRunPropsAsDefined = docDefaults.Elements(W.rPrDefault).Elements(W.rPr) .FirstOrDefault(); if (globalDefaultRunPropsAsDefined == null) + { globalDefaultRunPropsAsDefined = new XElement(W.rPr); + } + if (globalDefaultRunPropsAsDefined.Element(W.rFonts) == null) + { globalDefaultRunPropsAsDefined.Add( new XElement(W.rFonts, new XAttribute(W.ascii, "Times New Roman"), new XAttribute(W.hAnsi, "Times New Roman"), new XAttribute(W.cs, "Times New Roman"))); + } + if (globalDefaultRunPropsAsDefined.Element(W.sz) == null) + { globalDefaultRunPropsAsDefined.Add( new XElement(W.sz, new XAttribute(W.val, "20"))); + } + if (globalDefaultRunPropsAsDefined.Element(W.szCs) == null) + { globalDefaultRunPropsAsDefined.Add( new XElement(W.szCs, new XAttribute(W.val, "20"))); + } var runPropsForGlobalDefaultParaProps = MergeStyleElement(globalDefaultRunPropsAsDefined, globalDefaultParaPropsAsDefined.Element(W.rPr)); globalDefaultParaProps = new XElement(globalDefaultParaPropsAsDefined.Name, @@ -917,13 +982,17 @@ private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XEle new XAttribute(W.val, "20"))); if (globalDefaultParaProps == null) + { globalDefaultParaProps = new XElement(W.pPr, rPr); + } if (globalDefaultRunProps == null) + { globalDefaultRunProps = rPr; + } - XElement ptGlobalDefaultParaProps = new XElement(globalDefaultParaProps); - XElement ptGlobalDefaultRunProps = new XElement(globalDefaultRunProps); + var ptGlobalDefaultParaProps = new XElement(globalDefaultParaProps); + var ptGlobalDefaultRunProps = new XElement(globalDefaultRunProps); ptGlobalDefaultParaProps.Name = PtOpenXml.pPr; ptGlobalDefaultRunProps.Name = PtOpenXml.rPr; var parasAndRuns = rootElement.Descendants().Where(d => @@ -938,13 +1007,20 @@ private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XEle { var pStyle = (string)d.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault(); if (pStyle == null) + { pStyle = defaultParaStyleName; + } + if (pStyle != null) { if (d.Attribute(PtOpenXml.StyleName) != null) + { d.Attribute(PtOpenXml.StyleName).Value = pStyle; + } else + { d.Add(new XAttribute(PtOpenXml.StyleName, pStyle)); + } } d.Add(ptGlobalDefaultParaProps); } @@ -952,13 +1028,20 @@ private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XEle { var rStyle = (string)d.Elements(W.rPr).Elements(W.rStyle).Attributes(W.val).FirstOrDefault(); if (rStyle == null) + { rStyle = defaultCharStyleName; + } + if (rStyle != null) { if (d.Attribute(PtOpenXml.StyleName) != null) + { d.Attribute(PtOpenXml.StyleName).Value = rStyle; + } else + { d.Add(new XAttribute(PtOpenXml.StyleName, rStyle)); + } } d.Add(ptGlobalDefaultRunProps); } @@ -980,7 +1063,7 @@ private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XEle } } - private static XElement BlankTcBorders = new XElement(W.tcBorders, + private static readonly XElement BlankTcBorders = new XElement(W.tcBorders, new XElement(W.top, new XAttribute(W.val, "nil")), new XElement(W.left, new XAttribute(W.val, "nil")), new XElement(W.bottom, new XAttribute(W.val, "nil")), @@ -988,31 +1071,33 @@ private static void AnnotateWithGlobalDefaults(WordprocessingDocument wDoc, XEle private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, XElement rootElement) { - XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); foreach (var tbl in rootElement.Descendants(W.tbl)) { - string tblStyleName = (string)tbl.Elements(W.tblPr).Elements(W.tblStyle).Attributes(W.val).FirstOrDefault(); + var tblStyleName = (string)tbl.Elements(W.tblPr).Elements(W.tblStyle).Attributes(W.val).FirstOrDefault(); if (tblStyleName != null) { - XElement style = TableStyleRollup(wDoc, tblStyleName); + var style = TableStyleRollup(wDoc, tblStyleName); // annotate table with table style, in PowerTools namespace style.Name = PtOpenXml.style; tbl.Add(style); // merge tblPr in table style with tblPr of the table - // annnotate in PowerTools namespace - XElement tblPr2 = style.Element(W.tblPr); - XElement tblPr3 = MergeStyleElement(tbl.Element(W.tblPr), tblPr2, true); + // annotate in PowerTools namespace + var tblPr2 = style.Element(W.tblPr); + var tblPr3 = MergeStyleElement(tbl.Element(W.tblPr), tblPr2, true); if (tblPr3 != null) { - XElement newTblPr = new XElement(tblPr3); - newTblPr.Name = PtOpenXml.pt + "tblPr"; + var newTblPr = new XElement(tblPr3) + { + Name = PtOpenXml.pt + "tblPr" + }; tbl.Add(newTblPr); } AddTcPrPtToEveryCell(tbl); - AddOuterBorders(tbl, style); + AddOuterBorders(tbl); var tableTcPr = style.Element(W.tcPr); if (tableTcPr != null) @@ -1021,19 +1106,30 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X { foreach (var cell in row.Elements(W.tc)) { - bool tcPrPtExists = false; + var tcPrPtExists = false; var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr"); if (tcPrPt != null) + { tcPrPtExists = true; + } else + { tcPrPt = new XElement(W.tcPr); + } + tcPrPt = MergeStyleElement(tableTcPr, tcPrPt); - var newTcPrPt = new XElement(tcPrPt); - newTcPrPt.Name = PtOpenXml.tcPr; + var newTcPrPt = new XElement(tcPrPt) + { + Name = PtOpenXml.tcPr + }; if (tcPrPtExists) + { cell.Element(PtOpenXml.tcPr).ReplaceWith(newTcPrPt); + } else + { cell.Add(newTcPrPt); + } } } } @@ -1042,25 +1138,27 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X // as appropriate per the cnfStyle element, then replacing the row and cell properties foreach (var row in tbl.Elements(W.tr)) { - XElement trPr2 = null; + XElement? trPr2 = null; trPr2 = style.Element(W.trPr); if (trPr2 == null) + { trPr2 = new XElement(W.trPr); - XElement rowCnf = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); + } + + var rowCnf = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); if (rowCnf != null) { foreach (var ot in TableStyleOverrideTypes) { - XName attName = TableStyleOverrideXNameMap[ot]; + var attName = TableStyleOverrideXNameMap[ot]; if (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true) { - XElement o = style + var o = style .Elements(W.tblStylePr) - .Where(tsp => (string)tsp.Attribute(W.type) == ot) - .FirstOrDefault(); +.FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == ot); if (o != null) { - XElement ottrPr = o.Element(W.trPr); + var ottrPr = o.Element(W.trPr); trPr2 = MergeStyleElement(ottrPr, trPr2); } } @@ -1076,7 +1174,7 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X foreach (var ot in TableStyleOverrideTypes) { - XName attName = TableStyleOverrideXNameMap[ot]; + var attName = TableStyleOverrideXNameMap[ot]; if (attName == W.oddHBand || attName == W.evenHBand || attName == W.firstRow || @@ -1084,30 +1182,40 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X { foreach (var row in tbl.Elements(W.tr)) { - XElement rowCnf = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); + var rowCnf = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); if (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true) { - XElement o = style + var o = style .Elements(W.tblStylePr) - .Where(tsp => (string)tsp.Attribute(W.type) == ot) - .FirstOrDefault(); +.FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == ot); if (o != null) { foreach (var cell in row.Elements(W.tc)) { - bool tcPrPtExists = false; + var tcPrPtExists = false; var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr"); if (tcPrPt != null) + { tcPrPtExists = true; + } else + { tcPrPt = new XElement(W.tcPr); + } + tcPrPt = MergeStyleElement(o.Element(W.tcPr), tcPrPt); - var newTcPrPt = new XElement(tcPrPt); - newTcPrPt.Name = PtOpenXml.pt + "tcPr"; + var newTcPrPt = new XElement(tcPrPt) + { + Name = PtOpenXml.pt + "tcPr" + }; if (tcPrPtExists) + { cell.Element(PtOpenXml.pt + "tcPr").ReplaceWith(newTcPrPt); + } else + { cell.Add(newTcPrPt); + } } } } @@ -1133,7 +1241,9 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X { var cell = row.Elements(W.tc).LastOrDefault(); if (cell != null) + { ApplyCndFmtToCell(style, ot, attName, cell); + } } } else if (attName == W.firstRowFirstColumn) @@ -1143,7 +1253,9 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X { var cell = row.Elements(W.tc).FirstOrDefault(); if (cell != null) + { ApplyCndFmtToCell(style, ot, attName, cell); + } } } else if (attName == W.lastRowLastColumn) @@ -1153,7 +1265,9 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X { var cell = row.Elements(W.tc).LastOrDefault(); if (cell != null) + { ApplyCndFmtToCell(style, ot, attName, cell); + } } } else if (attName == W.lastRowFirstColumn) @@ -1163,7 +1277,9 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X { var cell = row.Elements(W.tc).FirstOrDefault(); if (cell != null) + { ApplyCndFmtToCell(style, ot, attName, cell); + } } } } @@ -1172,17 +1288,20 @@ private static void AnnotateTablesWithTableStyles(WordprocessingDocument wDoc, X else { var tblPr = new XElement(W.tblPr); - XElement tblPr3 = MergeStyleElement(tbl.Element(W.tblPr), tblPr, true); + var tblPr3 = MergeStyleElement(tbl.Element(W.tblPr), tblPr, true); if (tblPr3 != null) { - XElement newTblPr = new XElement(tblPr3); - newTblPr.Name = PtOpenXml.pt + "tblPr"; + var newTblPr = new XElement(tblPr3) + { + Name = PtOpenXml.pt + "tblPr" + }; tbl.Add(newTblPr); } AddTcPrPtToEveryCell(tbl); } - RollInDirectFormatting(tbl); // it is important that this is last. This merges in direct formatting. + // it is important that this is last. This merges in direct formatting. + RollInDirectFormatting(tbl); } } @@ -1194,7 +1313,10 @@ private static void AddTcPrPtToEveryCell(XElement tbl) { var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr"); if (tcPrPt != null) + { continue; + } + tcPrPt = new XElement(PtOpenXml.pt + "tcPr", new XElement(W.tcBorders)); cell.Add(tcPrPt); @@ -1204,28 +1326,38 @@ private static void AddTcPrPtToEveryCell(XElement tbl) private static void ApplyCndFmtToCell(XElement style, string ot, XName attName, XElement cell) { - XElement cellCnf = cell.Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault(); + var cellCnf = cell.Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault(); if (cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true) { - XElement o = style + var o = style .Elements(W.tblStylePr) - .Where(tsp => (string)tsp.Attribute(W.type) == ot) - .FirstOrDefault(); + .FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == ot); if (o != null) { - bool tcPrPtExists = false; + var tcPrPtExists = false; var tcPrPt = cell.Element(PtOpenXml.pt + "tcPr"); if (tcPrPt != null) + { tcPrPtExists = true; + } else + { tcPrPt = new XElement(W.tcPr); + } + tcPrPt = MergeStyleElement(o.Element(W.tcPr), tcPrPt); - var newTcPrPt = new XElement(tcPrPt); - newTcPrPt.Name = PtOpenXml.pt + "tcPr"; + var newTcPrPt = new XElement(tcPrPt) + { + Name = PtOpenXml.pt + "tcPr" + }; if (tcPrPtExists) + { cell.Element(PtOpenXml.pt + "tcPr").ReplaceWith(newTcPrPt); + } else + { cell.Add(newTcPrPt); + } } } } @@ -1255,13 +1387,19 @@ private static void RollInDirectFormatting(XElement tbl) mTcPr = new XElement(PtOpenXml.pt + "tcPr"); cell.Add(tcPr); } - var newTcPr = new XElement(mTcPr); - newTcPr.Name = PtOpenXml.pt + "tcPr"; + var newTcPr = new XElement(mTcPr) + { + Name = PtOpenXml.pt + "tcPr" + }; var existing = cell.Element(PtOpenXml.pt + "tcPr"); if (existing != null) + { existing.ReplaceWith(newTcPr); + } else + { cell.Add(newTcPr); + } XElement rightCell = cell.ElementsAfterSelf().FirstOrDefault(); if (rightCell != null) @@ -1323,9 +1461,13 @@ private static void ApplyTblBordersToTable(XElement tbl, XElement tblBorders) { var cellTop = cellTcBorders.Element(W.top); if (cellTop == null) + { cellTcBorders.Add(top); + } else + { cellTop.ReplaceAttributes(top.Attributes()); + } } } } @@ -1343,9 +1485,13 @@ private static void ApplyTblBordersToTable(XElement tbl, XElement tblBorders) { var cellBottom = cellTcBorders.Element(W.bottom); if (cellBottom == null) + { cellTcBorders.Add(bottom); + } else + { cellBottom.ReplaceAttributes(bottom.Attributes()); + } } } } @@ -1363,9 +1509,13 @@ private static void ApplyTblBordersToTable(XElement tbl, XElement tblBorders) { var firstCellLeft = cellTcBorders.Element(W.left); if (firstCellLeft == null) + { cellTcBorders.Add(left); + } else + { firstCellLeft.ReplaceAttributes(left.Attributes()); + } } } } @@ -1380,33 +1530,41 @@ private static void ApplyTblBordersToTable(XElement tbl, XElement tblBorders) { var lastCellRight = cellTcBorders.Element(W.right); if (lastCellRight == null) + { cellTcBorders.Add(right); + } else + { lastCellRight.ReplaceAttributes(right.Attributes()); + } } } } } } - private static void AddOuterBorders(XElement tbl, XElement style) + private static void AddOuterBorders(XElement tbl) { var tblBorders = tbl.Elements(PtOpenXml.pt + "tblPr").Elements(W.tblBorders).FirstOrDefault(); if (tblBorders != null) + { ApplyTblBordersToTable(tbl, tblBorders); + } } private static void ProcessInnerBorders(XElement tbl, XElement style) { var tblBorders = tbl.Elements(PtOpenXml.pt + "tblPr").Elements(W.tblBorders).FirstOrDefault(); if (tblBorders != null) + { ProcessInnerBordersPerTblBorders(tbl, tblBorders); + } foreach (var attName in new[] { W.oddHBand, W.evenHBand, W.firstRow, W.lastRow }) { - int rowCount = tbl.Elements(W.tr).Count(); - int lastRow = rowCount - 1; - XElement insideV = null; + var rowCount = tbl.Elements(W.tr).Count(); + var lastRow = rowCount - 1; + XElement? insideV = null; foreach (var row in tbl.Elements(W.tr)) { var rowCnfStyle = row.Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); @@ -1431,8 +1589,8 @@ private static void ProcessInnerBorders(XElement tbl, XElement style) insideV = cndStyle.Elements(W.tcPr).Elements(W.tcBorders).Elements(W.insideV).FirstOrDefault(); if (insideV != null) { - int lastCol = row.Elements(W.tc).Count() - 1; - int colIdx = 0; + var lastCol = row.Elements(W.tc).Count() - 1; + var colIdx = 0; foreach (var cell in row.Elements(W.tc)) { var tcBorders = cell.Elements(PtOpenXml.pt + "tcPr").Elements(W.tcBorders).FirstOrDefault(); @@ -1461,8 +1619,8 @@ private static void ProcessInnerBorders(XElement tbl, XElement style) foreach (var attName in new[] { W.oddVBand, W.evenVBand, W.firstColumn, W.lastColumn }) { - int rowIdx = 0; - int lastRow = tbl.Elements(W.tr).Count() - 1; + var rowIdx = 0; + var lastRow = tbl.Elements(W.tr).Count() - 1; foreach (var row in tbl.Elements(W.tr)) { foreach (var cell in row.Elements(W.tc)) @@ -1514,7 +1672,7 @@ private static void ProcessInnerBordersPerTblBorders(XElement tbl, XElement tblB foreach (var row in tbl.Elements(W.tr)) { var lastCell = row.Elements(W.tc).Count() - 1; - int cellIdx = 0; + var cellIdx = 0; foreach (var cell in row.Elements(W.tc)) { var tcPr = cell.Element(PtOpenXml.pt + "tcPr"); @@ -1549,8 +1707,8 @@ private static void ProcessInnerBordersPerTblBorders(XElement tbl, XElement tblB var tblInsideH = tblBorders.Elements(W.insideH).FirstOrDefault(); if (tblInsideH != null) { - int rowIdx1 = 0; - int lastRow1 = tbl.Elements(W.tr).Count() - 1; + var rowIdx1 = 0; + var lastRow1 = tbl.Elements(W.tr).Count() - 1; foreach (var row in tbl.Elements(W.tr)) { if (rowIdx1 == 0) @@ -1599,7 +1757,7 @@ private static void ResolveInsideWithExisting(XElement tcBorders, XElement insid } } - private static Dictionary BorderTypePriority = new Dictionary() + private static readonly Dictionary BorderTypePriority = new Dictionary() { { "single", 1 }, { "thick", 2 }, @@ -1607,7 +1765,7 @@ private static void ResolveInsideWithExisting(XElement tcBorders, XElement insid { "dotted", 4 }, }; - private static Dictionary BorderNumber = new Dictionary() + private static readonly Dictionary BorderNumber = new Dictionary() { {"single", 1 }, {"thick", 2 }, @@ -1636,14 +1794,22 @@ private static void ResolveInsideWithExisting(XElement tcBorders, XElement insid {"inset", 25 }, }; - private static XElement ResolveInsideBorder(XElement inside1, XElement sideToReplace) + private static XElement? ResolveInsideBorder(XElement inside1, XElement sideToReplace) { if (inside1 == null && sideToReplace == null) + { return null; + } + if (inside1 == null) + { return sideToReplace; + } + if (sideToReplace == null) + { return inside1; + } // The following handles the situation where // if table innerV is set, and cnd format for first row specifies nill border, then nil border wins. @@ -1652,41 +1818,63 @@ private static XElement ResolveInsideBorder(XElement inside1, XElement sideToRep sideToReplace.Name == W.right) { if ((string)inside1.Attribute(W.val) == "nil") + { return inside1; + } + if ((string)sideToReplace.Attribute(W.val) == "nil") + { return sideToReplace; + } } else { if ((string)inside1.Attribute(W.val) == "nil") + { return sideToReplace; + } + if ((string)sideToReplace.Attribute(W.val) == "nil") + { return inside1; + } } var inside1Val = (string)inside1.Attribute(W.val); var border1Weight = 1; if (BorderNumber.ContainsKey(inside1Val)) + { border1Weight = BorderNumber[inside1Val]; + } var sideToReplaceVal = (string)sideToReplace.Attribute(W.val); var sideToReplaceWeight = 1; if (BorderNumber.ContainsKey(sideToReplaceVal)) + { sideToReplaceWeight = BorderNumber[sideToReplaceVal]; + } if (border1Weight != sideToReplaceWeight) { if (border1Weight < sideToReplaceWeight) + { return sideToReplace; + } else + { return inside1; + } } if ((int)inside1.Attribute(W.sz) > (int)sideToReplace.Attribute(W.sz)) + { return inside1; + } if ((int)sideToReplace.Attribute(W.sz) > (int)inside1.Attribute(W.sz)) + { return sideToReplace; + } if (BorderTypePriority.ContainsKey(inside1Val) && BorderTypePriority.ContainsKey(sideToReplaceVal)) @@ -1694,33 +1882,48 @@ private static XElement ResolveInsideBorder(XElement inside1, XElement sideToRep var inside1Pri = BorderTypePriority[inside1Val]; var inside2Pri = BorderTypePriority[sideToReplaceVal]; if (inside1Pri > inside2Pri) + { return inside1; + } + if (inside2Pri > inside1Pri) + { return sideToReplace; + } } if ((int)inside1.Attribute(W.sz) > (int)sideToReplace.Attribute(W.sz)) + { return inside1; + } if ((int)sideToReplace.Attribute(W.sz) > (int)inside1.Attribute(W.sz)) + { return sideToReplace; + } var color1str = (string)inside1.Attribute(W.color); if (color1str == "auto") + { color1str = "000000"; + } + var color2str = (string)sideToReplace.Attribute(W.color); if (color2str == "auto") + { color2str = "000000"; + } + if (color1str != null && color2str != null && color1str != color2str) { - Int32 color1; - Int32 color2; + int color1; + int color2; try { color1 = Convert.ToInt32(color1str, 16); } // if the above throws ArgumentException, FormatException, or OverflowException, then abort - catch (Exception) + catch (Exception) { return sideToReplace; } @@ -1734,9 +1937,15 @@ private static XElement ResolveInsideBorder(XElement inside1, XElement sideToRep return inside1; } if (color1 < color2) + { return inside1; + } + if (color2 < color1) + { return sideToReplace; + } + return inside1; } return inside1; @@ -1746,7 +1955,7 @@ private static XElement TableStyleRollup(WordprocessingDocument wDoc, string tbl { var tblStyleChain = TableStyleStack(wDoc, tblStyleName) .Reverse(); - XElement rolledStyle = new XElement(W.style); + var rolledStyle = new XElement(W.style); foreach (var style in tblStyleChain) { rolledStyle = MergeStyleElement(style, rolledStyle); @@ -1754,7 +1963,7 @@ private static XElement TableStyleRollup(WordprocessingDocument wDoc, string tbl return rolledStyle; } - private static XName[] SpecialCaseChildProperties = + private static readonly XName[] SpecialCaseChildProperties = { W.tblPr, W.trPr, @@ -1773,7 +1982,7 @@ private static XElement TableStyleRollup(WordprocessingDocument wDoc, string tbl W.numPr, }; - private static XName[] MergeChildProperties = + private static readonly XName[] MergeChildProperties = { W.tblPr, W.trPr, @@ -1786,7 +1995,7 @@ private static XElement TableStyleRollup(WordprocessingDocument wDoc, string tbl W.numPr, }; - private static string[] TableStyleOverrideTypes = + private static readonly string[] TableStyleOverrideTypes = { "band1Vert", "band2Vert", @@ -1802,7 +2011,7 @@ private static XElement TableStyleRollup(WordprocessingDocument wDoc, string tbl "swCell", }; - private static Dictionary TableStyleOverrideXNameMap = new Dictionary + private static readonly Dictionary TableStyleOverrideXNameMap = new Dictionary { {"band1Vert", W.oddVBand}, {"band2Vert", W.evenVBand}, @@ -1818,7 +2027,7 @@ private static XElement TableStyleRollup(WordprocessingDocument wDoc, string tbl {"swCell", W.lastRowFirstColumn}, }; - private static Dictionary TableStyleOverrideXNameRevMap = new Dictionary + private static readonly Dictionary TableStyleOverrideXNameRevMap = new Dictionary { {W.oddVBand, "band1Vert"}, {W.evenVBand, "band2Vert"}, @@ -1841,9 +2050,14 @@ private static XElement MergeStyleElement(XElement higherPriorityElement, XEleme // corresponding element in the merged element, then include the source element // in the merged element. if (lowerPriorityElement == null) + { return higherPriorityElement; + } + if (higherPriorityElement == null) + { return lowerPriorityElement; + } var hpe = higherPriorityElement .Elements() @@ -1879,9 +2093,15 @@ private static XElement MergeStyleElement(XElement higherPriorityElement, XEleme var h = higherPriorityElement.Element(e); var l = lowerPriorityElement.Element(e); if (h == null && l == null) + { return null; + } + if (h == null && l != null) + { return l; + } + if (h != null && l == null) { var newH = new XElement(h.Name, @@ -1901,17 +2121,26 @@ private static XElement MergeStyleElement(XElement higherPriorityElement, XEleme var h = higherPriorityElement.Elements(W.tblStylePr).FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == e); var l = lowerPriorityElement.Elements(W.tblStylePr).FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == e); if (h == null && l == null) + { return null; + } + if (h == null && l != null) + { return l; + } + if (h != null && l == null) + { return h; + } + return MergeStyleElement(h, l); }) .Where(m => m != null) .ToArray(); - XElement newMergedElement = new XElement(higherPriorityElement.Name, + var newMergedElement = new XElement(higherPriorityElement.Name, new XAttribute(XNamespace.Xmlns + "w", W.w), higherPriorityElement.Attributes().Where(a => !a.IsNamespaceDeclaration), hpe, // higher priority elements @@ -1928,14 +2157,23 @@ private static XElement MergeStyleElement(XElement higherPriorityElement, XEleme return newMergedElement; } - private static XElement LangMerge(XElement hLang, XElement lLang) + private static XElement? LangMerge(XElement hLang, XElement lLang) { if (hLang == null && lLang == null) + { return null; + } + if (hLang != null && lLang == null) + { return hLang; + } + if (lLang != null && hLang == null) + { return lLang; + } + return new XElement(W.lang, hLang.Attribute(W.val) != null ? hLang.Attribute(W.val) : lLang.Attribute(W.val), hLang.Attribute(W.bidi) != null ? hLang.Attribute(W.bidi) : lLang.Attribute(W.bidi), @@ -1952,29 +2190,45 @@ private enum IndAttType None, }; - private static XElement IndMerge(XElement higherPriorityElement, XElement lowerPriorityElement) + private static XElement? IndMerge(XElement higherPriorityElement, XElement lowerPriorityElement) { if (higherPriorityElement == null && lowerPriorityElement == null) + { return null; + } + if (higherPriorityElement != null && lowerPriorityElement == null) + { return higherPriorityElement; + } + if (lowerPriorityElement != null && higherPriorityElement == null) + { return lowerPriorityElement; + } - XElement hpe = new XElement(higherPriorityElement); - XElement lpe = new XElement(lowerPriorityElement); + var hpe = new XElement(higherPriorityElement); + var lpe = new XElement(lowerPriorityElement); if (hpe.Attribute(W.firstLine) != null) + { lpe.Attributes(W.hanging).Remove(); + } if (hpe.Attribute(W.firstLineChars) != null) + { lpe.Attributes(W.hangingChars).Remove(); + } if (hpe.Attribute(W.hanging) != null) + { lpe.Attributes(W.firstLine).Remove(); + } if (hpe.Attribute(W.hangingChars) != null) + { lpe.Attributes(W.firstLineChars).Remove(); + } var highPriAtts = hpe .Attributes() @@ -2000,14 +2254,23 @@ private static XElement IndMerge(XElement higherPriorityElement, XElement lowerP // merge child tab elements // they are additive, with the exception that if there are two elements at the same location, // we need to take the higher, and not take the lower. - private static XElement TabsMerge(XElement higherPriorityElement, XElement lowerPriorityElement) + private static XElement? TabsMerge(XElement higherPriorityElement, XElement lowerPriorityElement) { if (higherPriorityElement != null && lowerPriorityElement == null) + { return higherPriorityElement; + } + if (higherPriorityElement == null && lowerPriorityElement != null) + { return lowerPriorityElement; + } + if (higherPriorityElement == null && lowerPriorityElement == null) + { return null; + } + var hps = higherPriorityElement.Elements().Select(e => new { @@ -2033,14 +2296,23 @@ private static XElement TabsMerge(XElement higherPriorityElement, XElement lower return newTabs; } - private static XElement SpacingMerge(XElement hn, XElement ln) + private static XElement? SpacingMerge(XElement hn, XElement ln) { if (hn == null && ln == null) + { return null; + } + if (hn != null && ln == null) + { return hn; + } + if (hn == null && ln != null) + { return ln; + } + var mn1 = new XElement(W.spacing, hn.Attributes(), ln.Attributes().Where(a => hn.Attribute(a.Name) == null)); @@ -2049,50 +2321,62 @@ private static XElement SpacingMerge(XElement hn, XElement ln) private static IEnumerable TableStyleStack(WordprocessingDocument wDoc, string tblStyleName) { - XDocument sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); - string currentStyle = tblStyleName; + var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var currentStyle = tblStyleName; while (true) { - XElement style = sXDoc + var style = sXDoc .Root - .Elements(W.style).Where(s => (string)s.Attribute(W.type) == "table" && - (string)s.Attribute(W.styleId) == currentStyle) - .FirstOrDefault(); + .Elements(W.style).FirstOrDefault(s => (string)s.Attribute(W.type) == "table" && + (string)s.Attribute(W.styleId) == currentStyle); if (style == null) + { yield break; + } + yield return style; currentStyle = (string)style.Elements(W.basedOn).Attributes(W.val).FirstOrDefault(); if (currentStyle == null) + { yield break; + } } } - private static XElement FontMerge(XElement higherPriorityFont, XElement lowerPriorityFont) + private static XElement? FontMerge(XElement higherPriorityFont, XElement lowerPriorityFont) { XElement rFonts; if (higherPriorityFont == null) + { return lowerPriorityFont; + } + if (lowerPriorityFont == null) + { return higherPriorityFont; + } + if (higherPriorityFont == null && lowerPriorityFont == null) + { return null; + } rFonts = new XElement(W.rFonts, - (higherPriorityFont.Attribute(W.ascii) != null || higherPriorityFont.Attribute(W.asciiTheme) != null) ? + higherPriorityFont.Attribute(W.ascii) != null || higherPriorityFont.Attribute(W.asciiTheme) != null ? new[] { higherPriorityFont.Attribute(W.ascii), higherPriorityFont.Attribute(W.asciiTheme) } : new[] { lowerPriorityFont.Attribute(W.ascii), lowerPriorityFont.Attribute(W.asciiTheme) }, - (higherPriorityFont.Attribute(W.hAnsi) != null || higherPriorityFont.Attribute(W.hAnsiTheme) != null) ? + higherPriorityFont.Attribute(W.hAnsi) != null || higherPriorityFont.Attribute(W.hAnsiTheme) != null ? new[] { higherPriorityFont.Attribute(W.hAnsi), higherPriorityFont.Attribute(W.hAnsiTheme) } : new[] { lowerPriorityFont.Attribute(W.hAnsi), lowerPriorityFont.Attribute(W.hAnsiTheme) }, - (higherPriorityFont.Attribute(W.eastAsia) != null || higherPriorityFont.Attribute(W.eastAsiaTheme) != null) ? + higherPriorityFont.Attribute(W.eastAsia) != null || higherPriorityFont.Attribute(W.eastAsiaTheme) != null ? new[] { higherPriorityFont.Attribute(W.eastAsia), higherPriorityFont.Attribute(W.eastAsiaTheme) } : new[] { lowerPriorityFont.Attribute(W.eastAsia), lowerPriorityFont.Attribute(W.eastAsiaTheme) }, - (higherPriorityFont.Attribute(W.cs) != null || higherPriorityFont.Attribute(W.cstheme) != null) ? + higherPriorityFont.Attribute(W.cs) != null || higherPriorityFont.Attribute(W.cstheme) != null ? new[] { higherPriorityFont.Attribute(W.cs), higherPriorityFont.Attribute(W.cstheme) } : new[] { lowerPriorityFont.Attribute(W.cs), lowerPriorityFont.Attribute(W.cstheme) }, - (higherPriorityFont.Attribute(W.hint) != null ? higherPriorityFont.Attribute(W.hint) : - lowerPriorityFont.Attribute(W.hint)) + higherPriorityFont.Attribute(W.hint) != null ? higherPriorityFont.Attribute(W.hint) : + lowerPriorityFont.Attribute(W.hint) ); return rFonts; @@ -2108,14 +2392,14 @@ private static void AnnotateParagraphs(FormattingAssemblerInfo fai, Wordprocessi private static void AnnotateParagraph(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XElement para, FormattingAssemblerSettings settings) { - XElement localParaProps = para.Element(W.pPr); + var localParaProps = para.Element(W.pPr); if (localParaProps == null) { localParaProps = new XElement(W.pPr); } // get para table props, to be merged. - XElement tablepPr = null; + XElement? tablepPr = null; var blockLevelContentContainer = para .Ancestors() @@ -2128,10 +2412,10 @@ private static void AnnotateParagraph(FormattingAssemblerInfo fai, Wordprocessin a.Name == W.endnote); if (blockLevelContentContainer.Name == W.tbl) { - XElement tbl = blockLevelContentContainer; - XElement style = tbl.Element(PtOpenXml.pt + "style"); - XElement cellCnf = para.Ancestors(W.tc).Take(1).Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault(); - XElement rowCnf = para.Ancestors(W.tr).Take(1).Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); + var tbl = blockLevelContentContainer; + var style = tbl.Element(PtOpenXml.pt + "style"); + var cellCnf = para.Ancestors(W.tc).Take(1).Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault(); + var rowCnf = para.Ancestors(W.tr).Take(1).Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); if (style != null) { @@ -2139,21 +2423,22 @@ private static void AnnotateParagraph(FormattingAssemblerInfo fai, Wordprocessin // add each of these to the table, in PowerTools namespace. tablepPr = style.Element(W.pPr); if (tablepPr == null) + { tablepPr = new XElement(W.pPr); + } foreach (var ot in TableStyleOverrideTypes) { - XName attName = TableStyleOverrideXNameMap[ot]; - if ((cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true) || - (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true)) + var attName = TableStyleOverrideXNameMap[ot]; + if (cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true || + rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true) { - XElement o = style + var o = style .Elements(W.tblStylePr) - .Where(tsp => (string)tsp.Attribute(W.type) == ot) - .FirstOrDefault(); +.FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == ot); if (o != null) { - XElement otpPr = o.Element(W.pPr); + var otpPr = o.Element(W.pPr); tablepPr = MergeStyleElement(otpPr, tablepPr); } } @@ -2161,38 +2446,50 @@ private static void AnnotateParagraph(FormattingAssemblerInfo fai, Wordprocessin } } var stylesPart = wDoc.MainDocumentPart.StyleDefinitionsPart; - XDocument sXDoc = null; + XDocument? sXDoc = null; if (stylesPart != null) + { sXDoc = stylesPart.GetXDocument(); + } - ListItemRetriever.ListItemInfo lif = para.Annotation(); + var lif = para.Annotation(); - XElement rolledParaProps = ParagraphStyleRollup(para, sXDoc, fai.DefaultParagraphStyleName); + var rolledParaProps = ParagraphStyleRollup(para, sXDoc, fai.DefaultParagraphStyleName); if (lif != null && lif.IsZeroNumId) + { rolledParaProps.Elements(W.ind).Remove(); - XElement toggledParaProps = MergeStyleElement(rolledParaProps, tablepPr); - XElement mergedParaProps = MergeStyleElement(localParaProps, toggledParaProps); + } + + var toggledParaProps = MergeStyleElement(rolledParaProps, tablepPr); + var mergedParaProps = MergeStyleElement(localParaProps, toggledParaProps); - string li = ListItemRetriever.RetrieveListItem(wDoc, para, settings.ListItemRetrieverSettings); + var li = ListItemRetriever.RetrieveListItem(wDoc, para, settings.ListItemRetrieverSettings); if (lif != null && lif.IsListItem) { if (settings.RestrictToSupportedNumberingFormats) { - string numFmtForLevel = (string)lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(W.numFmt).Attributes(W.val).FirstOrDefault(); + var numFmtForLevel = (string)lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(W.numFmt).Attributes(W.val).FirstOrDefault(); if (numFmtForLevel == null) { var numFmtElement = lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(MC.AlternateContent).Elements(MC.Choice).Elements(W.numFmt).FirstOrDefault(); if (numFmtElement != null && (string)numFmtElement.Attribute(W.val) == "custom") + { numFmtForLevel = (string)numFmtElement.Attribute(W.format); + } } - bool isLgl = lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(W.isLgl).Any(); + var isLgl = lif.Lvl(ListItemRetriever.GetParagraphLevel(para)).Elements(W.isLgl).Any(); if (isLgl && numFmtForLevel != "decimalZero") + { numFmtForLevel = "decimal"; + } + if (!AcceptableNumFormats.Contains(numFmtForLevel)) + { throw new UnsupportedNumberingFormatException(numFmtForLevel + " is not a supported numbering format"); + } } - int paragraphLevel = ListItemRetriever.GetParagraphLevel(para); + var paragraphLevel = ListItemRetriever.GetParagraphLevel(para); var numberingParaProps = lif .Lvl(paragraphLevel) .Elements(W.pPr) @@ -2217,7 +2514,7 @@ private static void AnnotateParagraph(FormattingAssemblerInfo fai, Wordprocessin // if a paragraph contains a numPr with a numId=0, in other words, it is NOT a numbered item, then the indentation from the style // hierarchy is ignored. - ListItemRetriever.ListItemInfo lii = para.Annotation(); + var lii = para.Annotation(); if (lii.FromParagraph != null) { // order @@ -2246,8 +2543,8 @@ private static void AnnotateParagraph(FormattingAssemblerInfo fai, Wordprocessin // merge mergedParaProps with existing accumulatedParaProps, with mergedParaProps as high pri // replace accumulatedParaProps with newly merged - XElement accumulatedParaProps = para.Element(PtOpenXml.pt + "pPr"); - XElement newAccumulatedParaProps = MergeStyleElement(mergedParaProps, accumulatedParaProps); + var accumulatedParaProps = para.Element(PtOpenXml.pt + "pPr"); + var newAccumulatedParaProps = MergeStyleElement(mergedParaProps, accumulatedParaProps); AdjustFontAttributes(wDoc, para, newAccumulatedParaProps, newAccumulatedParaProps.Element(W.rPr), settings); newAccumulatedParaProps.Name = PtOpenXml.pt + "pPr"; @@ -2261,7 +2558,7 @@ private static void AnnotateParagraph(FormattingAssemblerInfo fai, Wordprocessin } } - private static string[] AcceptableNumFormats = new[] { + private static readonly string[] AcceptableNumFormats = new[] { "decimal", "decimalZero", "upperRoman", @@ -2284,10 +2581,16 @@ public static XElement ParagraphStyleRollup(XElement paragraph, XDocument styles .Attributes(W.val) .FirstOrDefault(); if (paraStyle == null) + { paraStyle = defaultParagraphStyleName; + } + var rolledUpParaStyleParaProps = new XElement(W.pPr); if (stylesXDoc == null) + { return rolledUpParaStyleParaProps; + } + if (paraStyle != null) { rolledUpParaStyleParaProps = ParaStyleParaPropsStack(stylesXDoc, paraStyle, paragraph) @@ -2305,11 +2608,14 @@ public static XElement ParagraphStyleRollup(XElement paragraph, XDocument styles private static IEnumerable ParaStyleParaPropsStack(XDocument stylesXDoc, string paraStyleName, XElement para) { if (stylesXDoc == null) + { yield break; + } + var localParaStyleName = paraStyleName; while (localParaStyleName != null) { - XElement paraStyle = stylesXDoc.Root.Elements(W.style).FirstOrDefault(s => + var paraStyle = stylesXDoc.Root.Elements(W.style).FirstOrDefault(s => s.Attribute(W.type).Value == "paragraph" && s.Attribute(W.styleId).Value == localParaStyleName); if (paraStyle == null) @@ -2324,10 +2630,10 @@ private static IEnumerable ParaStyleParaPropsStack(XDocument stylesXDo paraStyle.Element(W.rPr)); yield return elementToYield2; } - localParaStyleName = (string)(paraStyle + localParaStyleName = (string)paraStyle .Elements(W.basedOn) .Attributes(W.val) - .FirstOrDefault()); + .FirstOrDefault(); continue; } @@ -2335,22 +2641,25 @@ private static IEnumerable ParaStyleParaPropsStack(XDocument stylesXDo paraStyle.Element(W.pPr).Attributes(), paraStyle.Element(W.pPr).Elements(), paraStyle.Element(W.rPr)); - yield return (elementToYield); + yield return elementToYield; var listItemInfo = para.Annotation(); if (listItemInfo != null) { if (listItemInfo.IsListItem) { - XElement lipPr = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(para)).Element(W.pPr); + var lipPr = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(para)).Element(W.pPr); if (lipPr == null) + { lipPr = new XElement(W.pPr); - XElement lirPr = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(para)).Element(W.rPr); + } + + var lirPr = listItemInfo.Lvl(ListItemRetriever.GetParagraphLevel(para)).Element(W.rPr); var elementToYield2 = new XElement(W.pPr, lipPr.Attributes(), lipPr.Elements(), lirPr); - yield return (elementToYield2); + yield return elementToYield2; } } @@ -2377,7 +2686,7 @@ private static void AnnotateRuns(FormattingAssemblerInfo fai, WordprocessingDocu private static void AnnotateRunProperties(FormattingAssemblerInfo fai, WordprocessingDocument wDoc, XElement runOrPara, FormattingAssemblerSettings settings) { - XElement localRunProps = null; + XElement? localRunProps = null; if (runOrPara.Name == W.p) { var rPr = runOrPara.Elements(W.pPr).Elements(W.rPr).FirstOrDefault(); @@ -2396,7 +2705,7 @@ private static void AnnotateRunProperties(FormattingAssemblerInfo fai, Wordproce } // get run table props, to be merged. - XElement tablerPr = null; + XElement? tablerPr = null; var blockLevelContentContainer = runOrPara .Ancestors() .FirstOrDefault(a => a.Name == W.body || @@ -2408,44 +2717,48 @@ private static void AnnotateRunProperties(FormattingAssemblerInfo fai, Wordproce a.Name == W.endnote); if (blockLevelContentContainer.Name == W.tbl) { - XElement tbl = blockLevelContentContainer; - XElement style = tbl.Element(PtOpenXml.pt + "style"); - XElement cellCnf = runOrPara.Ancestors(W.tc).Take(1).Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault(); - XElement rowCnf = runOrPara.Ancestors(W.tr).Take(1).Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); + var tbl = blockLevelContentContainer; + var style = tbl.Element(PtOpenXml.pt + "style"); + var cellCnf = runOrPara.Ancestors(W.tc).Take(1).Elements(W.tcPr).Elements(W.cnfStyle).FirstOrDefault(); + var rowCnf = runOrPara.Ancestors(W.tr).Take(1).Elements(W.trPr).Elements(W.cnfStyle).FirstOrDefault(); if (style != null) { tablerPr = style.Element(W.rPr); if (tablerPr == null) + { tablerPr = new XElement(W.rPr); + } foreach (var ot in TableStyleOverrideTypes) { - XName attName = TableStyleOverrideXNameMap[ot]; - if ((cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true) || - (rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true)) + var attName = TableStyleOverrideXNameMap[ot]; + if (cellCnf != null && cellCnf.Attribute(attName).ToBoolean() == true || + rowCnf != null && rowCnf.Attribute(attName).ToBoolean() == true) { - XElement o = style + var o = style .Elements(W.tblStylePr) - .Where(tsp => (string)tsp.Attribute(W.type) == ot) - .FirstOrDefault(); +.FirstOrDefault(tsp => (string)tsp.Attribute(W.type) == ot); if (o != null) { - XElement otrPr = o.Element(W.rPr); + var otrPr = o.Element(W.rPr); tablerPr = MergeStyleElement(otrPr, tablerPr); } } } } } - XElement rolledRunProps = CharStyleRollup(fai, wDoc, runOrPara); + var rolledRunProps = CharStyleRollup(fai, wDoc, runOrPara); var toggledRunProps = ToggleMergeRunProps(rolledRunProps, tablerPr); var currentRunProps = runOrPara.Element(PtOpenXml.rPr); // this is already stored on the run from previous aggregation of props var mergedRunProps = MergeStyleElement(toggledRunProps, currentRunProps); var newMergedRunProps = MergeStyleElement(localRunProps, mergedRunProps); - XElement pPr = null; + XElement? pPr = null; if (runOrPara.Name == W.p) + { pPr = runOrPara.Element(PtOpenXml.pPr); + } + AdjustFontAttributes(wDoc, runOrPara, pPr, newMergedRunProps, settings); newMergedRunProps.Name = PtOpenXml.rPr; @@ -2463,19 +2776,21 @@ private static XElement CharStyleRollup(FormattingAssemblerInfo fai, Wordprocess { var sXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); - string charStyle = null; - string paraStyle = null; - XElement rPr = null; - XElement pPr = null; - XElement pStyle = null; - XElement rStyle = null; - CachedParaInfo cpi = null; // CachedParaInfo is an optimization for the case where a paragraph contains thousands of runs. + string? charStyle = null; + string? paraStyle = null; + XElement? rPr = null; + XElement? pPr = null; + XElement? pStyle = null; + XElement? rStyle = null; + CachedParaInfo? cpi = null; // CachedParaInfo is an optimization for the case where a paragraph contains thousands of runs. if (runOrPara.Name == W.p) { cpi = runOrPara.Annotation(); if (cpi != null) + { pPr = cpi.ParagraphProperties; + } else { pPr = runOrPara.Element(W.pPr); @@ -2513,6 +2828,7 @@ private static XElement CharStyleRollup(FormattingAssemblerInfo fai, Wordprocess else { if (runOrPara.Name == W.r) + { charStyle = (string)runOrPara .Ancestors(W.p) .Take(1) @@ -2520,12 +2836,15 @@ private static XElement CharStyleRollup(FormattingAssemblerInfo fai, Wordprocess .Elements(W.pStyle) .Attributes(W.val) .FirstOrDefault(); + } else + { charStyle = (string)runOrPara .Elements(W.pPr) .Elements(W.pStyle) .Attributes(W.val) .FirstOrDefault(); + } } } @@ -2536,9 +2855,13 @@ private static XElement CharStyleRollup(FormattingAssemblerInfo fai, Wordprocess var ancestorPara = runOrPara.Ancestors(W.p).First(); cpi = ancestorPara.Annotation(); if (cpi != null) + { charStyle = cpi.ParagraphStyleName; + } else + { charStyle = (string)runOrPara.Ancestors(W.p).First().Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault(); + } } if (charStyle == null) { @@ -2547,7 +2870,7 @@ private static XElement CharStyleRollup(FormattingAssemblerInfo fai, Wordprocess } // A run always must have an ancestor paragraph. - XElement para = null; + XElement? para = null; var rolledUpParaStyleRunProps = new XElement(W.rPr); if (runOrPara.Name == W.r) { @@ -2580,17 +2903,21 @@ private static XElement CharStyleRollup(FormattingAssemblerInfo fai, Wordprocess } } else + { paraStyle = fai.DefaultParagraphStyleName; + } - string key = (paraStyle == null ? "[null]" : paraStyle) + "~|~" + + var key = (paraStyle == null ? "[null]" : paraStyle) + "~|~" + (charStyle == null ? "[null]" : charStyle); - XElement rolledRunProps = null; + XElement? rolledRunProps = null; if (fai.RolledCharacterStyles.ContainsKey(key)) + { rolledRunProps = fai.RolledCharacterStyles[key]; + } else { - XElement rolledUpCharStyleRunProps = new XElement(W.rPr); + var rolledUpCharStyleRunProps = new XElement(W.rPr); if (charStyle != null) { rolledUpCharStyleRunProps = @@ -2656,7 +2983,7 @@ private static IEnumerable CharStyleStack(WordprocessingDocument wDoc, var rValue = new Stack(); while (localCharStyleName != null) { - XElement basedOn = null; + XElement? basedOn = null; // first look for character style var charStyle = sXDoc.Root.Elements(W.style).FirstOrDefault(s => { @@ -2701,9 +3028,14 @@ private static IEnumerable CharStyleStack(WordprocessingDocument wDoc, private static XElement ToggleMergeRunProps(XElement higherPriorityElement, XElement lowerPriorityElement) { if (lowerPriorityElement == null) + { return higherPriorityElement; + } + if (higherPriorityElement == null) + { return lowerPriorityElement; + } var hpe = higherPriorityElement.Elements().Select(e => e.Name).ToArray(); @@ -2764,7 +3096,7 @@ private static XElement ToggleMergeRunProps(XElement higherPriorityElement, XEle return newMergedElement; } - private static XName[] TogglePropertyNames = new[] { + private static readonly XName[] TogglePropertyNames = new[] { W.b, W.bCs, W.caps, @@ -2779,7 +3111,7 @@ private static XElement ToggleMergeRunProps(XElement higherPriorityElement, XEle W.vanish }; - private static XName[] PropertyNames = new[] { + private static readonly XName[] PropertyNames = new[] { W.cs, W.rtl, W.u, @@ -2790,60 +3122,63 @@ private static XElement ToggleMergeRunProps(XElement higherPriorityElement, XEle public class CharStyleAttributes { - public string AsciiFont; - public string HAnsiFont; - public string EastAsiaFont; - public string CsFont; - public string Hint; - public bool Rtl; + public string? AsciiFont { get; set; } + public string? HAnsiFont { get; set; } + public string? EastAsiaFont { get; set; } + public string? CsFont { get; set; } + public string? Hint { get; set; } + public bool Rtl { get; set; } - public string LatinLang; - public string BidiLang; - public string EastAsiaLang; + public string LatinLang { get; set; } + public string BidiLang { get; set; } + public string EastAsiaLang { get; set; } - public Dictionary ToggleProperties; - public Dictionary Properties; + public Dictionary ToggleProperties { get; set; } + public Dictionary Properties { get; set; } - public CharStyleAttributes(XElement pPr, XElement rPr) + public CharStyleAttributes(XElement? pPr, XElement? rPr) { ToggleProperties = new Dictionary(); Properties = new Dictionary(); if (rPr == null) + { return; - foreach (XName xn in TogglePropertyNames) + } + + foreach (var xn in TogglePropertyNames) { ToggleProperties[xn] = GetBoolProperty(rPr, xn); } - foreach (XName xn in PropertyNames) + foreach (var xn in PropertyNames) { Properties[xn] = GetXmlProperty(rPr, xn); } var rFonts = rPr.Element(W.rFonts); if (rFonts == null) { - this.AsciiFont = null; - this.HAnsiFont = null; - this.EastAsiaFont = null; - this.CsFont = null; - this.Hint = null; + AsciiFont = null; + HAnsiFont = null; + EastAsiaFont = null; + CsFont = null; + Hint = null; } else { - this.AsciiFont = (string)(rFonts.Attribute(W.ascii)); - this.HAnsiFont = (string)(rFonts.Attribute(W.hAnsi)); - this.EastAsiaFont = (string)(rFonts.Attribute(W.eastAsia)); - this.CsFont = (string)(rFonts.Attribute(W.cs)); - this.Hint = (string)(rFonts.Attribute(W.hint)); + AsciiFont = (string)rFonts.Attribute(W.ascii); + HAnsiFont = (string)rFonts.Attribute(W.hAnsi); + EastAsiaFont = (string)rFonts.Attribute(W.eastAsia); + CsFont = (string)rFonts.Attribute(W.cs); + Hint = (string)rFonts.Attribute(W.hint); } - XElement csel = this.Properties[W.cs]; - bool cs = csel != null && (csel.Attribute(W.val) == null || csel.Attribute(W.val).ToBoolean() == true); - XElement rtlel = this.Properties[W.rtl]; - bool rtl = rtlel != null && (rtlel.Attribute(W.val) == null || rtlel.Attribute(W.val).ToBoolean() == true); + var csel = Properties[W.cs]; + var cs = csel != null && (csel.Attribute(W.val) == null || csel.Attribute(W.val).ToBoolean() == true); + var rtlel = Properties[W.rtl]; + var rtl = rtlel != null && (rtlel.Attribute(W.val) == null || rtlel.Attribute(W.val).ToBoolean() == true); var bidi = false; if (pPr != null) { - XElement bidiel = pPr.Element(W.bidi); + var bidiel = pPr.Element(W.bidi); bidi = bidiel != null && (bidiel.Attribute(W.val) == null || bidiel.Attribute(W.val).ToBoolean() == true); } Rtl = cs || rtl || bidi; @@ -2859,23 +3194,47 @@ public CharStyleAttributes(XElement pPr, XElement rPr) private static bool? GetBoolProperty(XElement rPr, XName propertyName) { if (rPr.Element(propertyName) == null) + { return null; + } + var s = (string)rPr.Element(propertyName).Attribute(W.val); if (s == null) + { return true; + } + if (s == "1") + { return true; + } + if (s == "0") + { return false; + } + if (s == "true") + { return true; + } + if (s == "false") + { return false; + } + if (s == "on") + { return true; + } + if (s == "off") + { return false; - return (bool)(rPr.Element(propertyName).Attribute(W.val)); + } + + return (bool)rPr.Element(propertyName).Attribute(W.val); } private static XElement GetXmlProperty(XElement rPr, XName propertyName) @@ -2883,7 +3242,7 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) return rPr.Element(propertyName); } - private static XName[] TogglePropertyNames = new[] { + private static readonly XName[] TogglePropertyNames = new[] { W.b, W.bCs, W.caps, @@ -2898,7 +3257,7 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) W.vanish }; - private static XName[] PropertyNames = new[] { + private static readonly XName[] PropertyNames = new[] { W.cs, W.rtl, W.u, @@ -2906,10 +3265,9 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) W.highlight, W.shd }; - } - private static HashSet WeakAndNeutralDirectionalCharacters = new HashSet() { + private static readonly HashSet WeakAndNeutralDirectionalCharacters = new HashSet() { '0', '1', '2', @@ -2936,7 +3294,7 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) '\x066B', // arabic decimal separator '\x066C', // arabic thousands separator - '\x0627', // arabic pipe + '\x0627', // arabic pipe '\x20A0', // start currency symbols '\x20A1', @@ -3010,16 +3368,18 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) '\x06F9', }; - private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement paraOrRun, XElement pPr, + private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement paraOrRun, XElement? pPr, XElement rPr, FormattingAssemblerSettings settings) { - XDocument themeXDoc = null; + XDocument? themeXDoc = null; if (wDoc.MainDocumentPart.ThemePart != null) + { themeXDoc = wDoc.MainDocumentPart.ThemePart.GetXDocument(); + } - XElement fontScheme = null; - XElement majorFont = null; - XElement minorFont = null; + XElement? fontScheme = null; + XElement? majorFont = null; + XElement? minorFont = null; if (themeXDoc != null) { fontScheme = themeXDoc.Root.Element(A.themeElements).Element(A.fontScheme); @@ -3035,15 +3395,15 @@ private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement p var hAnsiTheme = (string)rFonts.Attribute(W.hAnsiTheme); var eastAsiaTheme = (string)rFonts.Attribute(W.eastAsiaTheme); var cstheme = (string)rFonts.Attribute(W.cstheme); - string ascii = null; - string hAnsi = null; - string eastAsia = null; - string cs = null; + string? ascii = null; + string? hAnsi = null; + string? eastAsia = null; + string? cs = null; - XElement minorLatin = null; - string minorLatinTypeface = null; - XElement majorLatin = null; - string majorLatinTypeface = null; + XElement? minorLatin = null; + string? minorLatinTypeface = null; + XElement? majorLatin = null; + string? majorLatinTypeface = null; if (minorFont != null) { @@ -3119,14 +3479,18 @@ private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement p } var firstTextNode = paraOrRun.Descendants(W.t).FirstOrDefault(t => t.Value.Length > 0); - string str = " "; + var str = " "; // if there is a run with no text in it, then no need to do any of the rest of this method. if (firstTextNode == null && paraOrRun.Name == W.r) + { return; + } if (firstTextNode != null) + { str = firstTextNode.Value; + } var csa = new CharStyleAttributes(pPr, rPr); @@ -3135,55 +3499,43 @@ private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement p // However, Word breaks up runs that use more than one font into multiple runs. Other producers of WordprocessingML may not, so in // that case, this routine may need to be augmented to look at all characters in a run. - /* - old code - var fontFamilies = str.select(function (c) { - var ft = Pav.DetermineFontTypeFromCharacter(c, csa); - switch (ft) { - case Pav.FontType.Ascii: - return cast(rFonts.attribute(W.ascii)); - case Pav.FontType.HAnsi: - return cast(rFonts.attribute(W.hAnsi)); - case Pav.FontType.EastAsia: - return cast(rFonts.attribute(W.eastAsia)); - case Pav.FontType.CS: - return cast(rFonts.attribute(W.cs)); - default: - return null; - } - }) - .where(function (f) { return f != null && f != ""; }) - .distinct() - .select(function (f) { return new Pav.FontFamily(f); }) - .toArray(); - */ - - var charToExamine = str.FirstOrDefault(c => ! WeakAndNeutralDirectionalCharacters.Contains(c)); + var charToExamine = str.FirstOrDefault(c => !WeakAndNeutralDirectionalCharacters.Contains(c)); if (charToExamine == '\0') + { charToExamine = str[0]; + } var ft = DetermineFontTypeFromCharacter(charToExamine, csa); - string fontType = null; - string languageType = null; + string? fontType = null; + string? languageType = null; switch (ft) { case FontType.Ascii: fontType = (string)rFonts.Attribute(W.ascii); languageType = "western"; break; + case FontType.HAnsi: fontType = (string)rFonts.Attribute(W.hAnsi); languageType = "western"; break; + case FontType.EastAsia: if (settings.RestrictToSupportedLanguages) + { throw new UnsupportedLanguageException("EastAsia languages are not supported"); + } + fontType = (string)rFonts.Attribute(W.eastAsia); languageType = "eastAsia"; break; + case FontType.CS: if (settings.RestrictToSupportedLanguages) + { throw new UnsupportedLanguageException("Complex script (RTL) languages are not supported"); + } + fontType = (string)rFonts.Attribute(W.cs); languageType = "bidi"; break; @@ -3193,7 +3545,7 @@ old code { if (paraOrRun.Attribute(PtOpenXml.FontName) == null) { - XAttribute fta = new XAttribute(PtOpenXml.FontName, fontType.ToString()); + var fta = new XAttribute(PtOpenXml.FontName, fontType.ToString()); paraOrRun.Add(fta); } else @@ -3205,7 +3557,7 @@ old code { if (paraOrRun.Attribute(PtOpenXml.LanguageType) == null) { - XAttribute lta = new XAttribute(PtOpenXml.LanguageType, languageType); + var lta = new XAttribute(PtOpenXml.LanguageType, languageType); paraOrRun.Add(lta); } else @@ -3266,27 +3618,24 @@ public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttribut ch == 0xAA || ch == 0xAD || ch == 0xAF || - (ch >= 0xB0 && ch <= 0xB4) || - (ch >= 0xB6 && ch <= 0xBA) || - (ch >= 0xBC && ch <= 0xBF) || + ch >= 0xB0 && ch <= 0xB4 || + ch >= 0xB6 && ch <= 0xBA || + ch >= 0xBC && ch <= 0xBF || ch == 0xD7 || ch == 0xF7) { return FontType.EastAsia; } - if (csa.EastAsiaLang == "zh-hant" || - csa.EastAsiaLang == "zh-hans") - { - if (ch == 0xE0 || + if ((csa.EastAsiaLang == "zh-hant" || + csa.EastAsiaLang == "zh-hans") && (ch == 0xE0 || ch == 0xE1 || - (ch >= 0xE8 && ch <= 0xEA) || - (ch >= 0xEC && ch <= 0xED) || - (ch >= 0xF2 && ch <= 0xF3) || - (ch >= 0xF9 && ch <= 0xFA) || - ch == 0xFC) - { - return FontType.EastAsia; - } + ch >= 0xE8 && ch <= 0xEA || + ch >= 0xEC && ch <= 0xED || + ch >= 0xF2 && ch <= 0xF3 || + ch >= 0xF9 && ch <= 0xFA || + ch == 0xFC)) + { + return FontType.EastAsia; } } return FontType.HAnsi; @@ -3295,14 +3644,11 @@ public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttribut // Unicode Block: Latin Extended-A if (ch >= 0x0100 && ch <= 0x017F) { - if (csa.Hint == "eastAsia") + if (csa.Hint == "eastAsia" && (csa.EastAsiaLang == "zh-hant" || + csa.EastAsiaLang == "zh-hans") +) { - if (csa.EastAsiaLang == "zh-hant" || - csa.EastAsiaLang == "zh-hans" - /* || the character set of the east Asia (or east Asia theme) font is Chinese5 || GB2312 todo */) - { - return FontType.EastAsia; - } + return FontType.EastAsia; } return FontType.HAnsi; } @@ -3735,9 +4081,14 @@ public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttribut if (csa.Hint == "eastAsia") { if (ch >= 0xFB00 && ch <= 0xFB1C) + { return FontType.EastAsia; + } + if (ch >= 0xFB1D && ch <= 0xFB4F) + { return FontType.Ascii; + } } return FontType.HAnsi; } @@ -3780,6 +4131,7 @@ private class FormattingAssemblerInfo public string DefaultCharacterStyleName; public string DefaultTableStyleName; public Dictionary RolledCharacterStyles; + public FormattingAssemblerInfo() { RolledCharacterStyles = new Dictionary(); @@ -3790,17 +4142,37 @@ public FormattingAssemblerInfo() private class CachedParaInfo { public string ParagraphStyleName; - public XElement ParagraphProperties; + public XElement? ParagraphProperties; } public class UnsupportedNumberingFormatException : Exception { - public UnsupportedNumberingFormatException(string message) : base(message) { } + public UnsupportedNumberingFormatException(string message) : base(message) + { + } + + public UnsupportedNumberingFormatException(string message, Exception innerException) : base(message, innerException) + { + } + + public UnsupportedNumberingFormatException() + { + } } public class UnsupportedLanguageException : Exception { - public UnsupportedLanguageException(string message) : base(message) { } + public UnsupportedLanguageException(string message) : base(message) + { + } + + public UnsupportedLanguageException(string message, Exception innerException) : base(message, innerException) + { + } + + public UnsupportedLanguageException() + { + } } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/GetListItemText_fr_FR.cs b/OpenXmlPowerTools/GetListItemText_fr_FR.cs index e7761a5f..a0af65b3 100644 --- a/OpenXmlPowerTools/GetListItemText_fr_FR.cs +++ b/OpenXmlPowerTools/GetListItemText_fr_FR.cs @@ -1,58 +1,53 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class ListItemTextGetter_fr_FR { - private static string[] OneThroughNineteen = { + private static readonly string[] OneThroughNineteen = { "", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf" }; - private static string[] Tens = { + private static readonly string[] Tens = { "", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante-dix", "quatre-vingt", "quatre-vingt-dix" }; - private static string[] OrdinalOneThroughNineteen = { + private static readonly string[] OrdinalOneThroughNineteen = { "", "unième", "deuxième", "troisième", "quatrième", "cinquième", "sixième", "septième", "huitième", "neuvième", "dixième", "onzième", "douzième", "treizième", "quatorzième", "quinzième", "seizième", "dix-septième", "dix-huitième", "dix-neuvième" }; - private static string[] OrdinalTenths = { + private static readonly string[] OrdinalTenths = { "", "dixième", "vingtième", "trentième", "quarantième", "cinquantième", "soixantième", "soixante-dixième", "quatre-vingtième", "quatre-vingt-dixième" }; - private static string[] OrdinalTenthsPlus = { + private static readonly string[] OrdinalTenthsPlus = { "", "", "vingt", "trente", "quarante", "cinquante", "soixante", "", "quatre-vingt", "" }; - public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) + public static string? GetListItemText(int levelNumber, string numFmt) { if (numFmt == "cardinalText") { - string result = ""; + var result = ""; var sLevel = (levelNumber + 10000).ToString(); - int thousands = int.Parse(sLevel.Substring(1, 1)); - int hundreds = int.Parse(sLevel.Substring(2, 1)); - int tens = int.Parse(sLevel.Substring(3, 1)); - int ones = int.Parse(sLevel.Substring(4, 1)); + var thousands = int.Parse(sLevel.Substring(1, 1)); + var hundreds = int.Parse(sLevel.Substring(2, 1)); + var tens = int.Parse(sLevel.Substring(3, 1)); + var ones = int.Parse(sLevel.Substring(4, 1)); /* exact thousands */ if (levelNumber == 1000) + { return "Mille"; + } + if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) { result = OneThroughNineteen[thousands] + " mille"; @@ -61,17 +56,26 @@ public static string GetListItemText(string languageCultureName, int levelNumber /* > 1000 */ if (levelNumber > 1000 && levelNumber < 2000) + { result = "mille "; + } else if (levelNumber > 2000 && levelNumber < 10000) + { result = OneThroughNineteen[thousands] + " mille "; + } /* exact hundreds */ if (hundreds > 0 && tens == 0 && ones == 0) { if (hundreds == 1) + { result += "cent"; + } else + { result += OneThroughNineteen[hundreds] + " cents"; + } + return result.Substring(0, 1).ToUpper() + result.Substring(1); } @@ -79,18 +83,27 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (hundreds > 0) { if (hundreds == 1) + { result += "cent "; + } else + { result += OneThroughNineteen[hundreds] + " cent "; + } } /* exact tens */ if (tens > 0 && ones == 0) { if (tens == 8) + { result += "quatre-vingts"; + } else + { result += Tens[tens]; + } + return result.Substring(0, 1).ToUpper() + result.Substring(1); } @@ -98,9 +111,14 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (tens == 7) { if (ones == 1) + { result += Tens[6] + " et " + OneThroughNineteen[ones + 10]; + } else + { result += Tens[6] + "-" + OneThroughNineteen[ones + 10]; + } + return result.Substring(0, 1).ToUpper() + result.Substring(1); } @@ -114,9 +132,14 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (tens >= 2) { if (ones == 1 && tens != 8 && tens != 9) + { result += Tens[tens] + " et un"; + } else + { result += Tens[tens] + "-" + OneThroughNineteen[ones]; + } + return result.Substring(0, 1).ToUpper() + result.Substring(1); } @@ -133,27 +156,37 @@ public static string GetListItemText(string languageCultureName, int levelNumber { string suffix; if (levelNumber == 1) + { suffix = "er"; + } else + { suffix = "e"; + } + return levelNumber.ToString() + suffix; } if (numFmt == "ordinalText") { - string result = ""; + var result = ""; if (levelNumber == 1) + { return "Premier"; + } var sLevel = (levelNumber + 10000).ToString(); - int thousands = int.Parse(sLevel.Substring(1, 1)); - int hundreds = int.Parse(sLevel.Substring(2, 1)); - int tens = int.Parse(sLevel.Substring(3, 1)); - int ones = int.Parse(sLevel.Substring(4, 1)); + var thousands = int.Parse(sLevel.Substring(1, 1)); + var hundreds = int.Parse(sLevel.Substring(2, 1)); + var tens = int.Parse(sLevel.Substring(3, 1)); + var ones = int.Parse(sLevel.Substring(4, 1)); /* exact thousands */ if (levelNumber == 1000) + { return "Millième"; + } + if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) { result = OneThroughNineteen[thousands] + " millième"; @@ -162,17 +195,26 @@ public static string GetListItemText(string languageCultureName, int levelNumber /* > 1000 */ if (levelNumber > 1000 && levelNumber < 2000) + { result = "mille "; + } else if (levelNumber > 2000 && levelNumber < 10000) + { result = OneThroughNineteen[thousands] + " mille "; + } /* exact hundreds */ if (hundreds > 0 && tens == 0 && ones == 0) { if (hundreds == 1) + { result += "centième"; + } else + { result += OneThroughNineteen[hundreds] + " centième"; + } + return result.Substring(0, 1).ToUpper() + result.Substring(1); } @@ -180,9 +222,13 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (hundreds > 0) { if (hundreds == 1) + { result += "cent "; + } else + { result += OneThroughNineteen[hundreds] + " cent "; + } } /* exact tens */ @@ -196,9 +242,14 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (tens == 7) { if (ones == 1) + { result += OrdinalTenthsPlus[6] + " et " + OrdinalOneThroughNineteen[ones + 10]; + } else + { result += OrdinalTenthsPlus[6] + "-" + OrdinalOneThroughNineteen[ones + 10]; + } + return result.Substring(0, 1).ToUpper() + result.Substring(1); } @@ -212,9 +263,14 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (tens >= 2) { if (ones == 1 && tens != 8 && tens != 9) + { result += OrdinalTenthsPlus[tens] + " et unième"; + } else + { result += OrdinalTenthsPlus[tens] + "-" + OrdinalOneThroughNineteen[ones]; + } + return result.Substring(0, 1).ToUpper() + result.Substring(1); } @@ -230,4 +286,4 @@ public static string GetListItemText(string languageCultureName, int levelNumber return null; } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/GetListItemText_ru_RU.cs b/OpenXmlPowerTools/GetListItemText_ru_RU.cs index ccf1c3f4..d0907eda 100644 --- a/OpenXmlPowerTools/GetListItemText_ru_RU.cs +++ b/OpenXmlPowerTools/GetListItemText_ru_RU.cs @@ -1,84 +1,101 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { - public class ListItemTextGetter_ru_RU + public class ListItemTextGetterRuRU { - private static string[] OneThroughNineteen = { + private static readonly string[] OneThroughNineteen = { "один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять", "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать", "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать" }; - private static string[] Tens = { + private static readonly string[] Tens = { "десять", "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", "восемьдесят", "девяносто" }; - private static string[] OrdinalOneThroughNineteen = { + private static readonly string[] OrdinalOneThroughNineteen = { "первый", "второй", "третий", "четвертый", "пятый", "шестой", "седьмой", "восьмой", "девятый", "десятый", "одиннадцатый", "двенадцатый", "тринадцатый", "четырнадцатый", "пятнадцатый", "шестнадцатый", "семнадцатый", "восемнадцатый", "девятнадцатый" }; - private static string[] OrdinalTenths = { + private static readonly string[] OrdinalTenths = { "десятый", "двадцатый", "тридцатый", "сороковой", "пятидесятый", "шестидесятый", "семидесятый", "восьмидесятый", "девяностый" }; // TODO this is not correct for values above 99 - public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) + public static string? GetListItemText(int levelNumber, string numFmt) { if (numFmt == "cardinalText") { - string result = ""; - int t1 = levelNumber / 1000; - int t2 = levelNumber % 1000; + var result = ""; + var t1 = levelNumber / 1000; + var t2 = levelNumber % 1000; if (t1 >= 1) + { result += OneThroughNineteen[t1 - 1] + " thousand"; + } + if (t1 >= 1 && t2 == 0) + { return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + if (t1 >= 1) + { result += " "; - int h1 = (levelNumber % 1000) / 100; - int h2 = levelNumber % 100; + } + + var h1 = levelNumber % 1000 / 100; + var h2 = levelNumber % 100; if (h1 >= 1) + { result += OneThroughNineteen[h1 - 1] + " hundred"; + } + if (h1 >= 1 && h2 == 0) + { return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + if (h1 >= 1) + { result += " "; - int z = levelNumber % 100; + } + + var z = levelNumber % 100; if (z <= 19) + { result += OneThroughNineteen[z - 1]; + } else { - int x = z / 10; - int r = z % 10; + var x = z / 10; + var r = z % 10; result += Tens[x - 1]; if (r >= 1) + { result += "-" + OneThroughNineteen[r - 1]; + } } return result.Substring(0, 1).ToUpper() + result.Substring(1); } if (numFmt == "ordinalText") { - string result = ""; - int t1 = levelNumber / 1000; - int t2 = levelNumber % 1000; + var result = ""; + var t1 = levelNumber / 1000; + var t2 = levelNumber % 1000; if (t1 >= 1 && t2 != 0) + { result += OneThroughNineteen[t1 - 1] + " thousand"; + } + if (t1 >= 1 && t2 == 0) { result += OneThroughNineteen[t1 - 1] + " thousandth"; @@ -86,11 +103,17 @@ public static string GetListItemText(string languageCultureName, int levelNumber result.Substring(1); } if (t1 >= 1) + { result += " "; - int h1 = (levelNumber % 1000) / 100; - int h2 = levelNumber % 100; + } + + var h1 = levelNumber % 1000 / 100; + var h2 = levelNumber % 100; if (h1 >= 1 && h2 != 0) + { result += OneThroughNineteen[h1 - 1] + " hundred"; + } + if (h1 >= 1 && h2 == 0) { result += OneThroughNineteen[h1 - 1] + " hundredth"; @@ -98,20 +121,32 @@ public static string GetListItemText(string languageCultureName, int levelNumber result.Substring(1); } if (h1 >= 1) + { result += " "; - int z = levelNumber % 100; + } + + var z = levelNumber % 100; if (z <= 19) + { result += OrdinalOneThroughNineteen[z - 1]; + } else { - int x = z / 10; - int r = z % 10; + var x = z / 10; + var r = z % 10; if (r == 0) + { result += OrdinalTenths[x - 1]; + } else + { result += Tens[x - 1]; + } + if (r >= 1) + { result += " " + OrdinalOneThroughNineteen[r - 1]; + } } return result.Substring(0, 1).ToUpper() + result.Substring(1); @@ -119,4 +154,4 @@ public static string GetListItemText(string languageCultureName, int levelNumber return null; } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/GetListItemText_sv_SE.cs b/OpenXmlPowerTools/GetListItemText_sv_SE.cs index 04a90e21..cdc81f9c 100644 --- a/OpenXmlPowerTools/GetListItemText_sv_SE.cs +++ b/OpenXmlPowerTools/GetListItemText_sv_SE.cs @@ -1,213 +1,265 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { - public class ListItemTextGetter_sv_SE - { - private static string[] OneThroughNineteen = { + public class ListItemTextGetter_sv_SE + { + private static readonly string[] OneThroughNineteen = { "", "ett", "två", "tre", "fyra", "fem", "sex", "sju", "åtta", "nio", "tio", "elva", "tolv", "tretton", "fjorton", "femton", "sexton", "sjutton", "arton", "nitton" }; - private static string[] Tens = { + private static readonly string[] Tens = { "","tio", "tjugo", "trettio", "fyrtio", "femtio", "sextio", "sjuttio", "åttio", "nittio", "etthundra" }; - private static string[] OrdinalOneThroughNineteen = { + private static readonly string[] OrdinalOneThroughNineteen = { "", "första", "andra", "tredje", "fjärde", "femte", "sjätte", "sjunde", "åttonde", "nionde", "tionde", "elfte", "tolfte", "trettonde", "fjortonde", "femtonde", "sextonde", "sjuttonde", "artonde", "nittonde" }; - public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) - { - switch (numFmt) - { - case "cardinalText": - return NumberAsCardinalText(languageCultureName, levelNumber, numFmt); - case "ordinalText": - return NumberAsOrdinalText(languageCultureName, levelNumber, numFmt); - case "ordinal": - return NumberAsOrdinal(languageCultureName, levelNumber, numFmt); - default: - return null; - } - } - private static string NumberAsCardinalText(string languageCultureName, int levelNumber, string numFmt) - { - string result = ""; - - var sLevel = (levelNumber + 10000).ToString(); - int thousands = int.Parse(sLevel.Substring(1, 1)); - int hundreds = int.Parse(sLevel.Substring(2, 1)); - int tens = int.Parse(sLevel.Substring(3, 1)); - int ones = int.Parse(sLevel.Substring(4, 1)); - - - //Validation - if (thousands > 19) - throw new ArgumentOutOfRangeException("levelNumber", "Convering a levelNumber to ordinal text that is greater then 19 999 is not supported"); - if (levelNumber == 0) - return "Noll"; - if (levelNumber < 0) - throw new ArgumentOutOfRangeException("levelNumber", "Converting a negative levelNumber to ordinal text is not supported"); - - /* exact thousands */ - if (levelNumber == 1000) - return "Ettusen"; - if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) - { - result = OneThroughNineteen[thousands] + "tusen"; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - - /* > 1000 */ - if (levelNumber > 1000 && levelNumber < 2000) - result = "ettusen"; - else if (levelNumber > 2000 && levelNumber < 10000) - result = OneThroughNineteen[thousands] + "tusen"; - - /* exact hundreds */ - if (hundreds > 0 && tens == 0 && ones == 0) - { - if (hundreds == 1) - result += "etthundra"; - else - result += OneThroughNineteen[hundreds] + "hundra"; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - - /* > 100 */ - if (hundreds > 0) - { - if (hundreds == 1) - result += "etthundra"; - else - result += OneThroughNineteen[hundreds] + "hundra"; - } - - /* exact tens */ - if (tens > 0 && ones == 0) - { - result += Tens[tens]; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - - /* > 20 */ - if (tens == 1) - { - result += OneThroughNineteen[tens * 10 + ones]; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - else if (tens > 1) - { - result += Tens[tens] + OneThroughNineteen[ones]; ; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - else - { - result += OneThroughNineteen[ones]; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - } - private static string NumberAsOrdinalText(string languageCultureName, int levelNumber, string numFmt) - { - string result = ""; - - if (levelNumber <= 0) - throw new ArgumentOutOfRangeException("levelNumber", "Converting a zero or negative levelNumber to ordinal text is not supported"); - if(levelNumber >= 10000) - throw new ArgumentOutOfRangeException("levelNumber", "Convering a levelNumber to ordinal text that is greater then 10000 is not supported"); - - if (levelNumber == 1) - return "Första"; - - var sLevel = (levelNumber + 10000).ToString(); - int thousands = int.Parse(sLevel.Substring(1, 1)); - int hundreds = int.Parse(sLevel.Substring(2, 1)); - int tens = int.Parse(sLevel.Substring(3, 1)); - int ones = int.Parse(sLevel.Substring(4, 1)); - - - /* exact thousands */ - if (levelNumber == 1000) - return "Ettusende"; - if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) - { - result = OneThroughNineteen[thousands] + "tusende"; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - - /* > 1000 */ - if (levelNumber > 1000 && levelNumber < 2000) - result = "ettusen"; - else if (levelNumber > 2000 && levelNumber < 10000) - result = OneThroughNineteen[thousands] + "tusende"; - - /* exact hundreds */ - if (hundreds > 0 && tens == 0 && ones == 0) - { - if (hundreds == 1) - result += "etthundrade"; - else - result += OneThroughNineteen[hundreds] + "hundrade"; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - - /* > 100 */ - if (hundreds > 0) - { - result += OneThroughNineteen[hundreds] + "hundra"; - } - - /* exact tens */ - if (tens > 0 && ones == 0) - { - result += Tens[tens] + "nde"; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - - /* > 20 */ - if (tens == 1) - { - result += OrdinalOneThroughNineteen[tens * 10 + ones]; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - else if (tens > 1) - { - result += Tens[tens] + OrdinalOneThroughNineteen[ones]; ; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - else - { - result += OrdinalOneThroughNineteen[ones]; - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - } - private static string NumberAsOrdinal(string languageCultureName, int levelNumber, string numFmt) - { - string levelAsString = levelNumber.ToString(); + public static string? GetListItemText(int levelNumber, string numFmt) + { + switch (numFmt) + { + case "cardinalText": + return NumberAsCardinalText(levelNumber); + + case "ordinalText": + return NumberAsOrdinalText(levelNumber); + + case "ordinal": + return NumberAsOrdinal(levelNumber); + + default: + return null; + } + } + + private static string NumberAsCardinalText(int levelNumber) + { + var result = ""; + + var sLevel = (levelNumber + 10000).ToString(); + var thousands = int.Parse(sLevel.Substring(1, 1)); + var hundreds = int.Parse(sLevel.Substring(2, 1)); + var tens = int.Parse(sLevel.Substring(3, 1)); + var ones = int.Parse(sLevel.Substring(4, 1)); + + //Validation + if (thousands > 19) + { + throw new ArgumentOutOfRangeException(nameof(levelNumber), "Convering a levelNumber to ordinal text that is greater then 19 999 is not supported"); + } + + if (levelNumber == 0) + { + return "Noll"; + } + + if (levelNumber < 0) + { + throw new ArgumentOutOfRangeException(nameof(levelNumber), "Converting a negative levelNumber to ordinal text is not supported"); + } + + /* exact thousands */ + if (levelNumber == 1000) + { + return "Ettusen"; + } + + if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) + { + result = OneThroughNineteen[thousands] + "tusen"; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + + /* > 1000 */ + if (levelNumber > 1000 && levelNumber < 2000) + { + result = "ettusen"; + } + else if (levelNumber > 2000 && levelNumber < 10000) + { + result = OneThroughNineteen[thousands] + "tusen"; + } + + /* exact hundreds */ + if (hundreds > 0 && tens == 0 && ones == 0) + { + if (hundreds == 1) + { + result += "etthundra"; + } + else + { + result += OneThroughNineteen[hundreds] + "hundra"; + } + + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + + /* > 100 */ + if (hundreds > 0) + { + if (hundreds == 1) + { + result += "etthundra"; + } + else + { + result += OneThroughNineteen[hundreds] + "hundra"; + } + } + + /* exact tens */ + if (tens > 0 && ones == 0) + { + result += Tens[tens]; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + + /* > 20 */ + if (tens == 1) + { + result += OneThroughNineteen[tens * 10 + ones]; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + else if (tens > 1) + { + result += Tens[tens] + OneThroughNineteen[ones]; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + else + { + result += OneThroughNineteen[ones]; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + } + + private static string NumberAsOrdinalText(int levelNumber) + { + var result = ""; + + if (levelNumber <= 0) + { + throw new ArgumentOutOfRangeException(nameof(levelNumber), "Converting a zero or negative levelNumber to ordinal text is not supported"); + } + + if (levelNumber >= 10000) + { + throw new ArgumentOutOfRangeException(nameof(levelNumber), "Convering a levelNumber to ordinal text that is greater then 10000 is not supported"); + } + + if (levelNumber == 1) + { + return "Första"; + } + + var sLevel = (levelNumber + 10000).ToString(); + var thousands = int.Parse(sLevel.Substring(1, 1)); + var hundreds = int.Parse(sLevel.Substring(2, 1)); + var tens = int.Parse(sLevel.Substring(3, 1)); + var ones = int.Parse(sLevel.Substring(4, 1)); + + /* exact thousands */ + if (levelNumber == 1000) + { + return "Ettusende"; + } + + if (levelNumber > 1000 && hundreds == 0 && tens == 0 && ones == 0) + { + result = OneThroughNineteen[thousands] + "tusende"; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + + /* > 1000 */ + if (levelNumber > 1000 && levelNumber < 2000) + { + result = "ettusen"; + } + else if (levelNumber > 2000 && levelNumber < 10000) + { + result = OneThroughNineteen[thousands] + "tusende"; + } + + /* exact hundreds */ + if (hundreds > 0 && tens == 0 && ones == 0) + { + if (hundreds == 1) + { + result += "etthundrade"; + } + else + { + result += OneThroughNineteen[hundreds] + "hundrade"; + } + + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + + /* > 100 */ + if (hundreds > 0) + { + result += OneThroughNineteen[hundreds] + "hundra"; + } + + /* exact tens */ + if (tens > 0 && ones == 0) + { + result += Tens[tens] + "nde"; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + + /* > 20 */ + if (tens == 1) + { + result += OrdinalOneThroughNineteen[tens * 10 + ones]; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + else if (tens > 1) + { + result += Tens[tens] + OrdinalOneThroughNineteen[ones]; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + else + { + result += OrdinalOneThroughNineteen[ones]; + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + } + + private static string NumberAsOrdinal(int levelNumber) + { + var levelAsString = levelNumber.ToString(); if (levelAsString == null) + { return ""; + } + if (levelAsString.Trim() == "") - return ""; - - if(levelAsString.EndsWith("1")) - return levelAsString + ":a"; - else if(levelAsString.EndsWith("2")) - return levelAsString + ":a"; - else - return levelAsString + ":e"; - } - } -} + { + return ""; + } + + if (levelAsString.EndsWith("1")) + { + return levelAsString + ":a"; + } + else if (levelAsString.EndsWith("2")) + { + return levelAsString + ":a"; + } + else + { + return levelAsString + ":e"; + } + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/GetListItemText_tr_TR.cs b/OpenXmlPowerTools/GetListItemText_tr_TR.cs index d6d26929..b30201e8 100644 --- a/OpenXmlPowerTools/GetListItemText_tr_TR.cs +++ b/OpenXmlPowerTools/GetListItemText_tr_TR.cs @@ -1,66 +1,58 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class ListItemTextGetter_tr_TR { - private static string[] RomanOnes = + private static readonly string[] RomanOnes = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; - private static string[] RomanTens = + private static readonly string[] RomanTens = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; - private static string[] RomanHundreds = + private static readonly string[] RomanHundreds = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M" }; - private static string[] RomanThousands = + private static readonly string[] RomanThousands = { "", "M", "MM", "MMM", "MMMM", "MMMMM", "MMMMMM", "MMMMMMM", "MMMMMMMM", "MMMMMMMMM", "MMMMMMMMMM" }; - private static string[] OneThroughNineteen = { + private static readonly string[] OneThroughNineteen = { "bir", "iki", "üç", "dört", "beş", "altı", "yedi", "sekiz", "dokuz", "on", "onbir", "oniki", "onüç", "ondört", "onbeş", "onaltı", "onyedi", "onsekiz", "ondokuz" }; - private static string[] Tens = { + private static readonly string[] Tens = { "on", "yirmi", "otuz", "kırk", "elli", "altmış", "yetmiş", "seksen", "doksan" }; - private static string[] OrdinalOneThroughNineteen = { + private static readonly string[] OrdinalOneThroughNineteen = { "birinci", "ikinci", "üçüncü", "dördüncü", "beşinci", "altıncı", "yedinci", "sekizinci", "dokuzuncu", "onuncu", "onbirinci", "onikinci", "onüçüncü", "ondördüncü", "onbeşinci", "onaltıncı", "onyedinci", "onsekizinci", "ondokuzuncu" }; - private static string[] TwoThroughNineteen = { + private static readonly string[] TwoThroughNineteen = { "", "iki", "üç", "dört", "beş", "altı", "yedi", "sekiz", "dokuz", "on", "onbir", "oniki", "onüç", "ondört", "onbeş", "onaltı", "onyedi", "onsekiz", "ondokuz" }; - private static string[] OrdinalTenths = { + private static readonly string[] OrdinalTenths = { "onuncu", "yirminci", "otuzuncu", "kırkıncı", "ellinci", "altmışıncı", "yetmişinci", "sekseninci", "doksanıncı" }; - public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) + public static string GetListItemText(int levelNumber, string numFmt) { #region if (numFmt == "decimal") @@ -70,43 +62,46 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (numFmt == "decimalZero") { if (levelNumber <= 9) + { return "0" + levelNumber.ToString(); + } else + { return levelNumber.ToString(); + } } if (numFmt == "upperRoman") { - int ones = levelNumber % 10; - int tens = (levelNumber % 100) / 10; - int hundreds = (levelNumber % 1000) / 100; - int thousands = levelNumber / 1000; + var ones = levelNumber % 10; + var tens = levelNumber % 100 / 10; + var hundreds = levelNumber % 1000 / 100; + var thousands = levelNumber / 1000; return RomanThousands[thousands] + RomanHundreds[hundreds] + RomanTens[tens] + RomanOnes[ones]; } if (numFmt == "lowerRoman") { - int ones = levelNumber % 10; - int tens = (levelNumber % 100) / 10; - int hundreds = (levelNumber % 1000) / 100; - int thousands = levelNumber / 1000; + var ones = levelNumber % 10; + var tens = levelNumber % 100 / 10; + var hundreds = levelNumber % 1000 / 100; + var thousands = levelNumber / 1000; return (RomanThousands[thousands] + RomanHundreds[hundreds] + RomanTens[tens] + RomanOnes[ones]).ToLower(); } if (numFmt == "upperLetter") { - string a = "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"; - //string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - int c = (levelNumber - 1) / 29; - int n = (levelNumber - 1) % 29; - char x = a[n]; + var a = "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"; + var c = (levelNumber - 1) / 29; + var n = (levelNumber - 1) % 29; + var x = a[n]; return "".PadRight(c + 1, x); } if (numFmt == "lowerLetter") { - string a = "abcçdefgğhıijklmnoöprsştuüvyz"; - int c = (levelNumber - 1) / 29; - int n = (levelNumber - 1) % 29; - char x = a[n]; + var a = "abcçdefgğhıijklmnoöprsştuüvyz"; + var c = (levelNumber - 1) / 29; + var n = (levelNumber - 1) % 29; + var x = a[n]; return "".PadRight(c + 1, x); } if (numFmt == "ordinal") @@ -128,35 +123,57 @@ public static string GetListItemText(string languageCultureName, int levelNumber } if (numFmt == "cardinalText") { - string result = ""; - int t1 = levelNumber / 1000; - int t2 = levelNumber % 1000; + var result = ""; + var t1 = levelNumber / 1000; + var t2 = levelNumber % 1000; if (t1 >= 1) + { result += OneThroughNineteen[t1 - 1] + " yüz"; + } + if (t1 >= 1 && t2 == 0) + { return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + if (t1 >= 1) + { result += " "; - int h1 = (levelNumber % 1000) / 100; - int h2 = levelNumber % 100; + } + + var h1 = levelNumber % 1000 / 100; + var h2 = levelNumber % 100; if (h1 >= 1) + { result += OneThroughNineteen[h1 - 1] + " bin"; + } + if (h1 >= 1 && h2 == 0) + { return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + if (h1 >= 1) + { result += " "; - int z = levelNumber % 100; + } + + var z = levelNumber % 100; if (z <= 19) + { result += OneThroughNineteen[z - 1]; + } else { - int x = z / 10; - int r = z % 10; + var x = z / 10; + var r = z % 10; result += Tens[x - 1]; if (r >= 1) + { result += /*"-" + */OneThroughNineteen[r - 1]; + } } return result.Substring(0, 1).ToUpper() + result.Substring(1); @@ -164,44 +181,55 @@ public static string GetListItemText(string languageCultureName, int levelNumber #endregion if (numFmt == "ordinalText") { - string result = ""; - int t1 = levelNumber / 1000; - int t2 = levelNumber % 1000; + var result = ""; + var t1 = levelNumber / 1000; + var t2 = levelNumber % 1000; if (t1 >= 1 && t2 != 0) + { result += TwoThroughNineteen[t1 - 1] + "bin"; + } + if (t1 >= 1 && t2 == 0) { result += TwoThroughNineteen[t1 - 1] + "bininci"; return result.Substring(0, 1).ToUpper() + result.Substring(1); } - //if (t1 >= 1) - // result += " "; - int h1 = (levelNumber % 1000) / 100; - int h2 = levelNumber % 100; + var h1 = levelNumber % 1000 / 100; + var h2 = levelNumber % 100; if (h1 >= 1 && h2 != 0) + { result += TwoThroughNineteen[h1 - 1] + "yüz"; + } + if (h1 >= 1 && h2 == 0) { result += TwoThroughNineteen[h1 - 1] + "yüzüncü"; return result.Substring(0, 1).ToUpper() + result.Substring(1); } - //if (h1 >= 1) - // result += " "; - int z = levelNumber % 100; + var z = levelNumber % 100; if (z <= 19) + { result += OrdinalOneThroughNineteen[z - 1]; + } else { - int x = z / 10; - int r = z % 10; + var x = z / 10; + var r = z % 10; if (r == 0) + { result += OrdinalTenths[x - 1]; + } else + { result += Tens[x - 1]; + } + if (r >= 1) - result += OrdinalOneThroughNineteen[r - 1]; //result += "-" + OrdinalOneThroughNineteen[r - 1]; + { + result += OrdinalOneThroughNineteen[r - 1]; + } } return result.Substring(0, 1).ToUpper() + result.Substring(1); @@ -211,8 +239,11 @@ public static string GetListItemText(string languageCultureName, int levelNumber return string.Format("{0:0000}", levelNumber); } if (numFmt == "bullet") + { return ""; + } + return levelNumber.ToString(); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/GetListItemText_zh_CN.cs b/OpenXmlPowerTools/GetListItemText_zh_CN.cs index 1bfaeaef..4018b00b 100644 --- a/OpenXmlPowerTools/GetListItemText_zh_CN.cs +++ b/OpenXmlPowerTools/GetListItemText_zh_CN.cs @@ -1,18 +1,10 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class ListItemTextGetter_zh_CN { - public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) + public static string? GetListItemText(int levelNumber, string numFmt) { - string[] ccTDigitCharacters = new[] { + var ccTDigitCharacters = new[] { "", "一", "二", @@ -24,12 +16,12 @@ public static string GetListItemText(string languageCultureName, int levelNumber "八", "九", }; - string tenCharacter = "十"; - string hundredCharacter = "百"; - string thousandCharacter = "千"; - string andCharacter = "〇"; + var tenCharacter = "十"; + var hundredCharacter = "百"; + var thousandCharacter = "千"; + var andCharacter = "〇"; - string[] ccDigitCharacters = new[] { + var ccDigitCharacters = new[] { "○", "一", "二", @@ -42,72 +34,120 @@ public static string GetListItemText(string languageCultureName, int levelNumber "九", }; - int thousandsRemainder = levelNumber % 1000; - int hundredsRemainder = levelNumber % 100; - int thousands = levelNumber / 1000; - int hundreds = (levelNumber % 1000) / 100; - int tens = (levelNumber % 100) / 10; - int ones = levelNumber % 10; + var thousandsRemainder = levelNumber % 1000; + var hundredsRemainder = levelNumber % 100; + var thousands = levelNumber / 1000; + var hundreds = levelNumber % 1000 / 100; + var tens = levelNumber % 100 / 10; + var ones = levelNumber % 10; if (numFmt == "chineseCounting") { if (levelNumber >= 1 && levelNumber <= 9) + { return ccDigitCharacters[levelNumber]; + } + if (levelNumber >= 10 && levelNumber <= 19) { if (levelNumber == 10) + { return tenCharacter; + } + return tenCharacter + ccDigitCharacters[ones]; } if (levelNumber >= 11 && levelNumber <= 99) { if (ones == 0) + { return ccDigitCharacters[tens] + tenCharacter; + } + return ccDigitCharacters[tens] + tenCharacter + ccDigitCharacters[ones]; } if (levelNumber >= 100 && levelNumber <= 999) + { return ccDigitCharacters[hundreds] + ccDigitCharacters[tens] + ccDigitCharacters[ones]; + } + if (levelNumber >= 1000 && levelNumber <= 9999) + { return ccDigitCharacters[thousands] + ccDigitCharacters[hundreds] + ccDigitCharacters[tens] + ccDigitCharacters[ones]; + } + return levelNumber.ToString(); } if (numFmt == "chineseCountingThousand") { if (levelNumber >= 1 && levelNumber <= 9) + { return ccTDigitCharacters[levelNumber]; + } + if (levelNumber >= 10 && levelNumber <= 19) + { return tenCharacter + ccTDigitCharacters[ones]; + } + if (levelNumber >= 20 && levelNumber <= 99) + { return ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; + } + if (levelNumber >= 100 && levelNumber <= 999) { if (hundredsRemainder == 0) + { return ccTDigitCharacters[hundreds] + hundredCharacter; + } + if (hundredsRemainder >= 1 && hundredsRemainder <= 9) + { return ccTDigitCharacters[hundreds] + hundredCharacter + andCharacter + ccTDigitCharacters[levelNumber % 10]; + } + if (ones == 0) + { return ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter; + } + return ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; } if (levelNumber >= 1000 && levelNumber <= 9999) { if (thousandsRemainder == 0) + { return ccTDigitCharacters[thousands] + thousandCharacter; + } + if (thousandsRemainder >= 1 && thousandsRemainder <= 9) - return ccTDigitCharacters[thousands] + thousandCharacter + andCharacter + GetListItemText("zh_CN", thousandsRemainder, numFmt); + { + return ccTDigitCharacters[thousands] + thousandCharacter + andCharacter + GetListItemText(thousandsRemainder, numFmt); + } + if (thousandsRemainder >= 10 && thousandsRemainder <= 99) + { return ccTDigitCharacters[thousands] + thousandCharacter + andCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; + } + if (hundredsRemainder == 0) + { return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter; + } + if (hundredsRemainder >= 1 && hundredsRemainder <= 9) + { return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter + andCharacter + ccTDigitCharacters[ones]; + } + return ccTDigitCharacters[thousands] + thousandCharacter + ccTDigitCharacters[hundreds] + hundredCharacter + ccTDigitCharacters[tens] + tenCharacter + ccTDigitCharacters[ones]; } return levelNumber.ToString(); } if (numFmt == "ideographTraditional") { - string[] iDigitCharacters = new[] { + var iDigitCharacters = new[] { " ", "甲", "乙", @@ -121,10 +161,13 @@ public static string GetListItemText(string languageCultureName, int levelNumber "癸", }; if (levelNumber >= 1 && levelNumber <= 10) + { return iDigitCharacters[levelNumber]; + } + return levelNumber.ToString(); } return null; } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/HtmlToWmlConverter.cs b/OpenXmlPowerTools/HtmlToWmlConverter.cs index 52685533..ab4ed98b 100644 --- a/OpenXmlPowerTools/HtmlToWmlConverter.cs +++ b/OpenXmlPowerTools/HtmlToWmlConverter.cs @@ -1,21 +1,11 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; -using System.Text; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using OpenXmlPowerTools.HtmlToWml; -using OpenXmlPowerTools.HtmlToWml.CSS; using System.Text.RegularExpressions; +using System.Xml.Linq; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class HtmlToWmlConverterSettings { @@ -28,12 +18,12 @@ public class HtmlToWmlConverterSettings public string DefaultBlockContentMargin; public string BaseUriForImages; - public Twip PageWidthTwips { get { return (long)SectPr.Elements(W.pgSz).Attributes(W._w).FirstOrDefault(); } } - public Twip PageMarginLeftTwips { get { return (long)SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); } } - public Twip PageMarginRightTwips { get { return (long)SectPr.Elements(W.pgMar).Attributes(W.right).FirstOrDefault(); } } - public Emu PageWidthEmus { get { return Emu.TwipsToEmus(PageWidthTwips); } } - public Emu PageMarginLeftEmus { get { return Emu.TwipsToEmus(PageMarginLeftTwips); } } - public Emu PageMarginRightEmus { get { return Emu.TwipsToEmus(PageMarginRightTwips); } } + public Twip PageWidthTwips => (long)SectPr.Elements(W.pgSz).Attributes(W._w).FirstOrDefault(); + public Twip PageMarginLeftTwips => (long)SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); + public Twip PageMarginRightTwips => (long)SectPr.Elements(W.pgMar).Attributes(W.right).FirstOrDefault(); + public Emu PageWidthEmus => Emu.TwipsToEmus(PageWidthTwips); + public Emu PageMarginLeftEmus => Emu.TwipsToEmus(PageMarginLeftTwips); + public Emu PageMarginRightEmus => Emu.TwipsToEmus(PageMarginRightTwips); } public class HtmlToWmlConverter @@ -60,7 +50,7 @@ public static WmlDocument ConvertHtmlToWml( return HtmlToWmlConverterCore.ConvertHtmlToWml(defaultCss, authorCss, userCss, xhtml, settings, emptyDocument, annotatedHtmlDumpFileName); } - private static string s_Blank_wml_base64 = @"UEsDBBQABgAIAAAAIQAJJIeCgQEAAI4FAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAAC + private static readonly string s_Blank_wml_base64 = @"UEsDBBQABgAIAAAAIQAJJIeCgQEAAI4FAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAAC AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -299,11 +289,12 @@ public static WmlDocument ConvertHtmlToWml( AAAAAABELQAAd29yZC9mb250VGFibGUueG1sUEsBAi0AFAAGAAgAAAAhAE5wytZwAQAAxQIAABAA AAAAAAAAAAAAAAAANi8AAGRvY1Byb3BzL2FwcC54bWxQSwUGAAAAAAwADAAJAwAA3DEAAAAA"; - private static WmlDocument s_EmptyDocument = null; + private static WmlDocument s_EmptyDocument; public static WmlDocument EmptyDocument { - get { + get + { if (s_EmptyDocument == null) { s_EmptyDocument = new WmlDocument("EmptyDocument.docx", Convert.FromBase64String(s_Blank_wml_base64)); @@ -319,43 +310,39 @@ public static HtmlToWmlConverterSettings GetDefaultSettings() public static HtmlToWmlConverterSettings GetDefaultSettings(WmlDocument wmlDocument) { - HtmlToWmlConverterSettings settings = new HtmlToWmlConverterSettings(); - using (MemoryStream ms = new MemoryStream()) + var settings = new HtmlToWmlConverterSettings(); + using (var ms = new MemoryStream()) { ms.Write(wmlDocument.DocumentByteArray, 0, wmlDocument.DocumentByteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(ms, false)) - { - string majorLatinFont, minorLatinFont; - double defaultFontSize; - GetDefaultFontInfo(wDoc, out majorLatinFont, out minorLatinFont, out defaultFontSize); - settings.MajorLatinFont = majorLatinFont; - settings.MinorLatinFont = minorLatinFont; - settings.DefaultFontSize = defaultFontSize; - - settings.MinorLatinFont = "Times New Roman"; - settings.DefaultFontSize = 12d; - settings.DefaultBlockContentMargin = "auto"; - settings.DefaultSpacingElement = new XElement(W.spacing, - new XAttribute(W.before, 100), - new XAttribute(W.beforeAutospacing, 1), - new XAttribute(W.after, 100), - new XAttribute(W.afterAutospacing, 1), - new XAttribute(W.line, 240), - new XAttribute(W.lineRule, "auto")); - settings.DefaultSpacingElementForParagraphsInTables = new XElement(W.spacing, - new XAttribute(W.before, 100), - new XAttribute(W.beforeAutospacing, 1), - new XAttribute(W.after, 100), - new XAttribute(W.afterAutospacing, 1), - new XAttribute(W.line, 240), - new XAttribute(W.lineRule, "auto")); - - XDocument mXDoc = wDoc.MainDocumentPart.GetXDocument(); - XElement existingSectPr = mXDoc.Root.Descendants(W.sectPr).FirstOrDefault(); - settings.SectPr = new XElement(W.sectPr, - existingSectPr.Elements(W.pgSz), - existingSectPr.Elements(W.pgMar)); - } + using var wDoc = WordprocessingDocument.Open(ms, false); + GetDefaultFontInfo(wDoc, out var majorLatinFont, out var minorLatinFont, out var defaultFontSize); + settings.MajorLatinFont = majorLatinFont; + settings.MinorLatinFont = minorLatinFont; + settings.DefaultFontSize = defaultFontSize; + + settings.MinorLatinFont = "Times New Roman"; + settings.DefaultFontSize = 12d; + settings.DefaultBlockContentMargin = "auto"; + settings.DefaultSpacingElement = new XElement(W.spacing, + new XAttribute(W.before, 100), + new XAttribute(W.beforeAutospacing, 1), + new XAttribute(W.after, 100), + new XAttribute(W.afterAutospacing, 1), + new XAttribute(W.line, 240), + new XAttribute(W.lineRule, "auto")); + settings.DefaultSpacingElementForParagraphsInTables = new XElement(W.spacing, + new XAttribute(W.before, 100), + new XAttribute(W.beforeAutospacing, 1), + new XAttribute(W.after, 100), + new XAttribute(W.afterAutospacing, 1), + new XAttribute(W.line, 240), + new XAttribute(W.lineRule, "auto")); + + var mXDoc = wDoc.MainDocumentPart.GetXDocument(); + var existingSectPr = mXDoc.Root.Descendants(W.sectPr).FirstOrDefault(); + settings.SectPr = new XElement(W.sectPr, + existingSectPr.Elements(W.pgSz), + existingSectPr.Elements(W.pgMar)); } return settings; } @@ -364,17 +351,16 @@ private static void GetDefaultFontInfo(WordprocessingDocument wDoc, out string m { if (wDoc.MainDocumentPart.ThemePart != null) { - XElement fontScheme = wDoc.MainDocumentPart.ThemePart.GetXDocument().Root.Elements(A.themeElements).Elements(A.fontScheme).FirstOrDefault(); + var fontScheme = wDoc.MainDocumentPart.ThemePart.GetXDocument().Root.Elements(A.themeElements).Elements(A.fontScheme).FirstOrDefault(); if (fontScheme != null) { majorLatinFont = (string)fontScheme.Elements(A.majorFont).Elements(A.latin).Attributes(NoNamespace.typeface).FirstOrDefault(); minorLatinFont = (string)fontScheme.Elements(A.minorFont).Elements(A.latin).Attributes(NoNamespace.typeface).FirstOrDefault(); - string defaultFontSizeString = (string)wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.docDefaults) + var defaultFontSizeString = (string)wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument().Root.Elements(W.docDefaults) .Elements(W.rPrDefault).Elements(W.rPr).Elements(W.sz).Attributes(W.val).FirstOrDefault(); if (defaultFontSizeString != null) { - double dfs; - if (double.TryParse(defaultFontSizeString, out dfs)) + if (double.TryParse(defaultFontSizeString, out var dfs)) { defaultFontSize = dfs / 2d; return; @@ -392,23 +378,38 @@ private static void GetDefaultFontInfo(WordprocessingDocument wDoc, out string m public static string CleanUpCss(string css) { if (css == null) + { return ""; + } + css = css.Trim(); - string cleanCss = Regex.Split(css, "\r\n|\r|\n") + var cleanCss = Regex.Split(css, "\r\n|\r|\n") .Where(l => { - string lTrim = l.Trim(); + var lTrim = l.Trim(); if (lTrim == "//") + { return false; + } + if (lTrim == "////") + { return false; + } + if (lTrim == "" || lTrim == "-->") + { return false; + } + return true; }) - .Select(l => l + Environment.NewLine ) + .Select(l => l + Environment.NewLine) .StringConcatenate(); return cleanCss; } @@ -421,18 +422,18 @@ public struct Emu public static Emu TwipsToEmus(long twips) { - float v1 = (float)twips / 20f; - float v2 = v1 / 72f; - float v3 = v2 * s_EmusPerInch; - long emus = (long)v3; + var v1 = twips / 20f; + var v2 = v1 / 72f; + var v3 = v2 * s_EmusPerInch; + var emus = (long)v3; return new Emu(emus); } public static Emu PointsToEmus(double points) { - double v1 = points / 72; - double v2 = v1 * s_EmusPerInch; - long emus = (long)v2; + var v1 = points / 72; + var v2 = v1 * s_EmusPerInch; + var emus = (long)v2; return new Emu(emus); } @@ -529,5 +530,4 @@ public SizeEmu(long width, long height) m_Height = height; } } -} - +} \ No newline at end of file diff --git a/OpenXmlPowerTools/HtmlToWmlConverterCore.cs b/OpenXmlPowerTools/HtmlToWmlConverterCore.cs index 438c57a2..97fc3d0f 100644 --- a/OpenXmlPowerTools/HtmlToWmlConverterCore.cs +++ b/OpenXmlPowerTools/HtmlToWmlConverterCore.cs @@ -1,6 +1,3 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - /*************************************************************************** * HTML elements handled in this module: * @@ -96,21 +93,17 @@ // then need to make sure that all of the cells below the caption have the border on the appropriate sides so that it looks as if the table // has a border. +using DocumentFormat.OpenXml.Packaging; +using SixLabors.Fonts; +using SkiaSharp; using System; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using OpenXmlPowerTools.HtmlToWml; -using OpenXmlPowerTools.HtmlToWml.CSS; -using System.Text.RegularExpressions; -namespace OpenXmlPowerTools.HtmlToWml +namespace Codeuctivity.OpenXmlPowerTools { public class ElementToStyleMap { @@ -120,13 +113,12 @@ public class ElementToStyleMap public static class LocalExtensions { - public static CssExpression GetProp(this XElement element, string propertyName) + public static CssExpression? GetProp(this XElement element, string propertyName) { - Dictionary d = element.Annotation>(); - if (d != null) + var d = element.Annotation>(); + if (d != null && d.ContainsKey(propertyName)) { - if (d.ContainsKey(propertyName)) - return d[propertyName]; + return d[propertyName]; } return null; } @@ -150,47 +142,48 @@ public static WmlDocument ConvertHtmlToWml( string userCss, XElement xhtml, HtmlToWmlConverterSettings settings, - WmlDocument emptyDocument, - string annotatedHtmlDumpFileName) + WmlDocument? emptyDocument, + string? annotatedHtmlDumpFileName) { if (emptyDocument == null) + { emptyDocument = HtmlToWmlConverter.EmptyDocument; + } NextRectId = 1025; // clone and transform all element names to lower case - XElement html = (XElement)TransformToLower(xhtml); + var html = (XElement)TransformToLower(xhtml); // add pseudo cells for rowspan - html = (XElement)AddPseudoCells(html); + html = AddPseudoCells(html); html = (XElement)TransformWhiteSpaceInPreCodeTtKbdSamp(html, false, false); - CssDocument defaultCssDoc, userCssDoc, authorCssDoc; CssApplier.ApplyAllCss( defaultCss, authorCss, userCss, html, settings, - out defaultCssDoc, - out authorCssDoc, - out userCssDoc, + out var defaultCssDoc, + out var authorCssDoc, + out var userCssDoc, annotatedHtmlDumpFileName); WmlDocument newWmlDocument; - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(emptyDocument)) + using (var streamDoc = new OpenXmlMemoryStreamDocument(emptyDocument)) { - using (WordprocessingDocument wDoc = streamDoc.GetWordprocessingDocument()) + using (var wDoc = streamDoc.GetWordprocessingDocument()) { AnnotateOlUl(wDoc, html); UpdateMainDocumentPart(wDoc, html, settings); NormalizeMainDocumentPart(wDoc); - StylesUpdater.UpdateStylesPart(wDoc, html, settings, defaultCssDoc, authorCssDoc, userCssDoc); - HtmlToWmlFontUpdater.UpdateFontsPart(wDoc, html, settings); - ThemeUpdater.UpdateThemePart(wDoc, html, settings); - NumberingUpdater.UpdateNumberingPart(wDoc, html, settings); + StylesUpdater.UpdateStylesPart(wDoc, html, settings, authorCssDoc); + HtmlToWmlFontUpdater.UpdateFontsPart(wDoc, settings); + ThemeUpdater.UpdateThemePart(wDoc, html); + NumberingUpdater.UpdateNumberingPart(wDoc, html); } newWmlDocument = streamDoc.GetModifiedWmlDocument(); } @@ -198,13 +191,11 @@ public static WmlDocument ConvertHtmlToWml( return newWmlDocument; } - private static object TransformToLower(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { - XElement e = new XElement(element.Name.LocalName.ToLower(), + var e = new XElement(element.Name.LocalName.ToLower(), element.Attributes().Select(a => new XAttribute(a.Name.LocalName.ToLower(), a.Value)), element.Nodes().Select(n => TransformToLower(n))); return e; @@ -220,14 +211,20 @@ private static XElement AddPseudoCells(XElement html) .Descendants(XhtmlNoNamespace.td) .FirstOrDefault(td => td.Attribute(XhtmlNoNamespace.rowspan) != null && td.Attribute("HtmlToWmlVMergeRestart") == null); if (rowSpanCell == null) + { break; + } + rowSpanCell.Add( new XAttribute("HtmlToWmlVMergeRestart", "true")); - int colNumber = rowSpanCell.ElementsBeforeSelf(XhtmlNoNamespace.td).Count(); - int numberPseudoToAdd = (int)rowSpanCell.Attribute(XhtmlNoNamespace.rowspan) - 1; + var colNumber = rowSpanCell.ElementsBeforeSelf(XhtmlNoNamespace.td).Count(); + var numberPseudoToAdd = (int)rowSpanCell.Attribute(XhtmlNoNamespace.rowspan) - 1; var tr = rowSpanCell.Ancestors(XhtmlNoNamespace.tr).FirstOrDefault(); if (tr == null) + { throw new OpenXmlPowerToolsException("Invalid HTML - td does not have parent tr"); + } + var rowsToAddTo = tr .ElementsAfterSelf(XhtmlNoNamespace.tr) .Take(numberPseudoToAdd) @@ -270,21 +267,25 @@ public class NumberedItemAnnotation private static void AnnotateOlUl(WordprocessingDocument wDoc, XElement html) { - int numId; - NumberingUpdater.GetNextNumId(wDoc, out numId); + NumberingUpdater.GetNextNumId(wDoc, out var numId); foreach (var item in html.DescendantsAndSelf().Where(d => d.Name == XhtmlNoNamespace.ol || d.Name == XhtmlNoNamespace.ul)) { - XElement parentOlUl = item.Ancestors().Where(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul).LastOrDefault(); + var parentOlUl = item.Ancestors().LastOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); int numIdToUse; if (parentOlUl != null) + { numIdToUse = parentOlUl.Annotation().numId; + } else + { numIdToUse = numId++; - string lst = CssApplier.GetComputedPropertyValue(null, item, "list-style-type", null).ToString(); + } + + var lst = CssApplier.GetComputedPropertyValue(null, item, "list-style-type", null).ToString(); item.AddAnnotation(new NumberedItemAnnotation { numId = numIdToUse, - ilvl = item.Ancestors().Where(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul).Count(), + ilvl = item.Ancestors().Count(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul), listStyleType = lst, }); } @@ -292,7 +293,7 @@ private static void AnnotateOlUl(WordprocessingDocument wDoc, XElement html) private static void UpdateMainDocumentPart(WordprocessingDocument wDoc, XElement html, HtmlToWmlConverterSettings settings) { - XDocument xDoc = XDocument.Parse( + var xDoc = XDocument.Parse( @""); - XElement body = new XElement(W.body, + var body = new XElement(W.body, Transform(html, settings, wDoc, NextExpected.Paragraph, false), settings.SectPr); @@ -318,15 +319,17 @@ private static void UpdateMainDocumentPart(WordprocessingDocument wDoc, XElement body = (XElement)TransformAndOrderElements(body); foreach (var d in body.Descendants()) + { d.Attributes().Where(a => a.Name.Namespace == PtOpenXml.pt).Remove(); + } + xDoc.Root.Add(body); wDoc.MainDocumentPart.PutXDocument(xDoc); } private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inPre, bool inOther) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == XhtmlNoNamespace.pre) { @@ -347,16 +350,19 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP element.Attributes(), element.Nodes().Select(n => TransformWhiteSpaceInPreCodeTtKbdSamp(n, false, false))); } - XText xt = node as XText; + var xt = node as XText; if (xt != null && inPre) { var val = xt.Value.TrimStart('\r', '\n').TrimEnd('\r', '\n'); var groupedCharacters = val.GroupAdjacent(c => c == '\r' || c == '\n'); var newNodes = groupedCharacters.Select(g => { - if (g.Key == true) - return (object)(new XElement(XhtmlNoNamespace.br)); - string x = g.Select(c => c.ToString()).StringConcatenate(); + if (g.Key) + { + return (object)new XElement(XhtmlNoNamespace.br); + } + + var x = g.Select(c => c.ToString()).StringConcatenate(); return new XText(x); }); return newNodes; @@ -369,7 +375,7 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP return node; } - private static Dictionary Order_pPr = new Dictionary + private static readonly Dictionary Order_pPr = new Dictionary { { W.pStyle, 10 }, { W.keepNext, 20 }, @@ -409,7 +415,7 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP { W.pPrChange, 370 }, }; - private static Dictionary Order_rPr = new Dictionary + private static readonly Dictionary Order_rPr = new Dictionary { { W.ins, 10 }, { W.del, 20 }, @@ -459,7 +465,7 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP { W.oMath, 460 }, }; - private static Dictionary Order_tblPr = new Dictionary + private static readonly Dictionary Order_tblPr = new Dictionary { { W.tblStyle, 10 }, { W.tblpPr, 20 }, @@ -480,7 +486,7 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP { W.tblDescription, 170 }, }; - private static Dictionary Order_tblBorders = new Dictionary + private static readonly Dictionary Order_tblBorders = new Dictionary { { W.top, 10 }, { W.left, 20 }, @@ -492,7 +498,7 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP { W.insideV, 80 }, }; - private static Dictionary Order_tcPr = new Dictionary + private static readonly Dictionary Order_tcPr = new Dictionary { { W.cnfStyle, 10 }, { W.tcW, 20 }, @@ -510,7 +516,7 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP { W.headers, 140 }, }; - private static Dictionary Order_tcBorders = new Dictionary + private static readonly Dictionary Order_tcBorders = new Dictionary { { W.top, 10 }, { W.start, 20 }, @@ -524,7 +530,7 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP { W.tr2bl, 100 }, }; - private static Dictionary Order_pBdr = new Dictionary + private static readonly Dictionary Order_pBdr = new Dictionary { { W.top, 10 }, { W.left, 20 }, @@ -536,90 +542,128 @@ private static object TransformWhiteSpaceInPreCodeTtKbdSamp(XNode node, bool inP private static object TransformAndOrderElements(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.pPr) + { return new XElement(element.Name, element.Attributes(), element.Elements().Select(e => (XElement)TransformAndOrderElements(e)).OrderBy(e => { if (Order_pPr.ContainsKey(e.Name)) + { return Order_pPr[e.Name]; + } + return 999; })); + } if (element.Name == W.rPr) + { return new XElement(element.Name, element.Attributes(), element.Elements().Select(e => (XElement)TransformAndOrderElements(e)).OrderBy(e => { if (Order_rPr.ContainsKey(e.Name)) + { return Order_rPr[e.Name]; + } + return 999; })); + } if (element.Name == W.tblPr) + { return new XElement(element.Name, element.Attributes(), element.Elements().Select(e => (XElement)TransformAndOrderElements(e)).OrderBy(e => { if (Order_tblPr.ContainsKey(e.Name)) + { return Order_tblPr[e.Name]; + } + return 999; })); + } if (element.Name == W.tcPr) + { return new XElement(element.Name, element.Attributes(), element.Elements().Select(e => (XElement)TransformAndOrderElements(e)).OrderBy(e => { if (Order_tcPr.ContainsKey(e.Name)) + { return Order_tcPr[e.Name]; + } + return 999; })); + } if (element.Name == W.tcBorders) + { return new XElement(element.Name, element.Attributes(), element.Elements().Select(e => (XElement)TransformAndOrderElements(e)).OrderBy(e => { if (Order_tcBorders.ContainsKey(e.Name)) + { return Order_tcBorders[e.Name]; + } + return 999; })); + } if (element.Name == W.tblBorders) + { return new XElement(element.Name, element.Attributes(), element.Elements().Select(e => (XElement)TransformAndOrderElements(e)).OrderBy(e => { if (Order_tblBorders.ContainsKey(e.Name)) + { return Order_tblBorders[e.Name]; + } + return 999; })); + } if (element.Name == W.pBdr) + { return new XElement(element.Name, element.Attributes(), element.Elements().Select(e => (XElement)TransformAndOrderElements(e)).OrderBy(e => { if (Order_pBdr.ContainsKey(e.Name)) + { return Order_pBdr[e.Name]; + } + return 999; })); + } if (element.Name == W.p) + { return new XElement(element.Name, element.Attributes(), element.Elements(W.pPr).Select(e => (XElement)TransformAndOrderElements(e)), element.Elements().Where(e => e.Name != W.pPr).Select(e => (XElement)TransformAndOrderElements(e))); + } if (element.Name == W.r) + { return new XElement(element.Name, element.Attributes(), element.Elements(W.rPr).Select(e => (XElement)TransformAndOrderElements(e)), element.Elements().Where(e => e.Name != W.rPr).Select(e => (XElement)TransformAndOrderElements(e))); + } return new XElement(element.Name, element.Attributes(), @@ -630,60 +674,72 @@ private static object TransformAndOrderElements(XNode node) private static void AddNonBreakingSpacesForSpansWithWidth(WordprocessingDocument wDoc, XElement body) { - List runsWithWidth = body + var runsWithWidth = body .Descendants(W.r) .Where(r => r.Attribute(PtOpenXml.HtmlToWmlCssWidth) != null) .ToList(); - foreach (XElement run in runsWithWidth) - { - XElement p = run.Ancestors(W.p).FirstOrDefault(); - XElement pPr = p != null ? p.Element(W.pPr) : null; - XElement rPr = run.Element(W.rPr); - XElement rFonts = rPr != null ? rPr.Element(W.rFonts) : null; - string str = run.Descendants(W.t).Select(t => (string) t).StringConcatenate(); - if ((pPr == null) || (rPr == null) || (rFonts == null) || (str == "")) continue; + foreach (var run in runsWithWidth) + { + var p = run.Ancestors(W.p).FirstOrDefault(); + var pPr = p?.Element(W.pPr); + var rPr = run.Element(W.rPr); + var rFonts = rPr?.Element(W.rFonts); + var str = run.Descendants(W.t).Select(t => (string)t).StringConcatenate(); + if (pPr == null || rPr == null || rFonts == null || str == "") + { + continue; + } AdjustFontAttributes(wDoc, run, pPr, rPr); var csa = new CharStyleAttributes(pPr, rPr); - char charToExamine = str.FirstOrDefault(c => !WeakAndNeutralDirectionalCharacters.Contains(c)); + var charToExamine = str.FirstOrDefault(c => !WeakAndNeutralDirectionalCharacters.Contains(c)); if (charToExamine == '\0') + { charToExamine = str[0]; + } - FontType ft = DetermineFontTypeFromCharacter(charToExamine, csa); - string fontType = null; - string languageType = null; + var ft = DetermineFontTypeFromCharacter(charToExamine, csa); + string? fontType = null; + string? languageType = null; switch (ft) { case FontType.Ascii: - fontType = (string) rFonts.Attribute(W.ascii); + fontType = (string)rFonts.Attribute(W.ascii); languageType = "western"; break; + case FontType.HAnsi: - fontType = (string) rFonts.Attribute(W.hAnsi); + fontType = (string)rFonts.Attribute(W.hAnsi); languageType = "western"; break; + case FontType.EastAsia: - fontType = (string) rFonts.Attribute(W.eastAsia); + fontType = (string)rFonts.Attribute(W.eastAsia); languageType = "eastAsia"; break; + case FontType.CS: - fontType = (string) rFonts.Attribute(W.cs); + fontType = (string)rFonts.Attribute(W.cs); languageType = "bidi"; break; } if (fontType != null) { - XAttribute fontNameAttribute = run.Attribute(PtOpenXml.FontName); + var fontNameAttribute = run.Attribute(PtOpenXml.FontName); if (fontNameAttribute == null) + { run.Add(new XAttribute(PtOpenXml.FontName, fontType)); + } else + { fontNameAttribute.SetValue(fontType); + } } if (languageType != null) { - XAttribute languageTypeAttribute = run.Attribute(PtOpenXml.LanguageType); + var languageTypeAttribute = run.Attribute(PtOpenXml.LanguageType); if (languageTypeAttribute == null) { run.Add(new XAttribute(PtOpenXml.LanguageType, languageType)); @@ -694,45 +750,53 @@ private static void AddNonBreakingSpacesForSpansWithWidth(WordprocessingDocument } } - int pixWidth = CalcWidthOfRunInPixels(run) ?? 0; + var pixWidth = CalcWidthOfRunInPixels(run) ?? 0; // calc width of non breaking spaces var npSpRun = new XElement(W.r, run.Attributes(), run.Elements(W.rPr), new XElement(W.t, "\u00a0")); - int nbSpWidth = CalcWidthOfRunInPixels(npSpRun) ?? 0; + var nbSpWidth = CalcWidthOfRunInPixels(npSpRun) ?? 0; if (nbSpWidth == 0) + { continue; + } // get HtmlToWmlCssWidth attribute - var cssWidth = (string) run.Attribute(PtOpenXml.HtmlToWmlCssWidth); - if (!cssWidth.EndsWith("pt")) continue; + var cssWidth = (string)run.Attribute(PtOpenXml.HtmlToWmlCssWidth); + if (!cssWidth.EndsWith("pt")) + { + continue; + } cssWidth = cssWidth.Substring(0, cssWidth.Length - 2); - decimal cssWidthInDecimal; - if (!decimal.TryParse(cssWidth, out cssWidthInDecimal)) continue; + if (!decimal.TryParse(cssWidth, out var cssWidthInDecimal)) + { + continue; + } // calculate the number of non-breaking spaces to add - decimal cssWidthInPixels = cssWidthInDecimal/72*96; - var numberOfNpSpToAdd = (int) ((cssWidthInPixels - pixWidth)/nbSpWidth); + var cssWidthInPixels = cssWidthInDecimal / 72 * 96; + var numberOfNpSpToAdd = (int)((cssWidthInPixels - pixWidth) / nbSpWidth); if (numberOfNpSpToAdd > 0) + { run.Add(new XElement(W.t, "".PadRight(numberOfNpSpToAdd, '\u00a0'))); + } } } private static void NormalizeMainDocumentPart(WordprocessingDocument wDoc) { - XDocument mainXDoc = wDoc.MainDocumentPart.GetXDocument(); - XElement newRoot = (XElement)NormalizeTransform(mainXDoc.Root); + var mainXDoc = wDoc.MainDocumentPart.GetXDocument(); + var newRoot = (XElement)NormalizeTransform(mainXDoc.Root); mainXDoc.Root.ReplaceWith(newRoot); wDoc.MainDocumentPart.PutXDocument(); } private static object NormalizeTransform(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == W.p && element.Elements().Any(c => c.Name == W.p || c.Name == W.tbl)) { @@ -741,9 +805,9 @@ private static object NormalizeTransform(XNode node) var newContent = groupedChildren .Select(g => { - if (g.Key == false) + if (!g.Key) { - XElement paragraph = new XElement(W.p, + var paragraph = new XElement(W.p, element.Elements(W.pPr), g.Where(gc => gc.Name != W.pPr)); return (object)paragraph; @@ -766,26 +830,25 @@ private enum NextExpected SubRun, } - private static object Transform(XNode node, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc, NextExpected nextExpected, bool preserveWhiteSpace) + private static object? Transform(XNode node, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc, NextExpected nextExpected, bool preserveWhiteSpace) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element.Name == XhtmlNoNamespace.a) { - string rId = "R" + Guid.NewGuid().ToString().Replace("-", ""); - string href = (string)element.Attribute(NoNamespace.href); + var rId = "R" + Guid.NewGuid().ToString().Replace("-", ""); + var href = (string)element.Attribute(NoNamespace.href); if (href != null) { - Uri uri = null; + Uri? uri = null; try { uri = new Uri(href); } catch (UriFormatException) { - XElement rPr = GetRunProperties(element, settings); - XElement run = new XElement(W.r, + var rPr = GetRunProperties(element, settings); + var run = new XElement(W.r, rPr, new XElement(W.t, element.Value)); return new[] { run }; @@ -819,8 +882,8 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, return newImageTransformed; } - XElement rPr = GetRunProperties(element, settings); - XElement hyperlink = new XElement(W.hyperlink, + var rPr = GetRunProperties(element, settings); + var hyperlink = new XElement(W.hyperlink, new XAttribute(R.id, rId), new XElement(W.r, rPr, @@ -832,10 +895,14 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, } if (element.Name == XhtmlNoNamespace.b) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.body) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.caption) { @@ -869,9 +936,11 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, } if (element.Name == XhtmlNoNamespace.em) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, NextExpected.Run, preserveWhiteSpace)); + } - HeadingInfo hi = HeadingTagMap.FirstOrDefault(htm => htm.Name == element.Name); + var hi = HeadingTagMap.FirstOrDefault(htm => htm.Name == element.Name); if (hi != null) { return GenerateNextExpected(element, settings, wDoc, hi.StyleName, NextExpected.Paragraph, false); @@ -879,8 +948,8 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, if (element.Name == XhtmlNoNamespace.hr) { - int i = GetNextRectId(); - XElement hr = XElement.Parse( + var i = GetNextRectId(); + var hr = XElement.Parse( @" Transform(n, settings, wDoc, NextExpected.Paragraph, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.i) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.blockquote) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.img) { if (element.Parent.Name == XhtmlNoNamespace.body) { - XElement para = new XElement(W.p, + var para = new XElement(W.p, GetParagraphPropertiesForImage(), TransformImageToWml(element, settings, wDoc)); return para; } else { - XElement content = TransformImageToWml(element, settings, wDoc); + var content = TransformImageToWml(element, settings, wDoc); return content; } } @@ -932,7 +1007,9 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, } if (element.Name == XhtmlNoNamespace.ol) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, NextExpected.Paragraph, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.p) { @@ -940,7 +1017,9 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, } if (element.Name == XhtmlNoNamespace.s) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } /****************************************** SharePoint Specific ********************************************/ // todo sharepoint specific @@ -955,7 +1034,9 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, // new XElement(W.t, element.Value))); //} if (element.Name == XhtmlNoNamespace.span && (string)element.Attribute(XhtmlNoNamespace.id) == "layoutsData") + { return null; + } /****************************************** End SharePoint Specific ********************************************/ if (element.Name == XhtmlNoNamespace.span) @@ -963,19 +1044,25 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, var spanReplacement = element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); var dummyElement = new XElement("dummy", spanReplacement); var firstChild = dummyElement.Elements().FirstOrDefault(); - XElement run = null; + XElement? run = null; if (firstChild != null && firstChild.Name == W.r) + { run = firstChild; + } + if (run != null) { - Dictionary computedProperties = element.Annotation>(); + var computedProperties = element.Annotation>(); if (computedProperties != null && computedProperties.ContainsKey("width")) { string width = computedProperties["width"]; if (width != "auto") + { run.Add(new XAttribute(PtOpenXml.HtmlToWmlCssWidth, width)); + } + var rFontsLocal = run.Element(W.rFonts); - XElement rFontsGlobal = null; + XElement? rFontsGlobal = null; var styleDefPart = wDoc.MainDocumentPart.StyleDefinitionsPart; if (styleDefPart != null) { @@ -987,9 +1074,13 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, { var rFontsExisting = rPr.Element(W.rFonts); if (rFontsExisting == null) + { rPr.AddFirst(rFontsGlobal); + } else + { rFontsExisting.ReplaceWith(rFontsGlobal); + } } } return dummyElement.Elements(); @@ -999,20 +1090,28 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, } if (element.Name == XhtmlNoNamespace.strong) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.style) + { return null; + } if (element.Name == XhtmlNoNamespace.sub) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.sup) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.table) { - XElement wmlTable = new XElement(W.tbl, + var wmlTable = new XElement(W.tbl, GetTableProperties(element), GetTableGrid(element, settings), element.Nodes().Select(n => Transform(n, settings, wDoc, NextExpected.Paragraph, preserveWhiteSpace))); @@ -1020,7 +1119,9 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, } if (element.Name == XhtmlNoNamespace.tbody) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, NextExpected.Paragraph, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.td) { @@ -1035,7 +1136,10 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, .Select(e => { if (e.Name == W.hyperlink || e.Name == W.r) + { return new XElement(W.p, e); + } + return e; })); @@ -1071,12 +1175,17 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, } if (element.Name == XhtmlNoNamespace.u) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.ul) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.br) + { if (nextExpected == NextExpected.Paragraph) { return new XElement(W.p, @@ -1087,50 +1196,64 @@ private static object Transform(XNode node, HtmlToWmlConverterSettings settings, { return new XElement(W.r, new XElement(W.br)); } + } if (element.Name == XhtmlNoNamespace.tt || element.Name == XhtmlNoNamespace.code || element.Name == XhtmlNoNamespace.kbd || element.Name == XhtmlNoNamespace.samp) + { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); + } if (element.Name == XhtmlNoNamespace.pre) + { return GenerateNextExpected(element, settings, wDoc, null, NextExpected.Paragraph, true); + } // if no match up to this point, then just recursively process descendants return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); } if (node.Parent.Name != XhtmlNoNamespace.title) + { return GenerateNextExpected(node, settings, wDoc, null, nextExpected, preserveWhiteSpace); + } return null; - } - private static XElement FontMerge(XElement higherPriorityFont, XElement lowerPriorityFont) + private static XElement? FontMerge(XElement higherPriorityFont, XElement lowerPriorityFont) { XElement rFonts; if (higherPriorityFont == null) + { return lowerPriorityFont; + } + if (lowerPriorityFont == null) + { return higherPriorityFont; + } + if (higherPriorityFont == null && lowerPriorityFont == null) + { return null; + } rFonts = new XElement(W.rFonts, - (higherPriorityFont.Attribute(W.ascii) != null || higherPriorityFont.Attribute(W.asciiTheme) != null) ? + higherPriorityFont.Attribute(W.ascii) != null || higherPriorityFont.Attribute(W.asciiTheme) != null ? new[] { higherPriorityFont.Attribute(W.ascii), higherPriorityFont.Attribute(W.asciiTheme) } : new[] { lowerPriorityFont.Attribute(W.ascii), lowerPriorityFont.Attribute(W.asciiTheme) }, - (higherPriorityFont.Attribute(W.hAnsi) != null || higherPriorityFont.Attribute(W.hAnsiTheme) != null) ? + higherPriorityFont.Attribute(W.hAnsi) != null || higherPriorityFont.Attribute(W.hAnsiTheme) != null ? new[] { higherPriorityFont.Attribute(W.hAnsi), higherPriorityFont.Attribute(W.hAnsiTheme) } : new[] { lowerPriorityFont.Attribute(W.hAnsi), lowerPriorityFont.Attribute(W.hAnsiTheme) }, - (higherPriorityFont.Attribute(W.eastAsia) != null || higherPriorityFont.Attribute(W.eastAsiaTheme) != null) ? + higherPriorityFont.Attribute(W.eastAsia) != null || higherPriorityFont.Attribute(W.eastAsiaTheme) != null ? new[] { higherPriorityFont.Attribute(W.eastAsia), higherPriorityFont.Attribute(W.eastAsiaTheme) } : new[] { lowerPriorityFont.Attribute(W.eastAsia), lowerPriorityFont.Attribute(W.eastAsiaTheme) }, - (higherPriorityFont.Attribute(W.cs) != null || higherPriorityFont.Attribute(W.cstheme) != null) ? + higherPriorityFont.Attribute(W.cs) != null || higherPriorityFont.Attribute(W.cstheme) != null ? new[] { higherPriorityFont.Attribute(W.cs), higherPriorityFont.Attribute(W.cstheme) } : new[] { lowerPriorityFont.Attribute(W.cs), lowerPriorityFont.Attribute(W.cstheme) }, - (higherPriorityFont.Attribute(W.hint) != null ? higherPriorityFont.Attribute(W.hint) : - lowerPriorityFont.Attribute(W.hint)) + higherPriorityFont.Attribute(W.hint) != null ? higherPriorityFont.Attribute(W.hint) : + lowerPriorityFont.Attribute(W.hint) ); return rFonts; @@ -1141,28 +1264,39 @@ private static XElement FontMerge(XElement higherPriorityFont, XElement lowerPri var fontName = (string)r.Attribute(PtOpenXml.FontName) ?? (string)r.Ancestors(W.p).First().Attribute(PtOpenXml.FontName); if (fontName == null) + { throw new OpenXmlPowerToolsException("Internal Error, should have FontName attribute"); + } + if (UnknownFonts.Contains(fontName)) + { return 0; + } if (UnknownFonts.Contains(fontName)) + { return null; + } var rPr = r.Element(W.rPr); if (rPr == null) + { return null; + } var sz = GetFontSize(r) ?? 22m; // unknown font families will throw ArgumentException, in which case just return 0 if (!KnownFamilies.Contains(fontName)) + { return 0; + } // in theory, all unknown fonts are found by the above test, but if not... FontFamily ff; try { - ff = new FontFamily(fontName); + ff = SystemFonts.Families.Single(font => font.Name == fontName); } catch (ArgumentException) { @@ -1173,9 +1307,14 @@ private static XElement FontMerge(XElement higherPriorityFont, XElement lowerPri var fs = FontStyle.Regular; if (Util.GetBoolProp(rPr, W.b) == true || Util.GetBoolProp(rPr, W.bCs) == true) + { fs |= FontStyle.Bold; + } + if (Util.GetBoolProp(rPr, W.i) == true || Util.GetBoolProp(rPr, W.iCs) == true) + { fs |= FontStyle.Italic; + } // Appended blank as a quick fix to accommodate   that will get // appended to some layout-critical runs such as list item numbers. @@ -1193,24 +1332,40 @@ private static XElement FontMerge(XElement higherPriorityFont, XElement lowerPri .Sum(); if (runText.Length == 0 && tabLength == 0) + { return 0; + } - int multiplier = 1; + var multiplier = 1; if (runText.Length <= 2) + { multiplier = 100; + } else if (runText.Length <= 4) + { multiplier = 50; + } else if (runText.Length <= 8) + { multiplier = 25; + } else if (runText.Length <= 16) + { multiplier = 12; + } else if (runText.Length <= 32) + { multiplier = 6; + } + if (multiplier != 1) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < multiplier; i++) + var sb = new StringBuilder(); + for (var i = 0; i < multiplier; i++) + { sb.Append(runText); + } + runText = sb.ToString(); } @@ -1233,19 +1388,19 @@ public enum FontType public class CharStyleAttributes { - public string AsciiFont; - public string HAnsiFont; - public string EastAsiaFont; - public string CsFont; - public string Hint; - public bool Rtl; + public string? AsciiFont { get; set; } + public string? HAnsiFont { get; set; } + public string? EastAsiaFont { get; set; } + public string? CsFont { get; set; } + public string? Hint { get; set; } + public bool Rtl { get; set; } - public string LatinLang; - public string BidiLang; - public string EastAsiaLang; + public string? LatinLang { get; set; } + public string? BidiLang { get; set; } + public string? EastAsiaLang { get; set; } - public Dictionary ToggleProperties; - public Dictionary Properties; + public Dictionary ToggleProperties { get; set; } + public Dictionary Properties { get; set; } public CharStyleAttributes(XElement pPr, XElement rPr) { @@ -1253,40 +1408,43 @@ public CharStyleAttributes(XElement pPr, XElement rPr) Properties = new Dictionary(); if (rPr == null) + { return; - foreach (XName xn in TogglePropertyNames) + } + + foreach (var xn in TogglePropertyNames) { ToggleProperties[xn] = Util.GetBoolProp(rPr, xn); } - foreach (XName xn in PropertyNames) + foreach (var xn in PropertyNames) { Properties[xn] = GetXmlProperty(rPr, xn); } var rFonts = rPr.Element(W.rFonts); if (rFonts == null) { - this.AsciiFont = null; - this.HAnsiFont = null; - this.EastAsiaFont = null; - this.CsFont = null; - this.Hint = null; + AsciiFont = null; + HAnsiFont = null; + EastAsiaFont = null; + CsFont = null; + Hint = null; } else { - this.AsciiFont = (string)(rFonts.Attribute(W.ascii)); - this.HAnsiFont = (string)(rFonts.Attribute(W.hAnsi)); - this.EastAsiaFont = (string)(rFonts.Attribute(W.eastAsia)); - this.CsFont = (string)(rFonts.Attribute(W.cs)); - this.Hint = (string)(rFonts.Attribute(W.hint)); + AsciiFont = (string)rFonts.Attribute(W.ascii); + HAnsiFont = (string)rFonts.Attribute(W.hAnsi); + EastAsiaFont = (string)rFonts.Attribute(W.eastAsia); + CsFont = (string)rFonts.Attribute(W.cs); + Hint = (string)rFonts.Attribute(W.hint); } - XElement csel = this.Properties[W.cs]; - bool cs = csel != null && (csel.Attribute(W.val) == null || csel.Attribute(W.val).ToBoolean() == true); - XElement rtlel = this.Properties[W.rtl]; - bool rtl = rtlel != null && (rtlel.Attribute(W.val) == null || rtlel.Attribute(W.val).ToBoolean() == true); + var csel = Properties[W.cs]; + var cs = csel != null && (csel.Attribute(W.val) == null || csel.Attribute(W.val).ToBoolean() == true); + var rtlel = Properties[W.rtl]; + var rtl = rtlel != null && (rtlel.Attribute(W.val) == null || rtlel.Attribute(W.val).ToBoolean() == true); var bidi = false; if (pPr != null) { - XElement bidiel = pPr.Element(W.bidi); + var bidiel = pPr.Element(W.bidi); bidi = bidiel != null && (bidiel.Attribute(W.val) == null || bidiel.Attribute(W.val).ToBoolean() == true); } Rtl = cs || rtl || bidi; @@ -1304,7 +1462,7 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) return rPr.Element(propertyName); } - private static XName[] TogglePropertyNames = new[] { + private static readonly XName[] TogglePropertyNames = new[] { W.b, W.bCs, W.caps, @@ -1319,7 +1477,7 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) W.vanish }; - private static XName[] PropertyNames = new[] { + private static readonly XName[] PropertyNames = new[] { W.cs, W.rtl, W.u, @@ -1327,7 +1485,6 @@ private static XElement GetXmlProperty(XElement rPr, XName propertyName) W.highlight, W.shd }; - } public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttributes csa) @@ -1367,27 +1524,24 @@ public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttribut ch == 0xAA || ch == 0xAD || ch == 0xAF || - (ch >= 0xB0 && ch <= 0xB4) || - (ch >= 0xB6 && ch <= 0xBA) || - (ch >= 0xBC && ch <= 0xBF) || + ch >= 0xB0 && ch <= 0xB4 || + ch >= 0xB6 && ch <= 0xBA || + ch >= 0xBC && ch <= 0xBF || ch == 0xD7 || ch == 0xF7) { return FontType.EastAsia; } - if (csa.EastAsiaLang == "zh-hant" || - csa.EastAsiaLang == "zh-hans") - { - if (ch == 0xE0 || + if ((csa.EastAsiaLang == "zh-hant" || + csa.EastAsiaLang == "zh-hans") && (ch == 0xE0 || ch == 0xE1 || - (ch >= 0xE8 && ch <= 0xEA) || - (ch >= 0xEC && ch <= 0xED) || - (ch >= 0xF2 && ch <= 0xF3) || - (ch >= 0xF9 && ch <= 0xFA) || - ch == 0xFC) - { - return FontType.EastAsia; - } + ch >= 0xE8 && ch <= 0xEA || + ch >= 0xEC && ch <= 0xED || + ch >= 0xF2 && ch <= 0xF3 || + ch >= 0xF9 && ch <= 0xFA || + ch == 0xFC)) + { + return FontType.EastAsia; } } return FontType.HAnsi; @@ -1396,14 +1550,10 @@ public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttribut // Unicode Block: Latin Extended-A if (ch >= 0x0100 && ch <= 0x017F) { - if (csa.Hint == "eastAsia") + if (csa.Hint == "eastAsia" && (csa.EastAsiaLang == "zh-hant" || csa.EastAsiaLang == "zh-hans") +) { - if (csa.EastAsiaLang == "zh-hant" || - csa.EastAsiaLang == "zh-hans" - /* || the character set of the east Asia (or east Asia theme) font is Chinese5 || GB2312 todo */) - { - return FontType.EastAsia; - } + return FontType.EastAsia; } return FontType.HAnsi; } @@ -1428,8 +1578,7 @@ public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttribut { if (csa.Hint == "eastAsia") { - if (csa.EastAsiaLang == "zh-hant" || - csa.EastAsiaLang == "zh-hans" + if (csa.EastAsiaLang == "zh-hant" || csa.EastAsiaLang == "zh-hans" /* || the character set of the east Asia (or east Asia theme) font is Chinese5 || GB2312 todo */) { return FontType.EastAsia; @@ -1836,9 +1985,14 @@ public static FontType DetermineFontTypeFromCharacter(char ch, CharStyleAttribut if (csa.Hint == "eastAsia") { if (ch >= 0xFB00 && ch <= 0xFB1C) + { return FontType.EastAsia; + } + if (ch >= 0xFB1D && ch <= 0xFB4F) + { return FontType.Ascii; + } } return FontType.HAnsi; } @@ -1885,15 +2039,16 @@ private static HashSet KnownFamilies if (_knownFamilies == null) { _knownFamilies = new HashSet(); - var families = FontFamily.Families; - foreach (var fam in families) + foreach (var fam in SystemFonts.Families) + { _knownFamilies.Add(fam.Name); + } } return _knownFamilies; } } - private static HashSet WeakAndNeutralDirectionalCharacters = new HashSet() { + private static readonly HashSet WeakAndNeutralDirectionalCharacters = new HashSet() { '0', '1', '2', @@ -1996,13 +2151,15 @@ private static HashSet KnownFamilies private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement paraOrRun, XElement pPr, XElement rPr) { - XDocument themeXDoc = null; + XDocument? themeXDoc = null; if (wDoc.MainDocumentPart.ThemePart != null) + { themeXDoc = wDoc.MainDocumentPart.ThemePart.GetXDocument(); + } - XElement fontScheme = null; - XElement majorFont = null; - XElement minorFont = null; + XElement? fontScheme = null; + XElement? majorFont = null; + XElement? minorFont = null; if (themeXDoc != null) { fontScheme = themeXDoc.Root.Element(A.themeElements).Element(A.fontScheme); @@ -2018,15 +2175,15 @@ private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement p var hAnsiTheme = (string)rFonts.Attribute(W.hAnsiTheme); var eastAsiaTheme = (string)rFonts.Attribute(W.eastAsiaTheme); var cstheme = (string)rFonts.Attribute(W.cstheme); - string ascii = null; - string hAnsi = null; - string eastAsia = null; - string cs = null; + string? ascii = null; + string? hAnsi = null; + string? eastAsia = null; + string? cs = null; - XElement minorLatin = null; - string minorLatinTypeface = null; - XElement majorLatin = null; - string majorLatinTypeface = null; + XElement? minorLatin = null; + string? minorLatinTypeface = null; + XElement? majorLatin = null; + string? majorLatinTypeface = null; if (minorFont != null) { @@ -2102,14 +2259,18 @@ private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement p } var firstTextNode = paraOrRun.Descendants(W.t).FirstOrDefault(t => t.Value.Length > 0); - string str = " "; + var str = " "; // if there is a run with no text in it, then no need to do any of the rest of this method. if (firstTextNode == null && paraOrRun.Name == W.r) + { return; + } if (firstTextNode != null) + { str = firstTextNode.Value; + } var csa = new CharStyleAttributes(pPr, rPr); @@ -2118,50 +2279,32 @@ private static void AdjustFontAttributes(WordprocessingDocument wDoc, XElement p // However, Word breaks up runs that use more than one font into multiple runs. Other producers of WordprocessingML may not, so in // that case, this routine may need to be augmented to look at all characters in a run. - /* - old code - var fontFamilies = str.select(function (c) { - var ft = Pav.DetermineFontTypeFromCharacter(c, csa); - switch (ft) { - case Pav.FontType.Ascii: - return cast(rFonts.attribute(W.ascii)); - case Pav.FontType.HAnsi: - return cast(rFonts.attribute(W.hAnsi)); - case Pav.FontType.EastAsia: - return cast(rFonts.attribute(W.eastAsia)); - case Pav.FontType.CS: - return cast(rFonts.attribute(W.cs)); - default: - return null; - } - }) - .where(function (f) { return f != null && f != ""; }) - .distinct() - .select(function (f) { return new Pav.FontFamily(f); }) - .toArray(); - */ - var charToExamine = str.FirstOrDefault(c => !WeakAndNeutralDirectionalCharacters.Contains(c)); if (charToExamine == '\0') + { charToExamine = str[0]; + } var ft = DetermineFontTypeFromCharacter(charToExamine, csa); - string fontType = null; - string languageType = null; + string? fontType = null; + string? languageType = null; switch (ft) { case FontType.Ascii: fontType = (string)rFonts.Attribute(W.ascii); languageType = "western"; break; + case FontType.HAnsi: fontType = (string)rFonts.Attribute(W.hAnsi); languageType = "western"; break; + case FontType.EastAsia: fontType = (string)rFonts.Attribute(W.eastAsia); languageType = "eastAsia"; break; + case FontType.CS: fontType = (string)rFonts.Attribute(W.cs); languageType = "bidi"; @@ -2172,7 +2315,7 @@ old code { if (paraOrRun.Attribute(PtOpenXml.FontName) == null) { - XAttribute fta = new XAttribute(PtOpenXml.FontName, fontType.ToString()); + var fta = new XAttribute(PtOpenXml.FontName, fontType.ToString()); paraOrRun.Add(fta); } else @@ -2184,7 +2327,7 @@ old code { if (paraOrRun.Attribute(PtOpenXml.LanguageType) == null) { - XAttribute lta = new XAttribute(PtOpenXml.LanguageType, languageType); + var lta = new XAttribute(PtOpenXml.LanguageType, languageType); paraOrRun.Add(lta); } else @@ -2210,7 +2353,11 @@ old code private static decimal? GetFontSize(string languageType, XElement rPr) { - if (rPr == null) return null; + if (rPr == null) + { + return null; + } + return languageType == "bidi" ? (decimal?)rPr.Elements(W.szCs).Attributes(W.val).FirstOrDefault() : (decimal?)rPr.Elements(W.sz).Attributes(W.val).FirstOrDefault(); @@ -2223,13 +2370,12 @@ private static int GetNextRectId() return NextRectId++; } - private static object GenerateNextExpected(XNode node, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc, - string styleName, NextExpected nextExpected, bool preserveWhiteSpace) + private static object? GenerateNextExpected(XNode node, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc, + string? styleName, NextExpected nextExpected, bool preserveWhiteSpace) { if (nextExpected == NextExpected.Paragraph) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { return new XElement(W.p, GetParagraphProperties(element, styleName, settings), @@ -2237,10 +2383,9 @@ private static object GenerateNextExpected(XNode node, HtmlToWmlConverterSetting } else { - XText xTextNode = node as XText; - if (xTextNode != null) + if (node is XText xTextNode) { - string textNodeString = GetDisplayText(xTextNode, preserveWhiteSpace); + var textNodeString = GetDisplayText(xTextNode, preserveWhiteSpace); XElement p; p = new XElement(W.p, GetParagraphProperties(node.Parent, null, settings), @@ -2256,16 +2401,15 @@ private static object GenerateNextExpected(XNode node, HtmlToWmlConverterSetting } else { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { return element.Nodes().Select(n => Transform(n, settings, wDoc, nextExpected, preserveWhiteSpace)); } else { - string textNodeString = GetDisplayText((XText)node, preserveWhiteSpace); - XElement rPr = GetRunProperties((XText)node, settings); - XElement r = new XElement(W.r, + var textNodeString = GetDisplayText((XText)node, preserveWhiteSpace); + var rPr = GetRunProperties((XText)node, settings); + var r = new XElement(W.r, rPr, new XElement(W.t, GetXmlSpaceAttribute(textNodeString), @@ -2275,28 +2419,38 @@ private static object GenerateNextExpected(XNode node, HtmlToWmlConverterSetting } } - private static XElement TransformImageToWml(XElement element, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc) + private static XElement? TransformImageToWml(XElement element, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc) { - string srcAttribute = (string)element.Attribute(XhtmlNoNamespace.src); - byte[] ba = null; - Bitmap bmp = null; - + var srcAttribute = (string)element.Attribute(XhtmlNoNamespace.src); + SKBitmap? bmp = null; + byte[] ba; if (srcAttribute.StartsWith("data:")) { var semiIndex = srcAttribute.IndexOf(';'); var commaIndex = srcAttribute.IndexOf(',', semiIndex); var base64 = srcAttribute.Substring(commaIndex + 1); - ba = Convert.FromBase64String(base64); - using (MemoryStream ms = new MemoryStream(ba)) + var raw = Convert.FromBase64String(base64); + bmp = SKBitmap.Decode(raw); + if (bmp == null) { - bmp = new Bitmap(ms); + return null; } + using var image = SKImage.FromBitmap(bmp); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + ba = data.ToArray(); } else { try { - bmp = new Bitmap(settings.BaseUriForImages + "/" + srcAttribute); + bmp = SKBitmap.Decode(Path.Combine(settings.BaseUriForImages, srcAttribute)); + if (bmp == null) + { + return null; + } + using var image = SKImage.FromBitmap(bmp); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + ba = data.ToArray(); } catch (ArgumentException) { @@ -2306,19 +2460,18 @@ private static XElement TransformImageToWml(XElement element, HtmlToWmlConverter { return null; } - MemoryStream ms = new MemoryStream(); - bmp.Save(ms, bmp.RawFormat); - ba = ms.ToArray(); } - MainDocumentPart mdp = wDoc.MainDocumentPart; - string rId = "R" + Guid.NewGuid().ToString().Replace("-", ""); - ImagePartType ipt = ImagePartType.Png; - ImagePart newPart = mdp.AddImagePart(ipt, rId); - using (Stream s = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) - s.Write(ba, 0, ba.GetUpperBound(0) + 1); + var mdp = wDoc.MainDocumentPart; + var rId = "R" + Guid.NewGuid().ToString().Replace("-", ""); + var ipt = ImagePartType.Png; + var newPart = mdp.AddImagePart(ipt, rId); + using (var s = newPart.GetStream(FileMode.Create, FileAccess.ReadWrite)) + { + s.Write(ba, 0, ba.Length); + } - PictureId pid = wDoc.Annotation(); + var pid = wDoc.Annotation(); if (pid == null) { pid = new PictureId @@ -2327,35 +2480,31 @@ private static XElement TransformImageToWml(XElement element, HtmlToWmlConverter }; wDoc.AddAnnotation(pid); } - int pictureId = pid.Id; + var pictureId = pid.Id; ++pid.Id; - string pictureDescription = "Picture " + pictureId.ToString(); + var pictureDescription = "Picture " + pictureId.ToString(); - string floatValue = element.GetProp("float").ToString(); + var floatValue = element.GetProp("float").ToString(); if (floatValue == "none") { - XElement run = new XElement(W.r, - GetRunPropertiesForImage(), - new XElement(W.drawing, - GetImageAsInline(element, settings, wDoc, bmp, rId, pictureId, pictureDescription))); + var run = new XElement(W.r, GetRunPropertiesForImage(), new XElement(W.drawing, GetImageAsInline(element, bmp, rId, pictureId, pictureDescription))); return run; } if (floatValue == "left" || floatValue == "right") { - XElement run = new XElement(W.r, + var run = new XElement(W.r, GetRunPropertiesForImage(), new XElement(W.drawing, - GetImageAsAnchor(element, settings, wDoc, bmp, rId, floatValue, pictureId, pictureDescription))); + GetImageAsAnchor(element, settings, bmp, rId, floatValue, pictureId, pictureDescription))); return run; } return null; } - private static XElement GetImageAsInline(XElement element, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc, Bitmap bmp, - string rId, int pictureId, string pictureDescription) + private static XElement GetImageAsInline(XElement element, SKBitmap bmp, string rId, int pictureId, string pictureDescription) { - XElement inline = new XElement(WP.inline, // 20.4.2.8 + var inline = new XElement(WP.inline, // 20.4.2.8 new XAttribute(XNamespace.Xmlns + "wp", WP.wp.NamespaceName), new XAttribute(NoNamespace.distT, 0), // distance from top of image to text, in EMUs, no effect if the parent is inline new XAttribute(NoNamespace.distB, 0), // bottom @@ -2369,16 +2518,15 @@ private static XElement GetImageAsInline(XElement element, HtmlToWmlConverterSet return inline; } - private static XElement GetImageAsAnchor(XElement element, HtmlToWmlConverterSettings settings, WordprocessingDocument wDoc, Bitmap bmp, - string rId, string floatValue, int pictureId, string pictureDescription) + private static XElement GetImageAsAnchor(XElement element, HtmlToWmlConverterSettings settings, SKBitmap bmp, string rId, string floatValue, int pictureId, string pictureDescription) { Emu minDistFromEdge = (long)(0.125 * Emu.s_EmusPerInch); long relHeight = 251658240; // z-order - CssExpression marginTopProp = element.GetProp("margin-top"); - CssExpression marginLeftProp = element.GetProp("margin-left"); - CssExpression marginBottomProp = element.GetProp("margin-bottom"); - CssExpression marginRightProp = element.GetProp("margin-right"); + var marginTopProp = element.GetProp("margin-top"); + var marginLeftProp = element.GetProp("margin-left"); + var marginBottomProp = element.GetProp("margin-bottom"); + var marginRightProp = element.GetProp("margin-right"); Emu marginTopInEmus = 0; Emu marginBottomInEmus = 0; @@ -2386,45 +2534,64 @@ private static XElement GetImageAsAnchor(XElement element, HtmlToWmlConverterSet Emu marginRightInEmus = 0; if (marginTopProp.IsNotAuto) + { marginTopInEmus = (Emu)marginTopProp; + } if (marginBottomProp.IsNotAuto) + { marginBottomInEmus = (Emu)marginBottomProp; + } if (marginLeftProp.IsNotAuto) + { marginLeftInEmus = (Emu)marginLeftProp; + } if (marginRightProp.IsNotAuto) + { marginRightInEmus = (Emu)marginRightProp; + } Emu relativeFromColumn = 0; if (floatValue == "left") { relativeFromColumn = marginLeftInEmus; - CssExpression parentMarginLeft = element.Parent.GetProp("margin-left"); + var parentMarginLeft = element.Parent.GetProp("margin-left"); if (parentMarginLeft.IsNotAuto) + { relativeFromColumn += (long)(Emu)parentMarginLeft; + } + marginRightInEmus = Math.Max(marginRightInEmus, minDistFromEdge); } else if (floatValue == "right") { - Emu printWidth = (long)settings.PageWidthEmus - (long)settings.PageMarginLeftEmus - (long)settings.PageMarginRightEmus; - SizeEmu sl = GetImageSizeInEmus(element, bmp); + Emu printWidth = settings.PageWidthEmus - settings.PageMarginLeftEmus - settings.PageMarginRightEmus; + var sl = GetImageSizeInEmus(element, bmp); relativeFromColumn = printWidth - sl.m_Width; if (marginRightProp.IsNotAuto) - relativeFromColumn -= (long)(Emu)marginRightInEmus; - CssExpression parentMarginRight = element.Parent.GetProp("margin-right"); + { + relativeFromColumn -= (long)marginRightInEmus; + } + + var parentMarginRight = element.Parent.GetProp("margin-right"); if (parentMarginRight.IsNotAuto) + { relativeFromColumn -= (long)(Emu)parentMarginRight; + } + marginLeftInEmus = Math.Max(marginLeftInEmus, minDistFromEdge); } - Emu relativeFromParagraph = marginTopInEmus; - CssExpression parentMarginTop = element.Parent.GetProp("margin-top"); + var relativeFromParagraph = marginTopInEmus; + var parentMarginTop = element.Parent.GetProp("margin-top"); if (parentMarginTop.IsNotAuto) + { relativeFromParagraph += (long)(Emu)parentMarginTop; + } - XElement anchor = new XElement(WP.anchor, + var anchor = new XElement(WP.anchor, new XAttribute(XNamespace.Xmlns + "wp", WP.wp.NamespaceName), new XAttribute(NoNamespace.distT, (long)marginTopInEmus), // distance from top of image to text, in EMUs, no effect if the parent is inline new XAttribute(NoNamespace.distB, (long)marginBottomInEmus), // bottom @@ -2454,92 +2621,8 @@ private static XElement GetImageAsAnchor(XElement element, HtmlToWmlConverterSet ); return anchor; } -#if false - - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - 0 - - -#endif - - private static XElement GetParagraphPropertiesForImage() + + private static XElement? GetParagraphPropertiesForImage() { return null; } @@ -2550,28 +2633,26 @@ private static XElement GetRunPropertiesForImage() new XElement(W.noProof)); } - private static SizeEmu GetImageSizeInEmus(XElement img, Bitmap bmp) + private static SizeEmu GetImageSizeInEmus(XElement img, SKBitmap bmp) { - double hres = bmp.HorizontalResolution; - double vres = bmp.VerticalResolution; - Size s = bmp.Size; - Emu cx = (long)((double)(s.Width / hres) * (double)Emu.s_EmusPerInch); - Emu cy = (long)((double)(s.Height / vres) * (double)Emu.s_EmusPerInch); - - CssExpression width = img.GetProp("width"); - CssExpression height = img.GetProp("height"); + const float dpi = 96f; + Emu cx = (long)(bmp.Width / dpi * Emu.s_EmusPerInch); + Emu cy = (long)(bmp.Height / dpi * Emu.s_EmusPerInch); + + var width = img.GetProp("width"); + var height = img.GetProp("height"); if (width.IsNotAuto && height.IsAuto) { - Emu widthInEmus = (Emu)width; - double percentChange = (float)widthInEmus / (float)cx; + var widthInEmus = (Emu)width; + double percentChange = widthInEmus / (float)cx; cx = widthInEmus; cy = (long)(cy * percentChange); return new SizeEmu(cx, cy); } if (width.IsAuto && height.IsNotAuto) { - Emu heightInEmus = (Emu)height; - double percentChange = (float)heightInEmus / (float)cy; + var heightInEmus = (Emu)height; + double percentChange = heightInEmus / (float)cy; cy = heightInEmus; cx = (long)(cx * percentChange); return new SizeEmu(cx, cy); @@ -2583,9 +2664,9 @@ private static SizeEmu GetImageSizeInEmus(XElement img, Bitmap bmp) return new SizeEmu(cx, cy); } - private static XElement GetImageExtent(XElement img, Bitmap bmp) + private static XElement GetImageExtent(XElement img, SKBitmap bmp) { - SizeEmu szEmu = GetImageSizeInEmus(img, bmp); + var szEmu = GetImageSizeInEmus(img, bmp); return new XElement(WP.extent, new XAttribute(NoNamespace.cx, (long)szEmu.m_Width), // in EMUs new XAttribute(NoNamespace.cy, (long)szEmu.m_Height)); // in EMUs @@ -2616,10 +2697,10 @@ private static XElement GetCNvGraphicFramePr() new XAttribute(NoNamespace.noChangeAspect, 1))); } - private static XElement GetGraphicForImage(XElement element, string rId, Bitmap bmp, int pictureId, string pictureDescription) + private static XElement GetGraphicForImage(XElement element, string rId, SKBitmap bmp, int pictureId, string pictureDescription) { - SizeEmu szEmu = GetImageSizeInEmus(element, bmp); - XElement graphic = new XElement(A.graphic, + var szEmu = GetImageSizeInEmus(element, bmp); + var graphic = new XElement(A.graphic, new XAttribute(XNamespace.Xmlns + "a", A.a.NamespaceName), new XElement(A.graphicData, new XAttribute(NoNamespace.uri, Pic.pic.NamespaceName), @@ -2657,62 +2738,24 @@ private static XElement GetGraphicForImage(XElement element, string rId, Bitmap return graphic; } -#if false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#endif - private static XElement GetParagraphProperties(XElement blockLevelElement, string styleName, HtmlToWmlConverterSettings settings) + private static XElement GetParagraphProperties(XElement blockLevelElement, string? styleName, HtmlToWmlConverterSettings settings) { - XElement paragraphMarkRunProperties = GetRunProperties(blockLevelElement, settings); - XElement backgroundProperty = GetBackgroundProperty(blockLevelElement); - XElement[] spacingProperty = GetSpacingProperties(blockLevelElement, settings); // spacing, ind, contextualSpacing - XElement jc = GetJustification(blockLevelElement, settings); - XElement pStyle = styleName != null ? new XElement(W.pStyle, new XAttribute(W.val, styleName)) : null; - XElement numPr = GetNumberingProperties(blockLevelElement, settings); - XElement pBdr = GetBlockContentBorders(blockLevelElement, W.pBdr, true); - - XElement bidi = null; - string direction = GetDirection(blockLevelElement); + var paragraphMarkRunProperties = GetRunProperties(blockLevelElement, settings); + var backgroundProperty = GetBackgroundProperty(blockLevelElement); + var spacingProperty = GetSpacingProperties(blockLevelElement, settings); // spacing, ind, contextualSpacing + var jc = GetJustification(blockLevelElement); + var pStyle = styleName != null ? new XElement(W.pStyle, new XAttribute(W.val, styleName)) : null; + var numPr = GetNumberingProperties(blockLevelElement); + var pBdr = GetBlockContentBorders(blockLevelElement, W.pBdr, true); + + XElement? bidi = null; + var direction = GetDirection(blockLevelElement); if (direction == "rtl") + { bidi = new XElement(W.bidi); + } - XElement pPr = new XElement(W.pPr, + var pPr = new XElement(W.pPr, pStyle, numPr, pBdr, @@ -2733,13 +2776,13 @@ private static XElement GetParagraphProperties(XElement blockLevelElement, strin // Returns the spacing, ind, and contextualSpacing elements private static XElement[] GetSpacingProperties(XElement paragraph, HtmlToWmlConverterSettings settings) { - CssExpression marginLeftProperty = paragraph.GetProp("margin-left"); - CssExpression marginRightProperty = paragraph.GetProp("margin-right"); - CssExpression marginTopProperty = paragraph.GetProp("margin-top"); - CssExpression marginBottomProperty = paragraph.GetProp("margin-bottom"); - CssExpression lineHeightProperty = paragraph.GetProp("line-height"); - CssExpression leftPaddingProperty = paragraph.GetProp("padding-left"); - CssExpression rightPaddingProperty = paragraph.GetProp("padding-right"); + var marginLeftProperty = paragraph.GetProp("margin-left"); + var marginRightProperty = paragraph.GetProp("margin-right"); + var marginTopProperty = paragraph.GetProp("margin-top"); + var marginBottomProperty = paragraph.GetProp("margin-bottom"); + var lineHeightProperty = paragraph.GetProp("line-height"); + var leftPaddingProperty = paragraph.GetProp("padding-left"); + var rightPaddingProperty = paragraph.GetProp("padding-right"); /*****************************************************************************************/ // leftIndent, rightIndent, firstLine @@ -2748,26 +2791,23 @@ private static XElement[] GetSpacingProperties(XElement paragraph, HtmlToWmlConv Twip rightIndent = 0; Twip firstLine = 0; -#if false - // this code is here for some reason. What is it? - double leftBorderSize = GetBorderSize(paragraph, "left"); // in 1/8 point - double rightBorderSize = GetBorderSize(paragraph, "right"); // in 1/8 point - leftIndent += (long)((leftBorderSize / 8d) * 20d); - rightIndent += (long)((rightBorderSize / 8d) * 20d); -#endif - if (leftPaddingProperty != null) + { leftIndent += (Twip)leftPaddingProperty; + } + if (rightPaddingProperty != null) + { rightIndent += (Twip)rightPaddingProperty; + } if (paragraph.Name == XhtmlNoNamespace.li) { leftIndent += 180; rightIndent += 180; } - XElement listElement = null; - NumberedItemAnnotation numberedItemAnnotation = null; + XElement? listElement = null; + NumberedItemAnnotation? numberedItemAnnotation = null; listElement = paragraph.Ancestors().FirstOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); if (listElement != null) { @@ -2775,44 +2815,53 @@ private static XElement[] GetSpacingProperties(XElement paragraph, HtmlToWmlConv leftIndent += 600 * (numberedItemAnnotation.ilvl + 1); } - int blockQuoteCount = paragraph.Ancestors(XhtmlNoNamespace.blockquote).Count(); + var blockQuoteCount = paragraph.Ancestors(XhtmlNoNamespace.blockquote).Count(); leftIndent += blockQuoteCount * 720; if (blockQuoteCount == 0) { if (marginLeftProperty != null && marginLeftProperty.IsNotAuto && marginLeftProperty.IsNotNormal) + { leftIndent += (Twip)marginLeftProperty; + } + if (marginRightProperty != null && marginRightProperty.IsNotAuto && marginRightProperty.IsNotNormal) + { rightIndent += (Twip)marginRightProperty; + } } - CssExpression textIndentProperty = paragraph.GetProp("text-indent"); + var textIndentProperty = paragraph.GetProp("text-indent"); if (textIndentProperty != null) { - Twip twips = (Twip)textIndentProperty; + var twips = (Twip)textIndentProperty; firstLine = twips; } - XElement ind = null; + XElement? ind = null; if (leftIndent > 0 || rightIndent > 0 || firstLine != 0) { if (firstLine < 0) + { ind = new XElement(W.ind, leftIndent != 0 ? new XAttribute(W.left, (long)leftIndent) : null, rightIndent != 0 ? new XAttribute(W.right, (long)rightIndent) : null, - firstLine != 0 ? new XAttribute(W.hanging, -(long)firstLine) : null); + firstLine != 0 ? new XAttribute(W.hanging, -firstLine) : null); + } else + { ind = new XElement(W.ind, leftIndent != 0 ? new XAttribute(W.left, (long)leftIndent) : null, rightIndent != 0 ? new XAttribute(W.right, (long)rightIndent) : null, firstLine != 0 ? new XAttribute(W.firstLine, (long)firstLine) : null); + } } /*****************************************************************************************/ // spacing long line = 240; - string lineRule = "auto"; - string beforeAutospacing = null; - string afterAutospacing = null; + var lineRule = "auto"; + string? beforeAutospacing = null; + string? afterAutospacing = null; long? before = null; long? after = null; @@ -2827,30 +2876,36 @@ private static XElement[] GetSpacingProperties(XElement paragraph, HtmlToWmlConv } // todo should check based on display property - bool numItem = paragraph.Name == XhtmlNoNamespace.li; + var numItem = paragraph.Name == XhtmlNoNamespace.li; if (numItem && marginTopProperty.IsAuto) + { beforeAutospacing = "1"; + } + if (numItem && marginBottomProperty.IsAuto) + { afterAutospacing = "1"; + } + if (marginTopProperty != null && marginTopProperty.IsNotAuto) { - before = (long)(Twip)marginTopProperty; + before = (Twip)marginTopProperty; beforeAutospacing = "0"; } if (marginBottomProperty != null && marginBottomProperty.IsNotAuto) { - after = (long)(Twip)marginBottomProperty; + after = (Twip)marginBottomProperty; afterAutospacing = "0"; } if (lineHeightProperty != null && lineHeightProperty.IsNotAuto && lineHeightProperty.IsNotNormal) { // line is in twips if lineRule == "atLeast" - line = (long)(Twip)lineHeightProperty; + line = (Twip)lineHeightProperty; lineRule = "atLeast"; } - XElement spacing = new XElement(W.spacing, + var spacing = new XElement(W.spacing, before != null ? new XAttribute(W.before, before) : null, beforeAutospacing != null ? new XAttribute(W.beforeAutospacing, beforeAutospacing) : null, after != null ? new XAttribute(W.after, after) : null, @@ -2861,21 +2916,23 @@ private static XElement[] GetSpacingProperties(XElement paragraph, HtmlToWmlConv /*****************************************************************************************/ // contextualSpacing - XElement contextualSpacing = null; + XElement? contextualSpacing = null; if (paragraph.Name == XhtmlNoNamespace.li) { - NumberedItemAnnotation thisNumberedItemAnnotation = null; - XElement listElement2 = paragraph.Ancestors().FirstOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); + NumberedItemAnnotation? thisNumberedItemAnnotation = null; + var listElement2 = paragraph.Ancestors().FirstOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); if (listElement2 != null) { thisNumberedItemAnnotation = listElement2.Annotation(); - XElement next = paragraph.ElementsAfterSelf().FirstOrDefault(); + var next = paragraph.ElementsAfterSelf().FirstOrDefault(); if (next != null && next.Name == XhtmlNoNamespace.li) { - XElement nextListElement = next.Ancestors().FirstOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); - NumberedItemAnnotation nextNumberedItemAnnotation = nextListElement.Annotation(); + var nextListElement = next.Ancestors().FirstOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); + var nextNumberedItemAnnotation = nextListElement.Annotation(); if (nextNumberedItemAnnotation != null && thisNumberedItemAnnotation.numId == nextNumberedItemAnnotation.numId) + { contextualSpacing = new XElement(W.contextualSpacing); + } } } } @@ -2885,61 +2942,69 @@ private static XElement[] GetSpacingProperties(XElement paragraph, HtmlToWmlConv private static XElement GetRunProperties(XText textNode, HtmlToWmlConverterSettings settings) { - XElement parent = textNode.Parent; - XElement rPr = GetRunProperties(parent, settings); + var parent = textNode.Parent; + var rPr = GetRunProperties(parent, settings); return rPr; } - private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSettings settings) + private static XElement? GetRunProperties(XElement element, HtmlToWmlConverterSettings settings) { - CssExpression colorProperty = element.GetProp("color"); - CssExpression fontFamilyProperty = element.GetProp("font-family"); - CssExpression fontSizeProperty = element.GetProp("font-size"); - CssExpression textDecorationProperty = element.GetProp("text-decoration"); - CssExpression fontStyleProperty = element.GetProp("font-style"); - CssExpression fontWeightProperty = element.GetProp("font-weight"); - CssExpression backgroundColorProperty = element.GetProp("background-color"); - CssExpression letterSpacingProperty = element.GetProp("letter-spacing"); - CssExpression directionProp = element.GetProp("direction"); - - string colorPropertyString = colorProperty.ToString(); - string fontFamilyString = GetUsedFontFromFontFamilyProperty(fontFamilyProperty); - TPoint? fontSizeTPoint = GetUsedSizeFromFontSizeProperty(fontSizeProperty); - string textDecorationString = textDecorationProperty.ToString(); - string fontStyleString = fontStyleProperty.ToString(); - string fontWeightString = fontWeightProperty.ToString().ToLower(); - string backgroundColorString = backgroundColorProperty.ToString().ToLower(); - string letterSpacingString = letterSpacingProperty.ToString().ToLower(); - string directionString = directionProp.ToString().ToLower(); - - bool subAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.sub).Any(); - bool supAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.sup).Any(); - bool bAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.b).Any(); - bool iAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.i).Any(); - bool strongAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.strong).Any(); - bool emAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.em).Any(); - bool uAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.u).Any(); - bool sAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.s).Any(); - - XAttribute dirAttribute = element.Attribute(XhtmlNoNamespace.dir); - string dirAttributeString = ""; + var colorProperty = element.GetProp("color"); + var fontFamilyProperty = element.GetProp("font-family"); + var fontSizeProperty = element.GetProp("font-size"); + var textDecorationProperty = element.GetProp("text-decoration"); + var fontStyleProperty = element.GetProp("font-style"); + var fontWeightProperty = element.GetProp("font-weight"); + var backgroundColorProperty = element.GetProp("background-color"); + var letterSpacingProperty = element.GetProp("letter-spacing"); + var directionProp = element.GetProp("direction"); + + var colorPropertyString = colorProperty.ToString(); + var fontFamilyString = GetUsedFontFromFontFamilyProperty(fontFamilyProperty); + var fontSizeTPoint = GetUsedSizeFromFontSizeProperty(fontSizeProperty); + var textDecorationString = textDecorationProperty.ToString(); + var fontStyleString = fontStyleProperty.ToString(); + var fontWeightString = fontWeightProperty.ToString().ToLower(); + var backgroundColorString = backgroundColorProperty.ToString().ToLower(); + var letterSpacingString = letterSpacingProperty.ToString().ToLower(); + var directionString = directionProp.ToString().ToLower(); + + var subAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.sub).Any(); + var supAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.sup).Any(); + var bAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.b).Any(); + var iAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.i).Any(); + var strongAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.strong).Any(); + var emAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.em).Any(); + var uAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.u).Any(); + var sAncestor = element.AncestorsAndSelf(XhtmlNoNamespace.s).Any(); + + var dirAttribute = element.Attribute(XhtmlNoNamespace.dir); + var dirAttributeString = ""; if (dirAttribute != null) + { dirAttributeString = dirAttribute.Value.ToLower(); + } - XElement shd = null; + XElement? shd = null; if (backgroundColorString != "transparent") + { shd = new XElement(W.shd, new XAttribute(W.val, "clear"), new XAttribute(W.color, "auto"), new XAttribute(W.fill, backgroundColorString)); + } - XElement subSuper = null; + XElement? subSuper = null; if (subAncestor) + { subSuper = new XElement(W.vertAlign, new XAttribute(W.val, "subscript")); + } else if (supAncestor) - subSuper = new XElement(W.vertAlign, new XAttribute(W.val, "superscript")); + { + subSuper = new XElement(W.vertAlign, new XAttribute(W.val, "superscript")); + } - XElement rFonts = null; + XElement? rFonts = null; if (fontFamilyString != null) { rFonts = new XElement(W.rFonts, @@ -2949,56 +3014,66 @@ private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSet } // todo I think this puts a color on every element. - XElement color = colorPropertyString != null ? + var color = colorPropertyString != null ? new XElement(W.color, new XAttribute(W.val, colorPropertyString)) : null; - XElement sz = null; - XElement szCs = null; + XElement? sz = null; + XElement? szCs = null; if (fontSizeTPoint != null) { sz = new XElement(W.sz, new XAttribute(W.val, (int)((double)fontSizeTPoint * 2))); szCs = new XElement(W.szCs, new XAttribute(W.val, (int)((double)fontSizeTPoint * 2))); } - XElement strike = null; + XElement? strike = null; if (textDecorationString == "line-through" || sAncestor) + { strike = new XElement(W.strike); + } - XElement bold = null; - XElement boldCs = null; + XElement? bold = null; + XElement? boldCs = null; if (bAncestor || strongAncestor || fontWeightString == "bold" || fontWeightString == "bolder" || fontWeightString == "600" || fontWeightString == "700" || fontWeightString == "800" || fontWeightString == "900") { bold = new XElement(W.b); boldCs = new XElement(W.bCs); } - XElement italic = null; - XElement italicCs = null; + XElement? italic = null; + XElement? italicCs = null; if (iAncestor || emAncestor || fontStyleString == "italic") { italic = new XElement(W.i); italicCs = new XElement(W.iCs); } - XElement underline = null; + XElement? underline = null; if (uAncestor || textDecorationString == "underline") + { underline = new XElement(W.u, new XAttribute(W.val, "single")); + } - XElement rStyle = null; + XElement? rStyle = null; if (element.Name == XhtmlNoNamespace.a) + { rStyle = new XElement(W.rStyle, new XAttribute(W.val, "Hyperlink")); + } - XElement spacing = null; + XElement? spacing = null; if (letterSpacingProperty.IsNotNormal) + { spacing = new XElement(W.spacing, new XAttribute(W.val, (long)(Twip)letterSpacingProperty)); + } - XElement rtl = null; + XElement? rtl = null; if (dirAttributeString == "rtl" || directionString == "rtl") + { rtl = new XElement(W.rtl); + } - XElement runProps = new XElement(W.rPr, + var runProps = new XElement(W.rPr, rStyle, rFonts, bold, @@ -3016,7 +3091,9 @@ private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSet rtl); if (runProps.Elements().Any()) + { return runProps; + } return null; } @@ -3027,92 +3104,135 @@ private static XElement GetRunProperties(XElement element, HtmlToWmlConverterSet // def and ghi. private static string GetDisplayText(XText node, bool preserveWhiteSpace) { - string textTransform = node.Parent.GetProp("text-transform").ToString(); - bool isFirst = node.Parent.Name == XhtmlNoNamespace.p && node == node.Parent.FirstNode; - bool isLast = node.Parent.Name == XhtmlNoNamespace.p && node == node.Parent.LastNode; + var textTransform = node.Parent.GetProp("text-transform").ToString(); + var isFirst = node.Parent.Name == XhtmlNoNamespace.p && node == node.Parent.FirstNode; + var isLast = node.Parent.Name == XhtmlNoNamespace.p && node == node.Parent.LastNode; - IEnumerable> groupedCharacters = null; + IEnumerable>? groupedCharacters = null; if (preserveWhiteSpace) + { groupedCharacters = node.Value.GroupAdjacent(c => c == '\r' || c == '\n'); + } else + { groupedCharacters = node.Value.GroupAdjacent(c => c == ' ' || c == '\r' || c == '\n'); + } - string newString = groupedCharacters.Select(g => + var newString = groupedCharacters.Select(g => { - if (g.Key == true) + if (g.Key) + { return " "; - string x = g.Select(c => c.ToString()).StringConcatenate(); + } + + var x = g.Select(c => c.ToString()).StringConcatenate(); return x; }) .StringConcatenate(); if (!preserveWhiteSpace) { if (isFirst) + { newString = newString.TrimStart(); + } + if (isLast) + { newString = newString.TrimEnd(); + } } if (textTransform == "uppercase") + { newString = newString.ToUpper(); + } else if (textTransform == "lowercase") + { newString = newString.ToLower(); + } else if (textTransform == "capitalize") + { newString = newString.Substring(0, 1).ToUpper() + newString.Substring(1).ToLower(); + } + return newString; } - private static XElement GetNumberingProperties(XElement paragraph, HtmlToWmlConverterSettings settings) + private static XElement? GetNumberingProperties(XElement paragraph) { // Numbering properties ****************************************************** - NumberedItemAnnotation numberedItemAnnotation = null; - XElement listElement = paragraph.Ancestors().FirstOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); + NumberedItemAnnotation? numberedItemAnnotation = null; + var listElement = paragraph.Ancestors().FirstOrDefault(a => a.Name == XhtmlNoNamespace.ol || a.Name == XhtmlNoNamespace.ul); if (listElement != null) { numberedItemAnnotation = listElement.Annotation(); } - XElement numPr = null; + XElement? numPr = null; if (paragraph.Name == XhtmlNoNamespace.li) + { numPr = new XElement(W.numPr, new XElement(W.ilvl, new XAttribute(W.val, numberedItemAnnotation.ilvl)), new XElement(W.numId, new XAttribute(W.val, numberedItemAnnotation.numId))); + } + return numPr; } - private static XElement GetJustification(XElement blockLevelElement, HtmlToWmlConverterSettings settings) + private static XElement? GetJustification(XElement blockLevelElement) { // Justify ****************************************************** - CssExpression textAlignProperty = blockLevelElement.GetProp("text-align"); + var textAlignProperty = blockLevelElement.GetProp("text-align"); string textAlign; if (blockLevelElement.Name == XhtmlNoNamespace.caption || blockLevelElement.Name == XhtmlNoNamespace.th) + { textAlign = "center"; + } else + { textAlign = "left"; + } + if (textAlignProperty != null) + { textAlign = textAlignProperty.ToString(); - string jc = null; + } + + string? jc = null; if (textAlign == "center") + { jc = "center"; + } else { if (textAlign == "right") + { jc = "right"; + } else { if (textAlign == "justify") + { jc = "both"; + } } } - string direction = GetDirection(blockLevelElement); + var direction = GetDirection(blockLevelElement); if (direction == "rtl") { if (jc == "left") + { jc = "right"; + } else if (jc == "right") + { jc = "left"; + } } - XElement jcElement = null; + XElement? jcElement = null; if (jc != null) + { jcElement = new XElement(W.jc, new XAttribute(W.val, jc)); + } + return jcElement; } @@ -3122,7 +3242,7 @@ private class HeadingInfo public string StyleName; }; - private static HeadingInfo[] HeadingTagMap = new[] + private static readonly HeadingInfo[] HeadingTagMap = new[] { new HeadingInfo { Name = XhtmlNoNamespace.h1, StyleName = "Heading1" }, new HeadingInfo { Name = XhtmlNoNamespace.h2, StyleName = "Heading2" }, @@ -3136,40 +3256,46 @@ private class HeadingInfo private static string GetDirection(XElement element) { - string retValue = "ltr"; - string dirString = (string)element.Attribute(XhtmlNoNamespace.dir); + var retValue = "ltr"; + var dirString = (string)element.Attribute(XhtmlNoNamespace.dir); if (dirString != null && dirString.ToLower() == "rtl") + { retValue = "rtl"; - CssExpression directionProp = element.GetProp("direction"); + } + + var directionProp = element.GetProp("direction"); if (directionProp != null) { - string directionValue = directionProp.ToString(); + var directionValue = directionProp.ToString(); if (directionValue.ToLower() == "rtl") + { retValue = "rtl"; + } } return retValue; } private static XElement GetTableProperties(XElement element) { - - XElement bidiVisual = null; - string direction = GetDirection(element); + XElement? bidiVisual = null; + var direction = GetDirection(element); if (direction == "rtl") + { bidiVisual = new XElement(W.bidiVisual); + } - XElement tblPr = new XElement(W.tblPr, + var tblPr = new XElement(W.tblPr, bidiVisual, GetTableWidth(element), GetTableCellSpacing(element), GetBlockContentBorders(element, W.tblBorders, false), - GetTableShading(element), - GetTableCellMargins(element), - GetTableLook(element)); + GetTableShading(), + GetTableCellMargins(), + GetTableLook()); return tblPr; } - private static XElement GetTableShading(XElement element) + private static XElement? GetTableShading() { // todo this is not done. // needs to work for W.tbl and W.tc @@ -3183,14 +3309,14 @@ private static XElement GetTableShading(XElement element) private static XElement GetTableWidth(XElement element) { - CssExpression width = element.GetProp("width"); + var width = element.GetProp("width"); if (width.IsAuto) { return new XElement(W.tblW, new XAttribute(W._w, "0"), new XAttribute(W.type, "auto")); } - XElement widthElement = new XElement(W.tblW, + var widthElement = new XElement(W.tblW, new XAttribute(W._w, (long)(Twip)width), new XAttribute(W.type, "dxa")); return widthElement; @@ -3198,34 +3324,40 @@ private static XElement GetTableWidth(XElement element) private static XElement GetCellWidth(XElement element) { - CssExpression width = element.GetProp("width"); + var width = element.GetProp("width"); if (width.IsAuto) { return new XElement(W.tcW, new XAttribute(W._w, "0"), new XAttribute(W.type, "auto")); } - XElement widthElement = new XElement(W.tcW, + var widthElement = new XElement(W.tcW, new XAttribute(W._w, (long)(Twip)width), new XAttribute(W.type, "dxa")); return widthElement; } - private static XElement GetBlockContentBorders(XElement element, XName borderXName, bool forParagraph) + private static XElement? GetBlockContentBorders(XElement element, XName borderXName, bool forParagraph) { if ((element.Name == XhtmlNoNamespace.td || element.Name == XhtmlNoNamespace.th || element.Name == XhtmlNoNamespace.caption) && forParagraph) + { return null; - XElement borders = new XElement(borderXName, + } + + var borders = new XElement(borderXName, new XElement(W.top, GetBorderAttributes(element, "top")), new XElement(W.left, GetBorderAttributes(element, "left")), new XElement(W.bottom, GetBorderAttributes(element, "bottom")), new XElement(W.right, GetBorderAttributes(element, "right"))); - if (borders.Elements().Attributes(W.val).Where(v => (string)v == "none").Count() == 4) + if (borders.Elements().Attributes(W.val).Count(v => (string)v == "none") == 4) + { return null; + } + return borders; } - private static Dictionary BorderStyleMap = new Dictionary() + private static readonly Dictionary BorderStyleMap = new Dictionary() { { "none", "none" }, { "hidden", "none" }, @@ -3241,12 +3373,10 @@ private static XElement GetBlockContentBorders(XElement element, XName borderXNa private static List GetBorderAttributes(XElement element, string whichBorder) { - //if (whichBorder == "right") - // Console.WriteLine(1); - CssExpression styleProp = element.GetProp(string.Format("border-{0}-style", whichBorder)); - CssExpression colorProp = element.GetProp(string.Format("border-{0}-color", whichBorder)); - CssExpression paddingProp = element.GetProp(string.Format("padding-{0}", whichBorder)); - CssExpression marginProp = element.GetProp(string.Format("margin-{0}", whichBorder)); + var styleProp = element.GetProp(string.Format("border-{0}-style", whichBorder)); + var colorProp = element.GetProp(string.Format("border-{0}-color", whichBorder)); + var paddingProp = element.GetProp(string.Format("padding-{0}", whichBorder)); + var marginProp = element.GetProp(string.Format("margin-{0}", whichBorder)); // The space attribute is equivalent to the margin properties of CSS // the ind element of the parent is more or less equivalent to the padding properties of CSS, except that ind takes space @@ -3255,38 +3385,29 @@ private static List GetBorderAttributes(XElement element, string whi // if there is no border, and yet there is padding, then need to create a thin border so that word will display the background // color of the paragraph properly (including padding). - XAttribute val = null; - XAttribute sz = null; - XAttribute space = null; - XAttribute color = null; + XAttribute? val = null; + XAttribute? color = null; if (styleProp != null) { if (BorderStyleMap.ContainsKey(styleProp.ToString())) + { val = new XAttribute(W.val, BorderStyleMap[styleProp.ToString()]); + } else + { val = new XAttribute(W.val, "none"); + } } double borderSizeInTwips = GetBorderSize(element, whichBorder); - double borderSizeInOneEighthPoint = borderSizeInTwips / 20 * 8; - sz = new XAttribute(W.sz, (int)borderSizeInOneEighthPoint); - + var borderSizeInOneEighthPoint = borderSizeInTwips / 20 * 8; + var sz = new XAttribute(W.sz, (int)borderSizeInOneEighthPoint); + XAttribute space; if (element.Name == XhtmlNoNamespace.td || element.Name == XhtmlNoNamespace.th) { space = new XAttribute(W.space, "0"); -#if false - // 2012-05-14 todo alternative algorithm for margin for cells - if (marginProp != null) - { - // space is specified in points, not twips - TPoint points = 0; - if (marginProp.IsNotAuto) - points = (TPoint)marginProp; - space = new XAttribute(W.space, Math.Min(31, (double)points)); - } -#endif } else { @@ -3294,26 +3415,27 @@ private static List GetBorderAttributes(XElement element, string whi if (paddingProp != null) { // space is specified in points, not twips - TPoint points = (TPoint)paddingProp; - space = new XAttribute(W.space, (int)(Math.Min(31, (double)points))); + var points = (TPoint)paddingProp; + space = new XAttribute(W.space, (int)Math.Min(31, points)); } } if (colorProp != null) + { color = new XAttribute(W.color, colorProp.ToString()); + } // no default yet if ((string)val == "none" && (double)space != 0d) { val.Value = "single"; sz.Value = "0"; - //color.Value = "FF0000"; } // sz is in 1/8 of a point // space is in 1/20 of a point - List attList = new List() + var attList = new List() { val, sz, @@ -3325,28 +3447,20 @@ private static List GetBorderAttributes(XElement element, string whi private static Twip GetBorderSize(XElement element, string whichBorder) { - CssExpression widthProp = element.GetProp(string.Format("border-{0}-width", whichBorder)); + var widthProp = element.GetProp(string.Format("border-{0}-width", whichBorder)); - if (widthProp != null && widthProp.Terms.Count() == 1) + if (widthProp != null && widthProp.Terms.Count == 1) { - CssTerm term = widthProp.Terms.First(); - Twip twips = (Twip)widthProp; + var term = widthProp.Terms.First(); + var twips = (Twip)widthProp; return twips; } return 12; } - private static XElement GetTableLook(XElement element) + private static XElement GetTableLook() { - XElement tblLook = XElement.Parse( - //@"" + var tblLook = XElement.Parse( @"" @@ -3361,19 +3475,19 @@ private static XElement GetTableGrid(XElement element, HtmlToWmlConverterSetting Twip? marginLeft = (int?)settings.SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); Twip? marginRight = (int?)settings.SectPr.Elements(W.pgMar).Attributes(W.right).FirstOrDefault(); Twip printable = (long)pageWidthInTwips - (long)marginLeft - (long)marginRight; - XElement[][] tableArray = GetTableArray(element); - int numberColumns = tableArray[0].Length; - CssExpression[] columnWidths = new CssExpression[numberColumns]; - for (int c = 0; c < numberColumns; c++) + var tableArray = GetTableArray(element); + var numberColumns = tableArray[0].Length; + var columnWidths = new CssExpression[numberColumns]; + for (var c = 0; c < numberColumns; c++) { - CssExpression columnWidth = new CssExpression { Terms = new List { new CssTerm { Value = "auto" } } }; - for (int r = 0; r < tableArray.Length; ++r) + var columnWidth = new CssExpression { Terms = new List { new CssTerm { Value = "auto" } } }; + for (var r = 0; r < tableArray.Length; ++r) { if (tableArray[r][c] != null) { - XElement cell = tableArray[r][c]; - CssExpression width = cell.GetProp("width"); - XAttribute colSpan = cell.Attribute(XhtmlNoNamespace.colspan); + var cell = tableArray[r][c]; + var width = cell.GetProp("width"); + var colSpan = cell.Attribute(XhtmlNoNamespace.colspan); if (colSpan == null && columnWidth.ToString() == "auto" && width.ToString() != "auto") { columnWidth = width; @@ -3384,22 +3498,21 @@ private static XElement GetTableGrid(XElement element, HtmlToWmlConverterSetting columnWidths[c] = columnWidth; } - XElement tblGrid = new XElement(W.tblGrid, + var tblGrid = new XElement(W.tblGrid, columnWidths.Select(cw => new XElement(W.gridCol, - new XAttribute(W._w, (long)GetTwipWidth(cw, (int)printable))))); + new XAttribute(W._w, (long)GetTwipWidth(cw))))); return tblGrid; } - private static Twip GetTwipWidth(CssExpression columnWidth, int printable) + private static Twip GetTwipWidth(CssExpression columnWidth) { Twip defaultTwipWidth = 1440; - if (columnWidth.Terms.Count() == 1) + if (columnWidth.Terms.Count == 1) { - CssTerm term = columnWidth.Terms.First(); + var term = columnWidth.Terms.First(); if (term.Unit == CssUnit.PT) { - Double ptValue; - if (Double.TryParse(term.Value, out ptValue)) + if (double.TryParse(term.Value, out var ptValue)) { Twip twips = (long)(ptValue * 20); return twips; @@ -3412,14 +3525,14 @@ private static Twip GetTwipWidth(CssExpression columnWidth, int printable) private static XElement[][] GetTableArray(XElement table) { - List rowList = table.DescendantsTrimmed(XhtmlNoNamespace.table).Where(e => e.Name == XhtmlNoNamespace.tr).ToList(); - int numberColumns = rowList.Select(r => r.Elements().Where(e => e.Name == XhtmlNoNamespace.td || e.Name == XhtmlNoNamespace.th).Count()).Max(); - XElement[][] tableArray = new XElement[rowList.Count()][]; - int rowNumber = 0; + var rowList = table.DescendantsTrimmed(XhtmlNoNamespace.table).Where(e => e.Name == XhtmlNoNamespace.tr).ToList(); + var numberColumns = rowList.Select(r => r.Elements().Count(e => e.Name == XhtmlNoNamespace.td || e.Name == XhtmlNoNamespace.th)).Max(); + var tableArray = new XElement[rowList.Count][]; + var rowNumber = 0; foreach (var row in rowList) { tableArray[rowNumber] = new XElement[numberColumns]; - int columnNumber = 0; + var columnNumber = 0; foreach (var cell in row.Elements(XhtmlNoNamespace.td)) { tableArray[rowNumber][columnNumber] = cell; @@ -3432,25 +3545,27 @@ private static XElement[][] GetTableArray(XElement table) private static XElement GetCellPropertiesForCaption(XElement element) { - XElement gridSpan = new XElement(W.gridSpan, + var gridSpan = new XElement(W.gridSpan, new XAttribute(W.val, 3)); - XElement tcBorders = GetBlockContentBorders(element, W.tcBorders, false); + var tcBorders = GetBlockContentBorders(element, W.tcBorders, false); if (tcBorders == null) + { tcBorders = new XElement(W.tcBorders, new XElement(W.top, new XAttribute(W.val, "nil")), new XElement(W.left, new XAttribute(W.val, "nil")), new XElement(W.bottom, new XAttribute(W.val, "nil")), new XElement(W.right, new XAttribute(W.val, "nil"))); + } - XElement shd = GetCellShading(element); + var shd = GetCellShading(element); //XElement hideMark = new XElement(W.hideMark); - XElement hideMark = null; + XElement? hideMark = null; - XElement tcMar = GetCellMargins(element); + var tcMar = GetCellMargins(element); - XElement vAlign = new XElement(W.vAlign, new XAttribute(W.val, "center")); + var vAlign = new XElement(W.vAlign, new XAttribute(W.val, "center")); return new XElement(W.tcPr, gridSpan, @@ -3463,41 +3578,52 @@ private static XElement GetCellPropertiesForCaption(XElement element) private static XElement GetCellProperties(XElement element) { - int? colspan = (int?)element.Attribute(XhtmlNoNamespace.colspan); - XElement gridSpan = null; + var colspan = (int?)element.Attribute(XhtmlNoNamespace.colspan); + XElement? gridSpan = null; if (colspan != null) + { gridSpan = new XElement(W.gridSpan, new XAttribute(W.val, colspan)); + } - XElement tblW = GetCellWidth(element); + var tblW = GetCellWidth(element); - XElement tcBorders = GetBlockContentBorders(element, W.tcBorders, false); + var tcBorders = GetBlockContentBorders(element, W.tcBorders, false); - XElement shd = GetCellShading(element); + var shd = GetCellShading(element); - //XElement hideMark = new XElement(W.hideMark); - XElement hideMark = null; + XElement? hideMark = null; - XElement tcMar = GetCellMargins(element); + var tcMar = GetCellMargins(element); - XElement vAlign = new XElement(W.vAlign, new XAttribute(W.val, "center")); + var vAlign = new XElement(W.vAlign, new XAttribute(W.val, "center")); - XElement vMerge = null; + XElement? vMerge = null; if (element.Attribute("HtmlToWmlVMergeNoRestart") != null) + { vMerge = new XElement(W.vMerge); + } else if (element.Attribute("HtmlToWmlVMergeRestart") != null) - vMerge = new XElement(W.vMerge, - new XAttribute(W.val, "restart")); + { + vMerge = new XElement(W.vMerge, + new XAttribute(W.val, "restart")); + } - string vAlignValue = (string)element.Attribute(XhtmlNoNamespace.valign); - CssExpression verticalAlignmentProp = element.GetProp("vertical-align"); + var vAlignValue = (string)element.Attribute(XhtmlNoNamespace.valign); + var verticalAlignmentProp = element.GetProp("vertical-align"); if (verticalAlignmentProp != null && verticalAlignmentProp.ToString() != "inherit") + { vAlignValue = verticalAlignmentProp.ToString(); + } + if (vAlignValue != null) { - if (vAlignValue == "middle" || (vAlignValue != "top" && vAlignValue != "bottom")) + if (vAlignValue == "middle" || vAlignValue != "top" && vAlignValue != "bottom") + { vAlignValue = "center"; + } + vAlign = new XElement(W.vAlign, new XAttribute(W.val, vAlignValue)); } @@ -3520,18 +3646,18 @@ private static XElement GetCellHeaderProperties(XElement element) // gridSpan = new XElement(W.gridSpan, // new XAttribute(W.val, colspan)); - XElement tblW = GetCellWidth(element); + var tblW = GetCellWidth(element); - XElement tcBorders = GetBlockContentBorders(element, W.tcBorders, false); + var tcBorders = GetBlockContentBorders(element, W.tcBorders, false); - XElement shd = GetCellShading(element); + var shd = GetCellShading(element); //XElement hideMark = new XElement(W.hideMark); - XElement hideMark = null; + XElement? hideMark = null; - XElement tcMar = GetCellMargins(element); + var tcMar = GetCellMargins(element); - XElement vAlign = new XElement(W.vAlign, new XAttribute(W.val, "center")); + var vAlign = new XElement(W.vAlign, new XAttribute(W.val, "center")); return new XElement(W.tcPr, tblW, @@ -3542,12 +3668,12 @@ private static XElement GetCellHeaderProperties(XElement element) hideMark); } - private static XElement GetCellShading(XElement element) + private static XElement? GetCellShading(XElement element) { - CssExpression backgroundColorProp = element.GetProp("background-color"); - if (backgroundColorProp != null && (string)backgroundColorProp != "transparent") + var backgroundColorProp = element.GetProp("background-color"); + if (backgroundColorProp != null && backgroundColorProp != "transparent") { - XElement shd = new XElement(W.shd, + var shd = new XElement(W.shd, new XAttribute(W.val, "clear"), new XAttribute(W.color, "auto"), new XAttribute(W.fill, backgroundColorProp)); @@ -3556,96 +3682,109 @@ private static XElement GetCellShading(XElement element) return null; } - private static XElement GetCellMargins(XElement element) + private static XElement? GetCellMargins(XElement element) { - CssExpression topProp = element.GetProp("padding-top"); - CssExpression leftProp = element.GetProp("padding-left"); - CssExpression bottomProp = element.GetProp("padding-bottom"); - CssExpression rightProp = element.GetProp("padding-right"); + var topProp = element.GetProp("padding-top"); + var leftProp = element.GetProp("padding-left"); + var bottomProp = element.GetProp("padding-bottom"); + var rightProp = element.GetProp("padding-right"); if ((long)topProp == 0 && (long)leftProp == 0 && (long)bottomProp == 0 && (long)rightProp == 0) + { return null; - XElement top = null; + } + + XElement? top = null; if (topProp != null) + { top = new XElement(W.top, new XAttribute(W._w, (long)(Twip)topProp), new XAttribute(W.type, "dxa")); - XElement left = null; + } + + XElement? left = null; if (leftProp != null) + { left = new XElement(W.left, new XAttribute(W._w, (long)(Twip)leftProp), new XAttribute(W.type, "dxa")); - XElement bottom = null; + } + + XElement? bottom = null; if (bottomProp != null) + { bottom = new XElement(W.bottom, new XAttribute(W._w, (long)(Twip)bottomProp), new XAttribute(W.type, "dxa")); - XElement right = null; + } + + XElement? right = null; if (rightProp != null) + { right = new XElement(W.right, new XAttribute(W._w, (long)(Twip)rightProp), new XAttribute(W.type, "dxa")); - XElement tcMar = new XElement(W.tcMar, + } + + var tcMar = new XElement(W.tcMar, top, left, bottom, right); if (tcMar.Elements().Any()) + { return tcMar; + } + return null; } -#if false - - - - - - -#endif - - private static XElement GetTableCellSpacing(XElement element) + private static XElement? GetTableCellSpacing(XElement element) { - XElement table = element.AncestorsAndSelf(XhtmlNoNamespace.table).FirstOrDefault(); - XElement tblCellSpacing = null; + var table = element.AncestorsAndSelf(XhtmlNoNamespace.table).FirstOrDefault(); + XElement? tblCellSpacing = null; if (table != null) { - CssExpression borderCollapse = table.GetProp("border-collapse"); - if (borderCollapse == null || (string)borderCollapse != "collapse") + var borderCollapse = table.GetProp("border-collapse"); + if (borderCollapse == null || borderCollapse != "collapse") { // todo very incomplete - CssExpression borderSpacing = table.GetProp("border-spacing"); - CssExpression marginTopProperty = element.GetProp("margin-top"); + var borderSpacing = table.GetProp("border-spacing"); + var marginTopProperty = element.GetProp("margin-top"); if (marginTopProperty == null) + { marginTopProperty = new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT } } }; - CssExpression marginBottomProperty = element.GetProp("margin-bottom"); + } + + var marginBottomProperty = element.GetProp("margin-bottom"); if (marginBottomProperty == null) + { marginBottomProperty = new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT } } }; - Twip twips1 = (Twip)marginTopProperty; - Twip twips2 = (Twip)marginBottomProperty; + } + + var twips1 = (Twip)marginTopProperty; + var twips2 = (Twip)marginBottomProperty; Twip minTwips = 15; if (borderSpacing != null) + { minTwips = (Twip)borderSpacing; - long twipToUse = Math.Max((long)twips1, (long)twips2); - twipToUse = Math.Max(twipToUse, (long)minTwips); + } + + var twipToUse = Math.Max(twips1, twips2); + twipToUse = Math.Max(twipToUse, minTwips); // have to divide twipToUse by 2 because border-spacing specifies the space between the border of once cell and its adjacent. // tblCellSpacing specifies the distance between the border and the half way point between two cells. - long twipToUseOverTwo = (long)twipToUse / 2; + var twipToUseOverTwo = twipToUse / 2; tblCellSpacing = new XElement(W.tblCellSpacing, new XAttribute(W._w, twipToUseOverTwo), new XAttribute(W.type, "dxa")); } - } return tblCellSpacing; } - private static XElement GetTableCellMargins(XElement element) + private static XElement GetTableCellMargins() { // todo very incomplete - XElement tblCellMar = XElement.Parse( + var tblCellMar = XElement.Parse( @" @@ -3660,23 +3799,13 @@ private static XElement GetTableCellMargins(XElement element) return tblCellMar; } - private static XElement GetTableRowProperties(XElement element) + private static XElement? GetTableRowProperties(XElement element) { - XElement trPr = null; - XElement table = element.AncestorsAndSelf(XhtmlNoNamespace.table).FirstOrDefault(); + XElement? trPr = null; + var table = element.AncestorsAndSelf(XhtmlNoNamespace.table).FirstOrDefault(); if (table != null) { - CssExpression heightProperty = element.GetProp("height"); - //long? maxCellHeight = element.Elements(Xhtml.td).Aggregate((long?)null, - // (XElement td, long? last) => - // { - // Expression heightProp2 = td.GetProp("height"); - // if (heightProp2 == null) - // return last; - // if (last == null) - // return (long)(Twip)heightProp2; - // return last + (long?)(long)(Twip)heightProp2; - // }); + var heightProperty = element.GetProp("height"); var cellHeights = element .Elements(XhtmlNoNamespace.td) .Select(td => td.GetProp("height")) @@ -3684,37 +3813,43 @@ private static XElement GetTableRowProperties(XElement element) .Where(d => d != null) .Select(e => (long)(Twip)e) .ToList(); - XElement trHeight = null; + XElement? trHeight = null; if (cellHeights.Any()) { - long max = cellHeights.Max(); + var max = cellHeights.Max(); trHeight = new XElement(W.trHeight, new XAttribute(W.val, max)); } - CssExpression borderCollapseProperty = table.GetProp("border-collapse"); - XElement borderCollapse = null; - if (borderCollapseProperty != null && (string)borderCollapseProperty != "collapse") + var borderCollapseProperty = table.GetProp("border-collapse"); + XElement? borderCollapse = null; + if (borderCollapseProperty != null && borderCollapseProperty != "collapse") + { borderCollapse = GetTableCellSpacing(element); + } trPr = new XElement(W.trPr, GetTableCellSpacing(element), trHeight); if (trPr.Elements().Any()) + { return trPr; + } } return trPr; } - private static XAttribute GetXmlSpaceAttribute(string value) + private static XAttribute? GetXmlSpaceAttribute(string value) { if (value.StartsWith(" ") || value.EndsWith(" ")) + { return new XAttribute(XNamespace.Xml + "space", "preserve"); + } + return null; } - - private static Dictionary InstalledFonts = new Dictionary + private static readonly Dictionary InstalledFonts = new Dictionary { {"serif", "Times New Roman"}, {"sans-serif", "Arial"}, @@ -4123,15 +4258,20 @@ private static XAttribute GetXmlSpaceAttribute(string value) private static TPoint? GetUsedSizeFromFontSizeProperty(CssExpression fontSize) { if (fontSize == null) + { return null; - if (fontSize.Terms.Count() == 1) + } + + if (fontSize.Terms.Count == 1) { - CssTerm term = fontSize.Terms.First(); - double size = 0; + var term = fontSize.Terms.First(); if (term.Unit == CssUnit.PT) { - if (double.TryParse(term.Value, out size)) + if (double.TryParse(term.Value, out var size)) + { return new TPoint(size); + } + return null; } return null; @@ -4139,26 +4279,32 @@ private static XAttribute GetXmlSpaceAttribute(string value) return null; } - private static string GetUsedFontFromFontFamilyProperty(CssExpression fontFamily) + private static string? GetUsedFontFromFontFamilyProperty(CssExpression fontFamily) { if (fontFamily == null) + { return null; - string fullFontFamily = fontFamily.Terms.Select(t => t + " ").StringConcatenate().Trim(); - string lcfont = fullFontFamily.ToLower(); + } + + var fullFontFamily = fontFamily.Terms.Select(t => t + " ").StringConcatenate().Trim(); + var lcfont = fullFontFamily.ToLower(); if (InstalledFonts.ContainsKey(lcfont)) + { return InstalledFonts[lcfont]; + } + return null; } - private static XElement GetBackgroundProperty(XElement element) + private static XElement? GetBackgroundProperty(XElement element) { - CssExpression color = element.GetProp("background-color"); + var color = element.GetProp("background-color"); // todo this really should test against default background color if (color.ToString() != "transparent") { - string hexString = color.ToString(); - XElement shd = new XElement(W.shd, + var hexString = color.ToString(); + var shd = new XElement(W.shd, new XAttribute(W.val, "clear"), new XAttribute(W.color, "auto"), new XAttribute(W.fill, hexString)); @@ -4166,7 +4312,6 @@ private static XElement GetBackgroundProperty(XElement element) } return null; } - } public class PictureId @@ -4174,18 +4319,17 @@ public class PictureId public int Id; } - class HtmlToWmlFontUpdater + internal class HtmlToWmlFontUpdater { - public static void UpdateFontsPart(WordprocessingDocument wDoc, XElement html, HtmlToWmlConverterSettings settings) + public static void UpdateFontsPart(WordprocessingDocument wDoc, HtmlToWmlConverterSettings settings) { - XDocument fontXDoc = wDoc.MainDocumentPart.FontTablePart.GetXDocument(); + var fontXDoc = wDoc.MainDocumentPart.FontTablePart.GetXDocument(); PtUtils.AddElementIfMissing(fontXDoc, fontXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading1") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading1"), @" @@ -4203,15 +4347,15 @@ public static void UpdateFontsPart(WordprocessingDocument wDoc, XElement html, H } } - class NumberingUpdater + internal class NumberingUpdater { public static void InitializeNumberingPart(WordprocessingDocument wDoc) { - NumberingDefinitionsPart numberingPart = wDoc.MainDocumentPart.NumberingDefinitionsPart; + var numberingPart = wDoc.MainDocumentPart.NumberingDefinitionsPart; if (numberingPart == null) { wDoc.MainDocumentPart.AddNewPart(); - XDocument npXDoc = new XDocument( + var npXDoc = new XDocument( new XElement(W.numbering, new XAttribute(XNamespace.Xmlns + "w", W.w))); wDoc.MainDocumentPart.NumberingDefinitionsPart.PutXDocument(npXDoc); @@ -4221,13 +4365,13 @@ public static void InitializeNumberingPart(WordprocessingDocument wDoc) public static void GetNextNumId(WordprocessingDocument wDoc, out int nextNumId) { InitializeNumberingPart(wDoc); - NumberingDefinitionsPart numberingPart = wDoc.MainDocumentPart.NumberingDefinitionsPart; - XDocument numberingXDoc = numberingPart.GetXDocument(); + var numberingPart = wDoc.MainDocumentPart.NumberingDefinitionsPart; + var numberingXDoc = numberingPart.GetXDocument(); nextNumId = numberingXDoc.Root.Elements(W.num).Attributes(W.numId).Select(ni => (int)ni).Concat(new[] { 1 }).Max(); } // decimal, lowerLetter - private static string OrderedListAbstractXml = + private static readonly string OrderedListAbstractXml = @" @@ -4367,7 +4511,7 @@ public static void GetNextNumId(WordprocessingDocument wDoc, out int nextNumId) "; - private static string BulletAbstractXml = + private static readonly string BulletAbstractXml = @" @@ -4517,52 +4661,55 @@ public static void GetNextNumId(WordprocessingDocument wDoc, out int nextNumId) "; - public static void UpdateNumberingPart(WordprocessingDocument wDoc, XElement html, HtmlToWmlConverterSettings settings) + public static void UpdateNumberingPart(WordprocessingDocument wDoc, XElement html) { InitializeNumberingPart(wDoc); - NumberingDefinitionsPart numberingPart = wDoc.MainDocumentPart.NumberingDefinitionsPart; - XDocument numberingXDoc = numberingPart.GetXDocument(); + var numberingPart = wDoc.MainDocumentPart.NumberingDefinitionsPart; + var numberingXDoc = numberingPart.GetXDocument(); int nextAbstractId, nextNumId; nextNumId = numberingXDoc.Root.Elements(W.num).Attributes(W.numId).Select(ni => (int)ni).Concat(new[] { 1 }).Max(); nextAbstractId = numberingXDoc.Root.Elements(W.abstractNum).Attributes(W.abstractNumId).Select(ani => (int)ani).Concat(new[] { 0 }).Max(); var numberingElements = html.DescendantsAndSelf().Where(d => d.Name == XhtmlNoNamespace.ol || d.Name == XhtmlNoNamespace.ul).ToList(); - Dictionary numToAbstractNum = new Dictionary(); + var numToAbstractNum = new Dictionary(); // add abstract numbering elements - int currentNumId = nextNumId; - int currentAbstractId = nextAbstractId; + var currentNumId = nextNumId; + var currentAbstractId = nextAbstractId; foreach (var list in numberingElements) { - HtmlToWmlConverterCore.NumberedItemAnnotation nia = list.Annotation(); + var nia = list.Annotation(); if (!numToAbstractNum.ContainsKey(nia.numId)) { numToAbstractNum.Add(nia.numId, currentAbstractId); if (list.Name == XhtmlNoNamespace.ul) { - XElement bulletAbstract = XElement.Parse(String.Format(BulletAbstractXml, currentAbstractId++)); + var bulletAbstract = XElement.Parse(string.Format(BulletAbstractXml, currentAbstractId++)); numberingXDoc.Root.Add(bulletAbstract); } if (list.Name == XhtmlNoNamespace.ol) { - string[] numFmt = new string[9]; - string[] just = new string[9]; - for (int i = 0; i < numFmt.Length; ++i) + var numFmt = new string[9]; + var just = new string[9]; + for (var i = 0; i < numFmt.Length; ++i) { numFmt[i] = "decimal"; just[i] = "left"; - XElement itemAtLevel = numberingElements + var itemAtLevel = numberingElements .FirstOrDefault(nf => { - HtmlToWmlConverterCore.NumberedItemAnnotation n = nf.Annotation(); + var n = nf.Annotation(); if (n != null && n.numId == nia.numId && n.ilvl == i) + { return true; + } + return false; }); if (itemAtLevel != null) { - HtmlToWmlConverterCore.NumberedItemAnnotation thisLevelNia = itemAtLevel.Annotation(); - string thisLevelNumFmt = thisLevelNia.listStyleType; + var thisLevelNia = itemAtLevel.Annotation(); + var thisLevelNumFmt = thisLevelNia.listStyleType; if (thisLevelNumFmt == "lower-alpha" || thisLevelNumFmt == "lower-latin") { numFmt[i] = "lowerLetter"; @@ -4591,7 +4738,7 @@ public static void UpdateNumberingPart(WordprocessingDocument wDoc, XElement htm } } - XElement simpleNumAbstract = XElement.Parse(String.Format(OrderedListAbstractXml, currentAbstractId++, + var simpleNumAbstract = XElement.Parse(string.Format(OrderedListAbstractXml, currentAbstractId++, numFmt[0], just[0], numFmt[1], just[1], numFmt[2], just[2], numFmt[3], just[3], numFmt[4], just[4], numFmt[5], just[5], numFmt[6], just[6], numFmt[7], just[7], numFmt[8], just[8])); numberingXDoc.Root.Add(simpleNumAbstract); } @@ -4606,31 +4753,26 @@ public static void UpdateNumberingPart(WordprocessingDocument wDoc, XElement htm } wDoc.MainDocumentPart.NumberingDefinitionsPart.PutXDocument(); -#if false - - - -#endif } } - class StylesUpdater + internal class StylesUpdater { public static void UpdateStylesPart( WordprocessingDocument wDoc, XElement html, HtmlToWmlConverterSettings settings, - CssDocument defaultCssDoc, - CssDocument authorCssDoc, - CssDocument userCssDoc) + CssDocument authorCssDoc) { - XDocument styleXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); + var styleXDoc = wDoc.MainDocumentPart.StyleDefinitionsPart.GetXDocument(); if (settings.DefaultSpacingElement != null) { - XElement spacingElement = styleXDoc.Root.Elements(W.docDefaults).Elements(W.pPrDefault).Elements(W.pPr).Elements(W.spacing).FirstOrDefault(); + var spacingElement = styleXDoc.Root.Elements(W.docDefaults).Elements(W.pPrDefault).Elements(W.pPr).Elements(W.spacing).FirstOrDefault(); if (spacingElement != null) + { spacingElement.ReplaceWith(settings.DefaultSpacingElement); + } } var classes = html @@ -4640,24 +4782,21 @@ public static void UpdateStylesPart( foreach (var item in classes) { - //string item = "ms-rteStyle-Byline"; foreach (var ruleSet in authorCssDoc.RuleSets) { - var selector = ruleSet.Selectors.Where( - sel => + var selector = ruleSet.Selectors.FirstOrDefault(sel => { - bool found = sel.SimpleSelectors.Count() == 1 && + var found = sel.SimpleSelectors.Count == 1 && sel.SimpleSelectors.First().Class == item && (sel.SimpleSelectors.First().ElementName == "" || sel.SimpleSelectors.First().ElementName == null); return found; - }).FirstOrDefault(); + }); var color = ruleSet.Declarations.FirstOrDefault(d => d.Name == "color"); if (selector != null) { - //Console.WriteLine("found ruleset and selector for {0}", item); - string styleName = item.ToLower(); - XElement newStyle = new XElement(W.style, + var styleName = item.ToLower(); + var newStyle = new XElement(W.style, new XAttribute(W.type, "paragraph"), new XAttribute(W.customStyle, "1"), new XAttribute(W.styleId, styleName), @@ -4681,20 +4820,21 @@ public static void UpdateStylesPart( if (styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && ((string)e.Attribute(W.styleId)).ToLower() == styleName) - .FirstOrDefault() == null) +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && ((string)e.Attribute(W.styleId)).ToLower() == styleName) == null) + { styleXDoc.Root.Add(newStyle); + } } } } if (html.Descendants(XhtmlNoNamespace.h1).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading1") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading1"), @" @@ -4725,14 +4865,15 @@ public static void UpdateStylesPart( "); + } if (html.Descendants(XhtmlNoNamespace.h2).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading2") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading2"), @" @@ -4763,14 +4904,15 @@ public static void UpdateStylesPart( "); + } if (html.Descendants(XhtmlNoNamespace.h3).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading3") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading3"), @" @@ -4799,14 +4941,15 @@ public static void UpdateStylesPart( w:themeColor='accent1'/> "); + } if (html.Descendants(XhtmlNoNamespace.h4).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading4") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading4"), @" @@ -4837,14 +4980,15 @@ public static void UpdateStylesPart( w:themeColor='accent1'/> "); + } if (html.Descendants(XhtmlNoNamespace.h5).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading5") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading5"), @" @@ -4872,14 +5016,11 @@ public static void UpdateStylesPart( w:themeShade='7F'/> "); + } if (html.Descendants(XhtmlNoNamespace.h6).Any()) - PtUtils.AddElementIfMissing(styleXDoc, - styleXDoc - .Root - .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading6") - .FirstOrDefault(), + { + PtUtils.AddElementIfMissing(styleXDoc, styleXDoc.Root.Elements(W.style).FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading6"), @" @@ -4909,14 +5050,15 @@ public static void UpdateStylesPart( w:themeShade='7F'/> "); + } if (html.Descendants(XhtmlNoNamespace.h7).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading7") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading7"), @" @@ -4946,14 +5088,15 @@ public static void UpdateStylesPart( w:themeTint='BF'/> "); + } if (html.Descendants(XhtmlNoNamespace.h8).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading8") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading8"), @" @@ -4983,14 +5126,15 @@ public static void UpdateStylesPart( "); + } if (html.Descendants(XhtmlNoNamespace.h9).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading9") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "paragraph" && (string)e.Attribute(W.styleId) == "Heading9"), @" @@ -5022,14 +5166,15 @@ public static void UpdateStylesPart( "); + } if (html.Descendants(XhtmlNoNamespace.h1).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading1Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading1Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h2).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading2Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading2Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h3).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading3Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading3Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h4).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading4Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading4Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h5).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading5Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading5Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h6).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading6Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading6Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h7).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading7Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading7Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h8).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading8Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading8Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.h9).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading9Char") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Heading9Char"), @" "); + } if (html.Descendants(XhtmlNoNamespace.a).Any()) + { PtUtils.AddElementIfMissing(styleXDoc, styleXDoc .Root .Elements(W.style) - .Where(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Hyperlink") - .FirstOrDefault(), +.FirstOrDefault(e => (string)e.Attribute(W.type) == "character" && (string)e.Attribute(W.styleId) == "Hyperlink"), @" @@ -5298,19 +5452,20 @@ public static void UpdateStylesPart( "); + } wDoc.MainDocumentPart.StyleDefinitionsPart.PutXDocument(); } } - class ThemeUpdater + internal class ThemeUpdater { - public static void UpdateThemePart(WordprocessingDocument wDoc, XElement html, HtmlToWmlConverterSettings settings) + public static void UpdateThemePart(WordprocessingDocument wDoc, XElement html) { - XDocument themeXDoc = wDoc.MainDocumentPart.ThemePart.GetXDocument(); + var themeXDoc = wDoc.MainDocumentPart.ThemePart.GetXDocument(); - CssExpression minorFont = html.Descendants(XhtmlNoNamespace.body).FirstOrDefault().GetProp("font-family"); - XElement majorFontElement = html.Descendants().Where(e => + var minorFont = html.Descendants(XhtmlNoNamespace.body).FirstOrDefault().GetProp("font-family"); + var majorFontElement = html.Descendants().FirstOrDefault(e => e.Name == XhtmlNoNamespace.h1 || e.Name == XhtmlNoNamespace.h2 || e.Name == XhtmlNoNamespace.h3 || @@ -5319,12 +5474,14 @@ public static void UpdateThemePart(WordprocessingDocument wDoc, XElement html, H e.Name == XhtmlNoNamespace.h6 || e.Name == XhtmlNoNamespace.h7 || e.Name == XhtmlNoNamespace.h8 || - e.Name == XhtmlNoNamespace.h9).FirstOrDefault(); - CssExpression majorFont = null; + e.Name == XhtmlNoNamespace.h9); + CssExpression? majorFont = null; if (majorFontElement != null) + { majorFont = majorFontElement.GetProp("font-family"); + } - XAttribute majorTypeface = themeXDoc + var majorTypeface = themeXDoc .Root .Elements(A.themeElements) .Elements(A.fontScheme) @@ -5334,11 +5491,13 @@ public static void UpdateThemePart(WordprocessingDocument wDoc, XElement html, H .FirstOrDefault(); if (majorTypeface != null && majorFont != null) { - CssTerm term = majorFont.Terms.FirstOrDefault(); + var term = majorFont.Terms.FirstOrDefault(); if (term != null) + { majorTypeface.Value = term.Value; + } } - XAttribute minorTypeface = themeXDoc + var minorTypeface = themeXDoc .Root .Elements(A.themeElements) .Elements(A.fontScheme) @@ -5348,12 +5507,14 @@ public static void UpdateThemePart(WordprocessingDocument wDoc, XElement html, H .FirstOrDefault(); if (minorTypeface != null && minorFont != null) { - CssTerm term = minorFont.Terms.FirstOrDefault(); + var term = minorFont.Terms.FirstOrDefault(); if (term != null) + { minorTypeface.Value = term.Value; + } } wDoc.MainDocumentPart.ThemePart.PutXDocument(); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/HtmlToWmlCssApplier.cs b/OpenXmlPowerTools/HtmlToWmlCssApplier.cs index 07c7367d..d06c7046 100644 --- a/OpenXmlPowerTools/HtmlToWmlCssApplier.cs +++ b/OpenXmlPowerTools/HtmlToWmlCssApplier.cs @@ -1,125 +1,16 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; +using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; -using OpenXmlPowerTools; -using OpenXmlPowerTools.HtmlToWml; -using OpenXmlPowerTools.HtmlToWml.CSS; -using System.Globalization; -#if false -Sort: -1. User agent declarations - 1. Default value - 2. Specified in default style sheet (which I am going to supply) -2. User normal declarations - 1. Specified in user-supplied style sheet -3. Author normal declarations - 1. Specified in style sheet (can defer this until later) - 2. Specified in style element - 3. Specified in style attribute -4. Author important declarations - 1. Specified in style sheet -5. User important declarations - 1. Specified in style sheet - -Sort Key (most significant first) -{0} 9 if user supplied style sheet, high -{0} 8 if author supplied style sheet, high -{0} 7 if style attribute, high -{0} 6 if style attribute, normal -{0} 5 if author suplied style sheet, normal -{0} 4 if user supplied style sheet, normal -{0} 3 if user-agent default style sheet, high -{0} 2 if user-agent default style sheet, normal -{0} 1 if inherited -{1} count of number of ID attributes in selector -{2} count of number of attributes and pseudo classes in selector -{3} count of number of element names and pseudo elements in selector -{4} sequence number in order added to list - -1. Process user-agent default style sheet -2. Process user-supplied style sheet -3. process author-supplied style sheet -4. process STYLE element -5. process style attribute on all elements -6. expand all shorthand properties - the new properties have the same sort key as shorthand prop -7. Add initial values for all properties that don't have values - -The various types of properties: -- Set by the style sheet explicitely -- Set at root level of hierarchy so that other elements can inherit (if no other ancestor) -- Set as initial value for certain elements - all properties have an initial value. Some properties apply to only certain elements. -- Some properties inherit automatically, such as font-family. -- Some properties are set to inherit in the stylesheet. -- border-top-color etc. are set to the color property, which then inherits. - -Properties either inherit, or are set to an initial value. Those properties that inherit are set to initial value only at the top of -the tree. Those props that are set to an initial value throughout the tree do not inherit. It is either one or the other. - -Following is my new theory of the correct algorithm: -- set all properties from the cascade. -- Set initial values for properties at the top of the tree. These specifically need to be set for other properties that will - inherit from them. -- create a matrix of all elements, and which properties need to be set for each element. -- iterate through the tree. - - SetAllValues - for each element - for each property - call GetComputedPropertyValue // implemented in a recursive fashion to set & get its computed value. - - GetComputedPropertyValue - if (property is already computed) - return the computed value - if (property is set) - compute value - set the computed value - return the computed value - if (property is inherited (either because it was set to inherit, or because it is an inherited property)) - if (parent exists) - call GetComputedValue on parent - set the computed value - return the computed value - else - call GetInitialValue for property - compute value - set the computed value - return the computed value - else - call GetInitialValue for property - compute value - set the computed value - return the computed value - - ComputeValue - this needs to be specifically coded for each property - if value is relative (em, ex, percentage, - if property is not on font-size - GetComputedValue for font-size - compute value accordingly - return value - if property is on font-size - call GetComputedValue for font-size of parent - compute value accordingly - return value - if value is percentage - if margin-top, margin-bottom, margin-right, margin-left, padding-top, padding-bottom, padding-right, padding-left - call GetComputedValue for width of containing block - if value is absolute (in, cm, mm, pt, pc, px) - return value -#endif - -namespace OpenXmlPowerTools.HtmlToWml +namespace Codeuctivity.OpenXmlPowerTools { - class CssApplier + internal class CssApplier { - private static List PropertyInfoList = new List() + private static readonly List PropertyInfoList = new List() { // color // Value: | inherit @@ -133,8 +24,8 @@ class CssApplier Names = new[] { "color" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "black", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, - ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "black", Type = CssTermType.String } } }, + ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = CssTermType.String } } }, }, // direction @@ -149,7 +40,7 @@ class CssApplier Names = new[] { "direction" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "ltr", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "ltr", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -165,13 +56,11 @@ class CssApplier Names = new[] { "line-height" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element, "font-size", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element, "font-size", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -187,7 +76,7 @@ class CssApplier Names = new[] { "visibility" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "visible", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "visible", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -208,11 +97,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "list-item") - return true; - return false; + if (display.ToString() == "list-item") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "disc", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "disc", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -230,11 +117,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "list-item") - return true; - return false; + if (display.ToString() == "list-item") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -252,11 +137,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "list-item") - return true; - return false; + if (display.ToString() == "list-item") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -273,7 +156,7 @@ class CssApplier Names = new[] { "font-family" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = settings.MinorLatinFont, Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = settings.MinorLatinFont, Type = CssTermType.String } } }, ComputedValue = (element, assignedValue, settings) => assignedValue, }, @@ -289,7 +172,7 @@ class CssApplier Names = new[] { "font-style" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -305,7 +188,7 @@ class CssApplier Names = new[] { "font-variant" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -322,7 +205,7 @@ class CssApplier Names = new[] { "font-weight" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -339,7 +222,7 @@ class CssApplier Names = new[] { "font-size" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = ((double)settings.DefaultFontSize).ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, Unit = CssUnit.PT } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = settings.DefaultFontSize.ToString(CultureInfo.InvariantCulture), Type = CssTermType.String, Unit = CssUnit.PT } } }, ComputedValue = (element, assignedValue, settings) => ComputeAbsoluteFontSize(element, assignedValue, settings), }, @@ -357,17 +240,13 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "block") - return true; - return false; + if (display.ToString() == "block") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -386,11 +265,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "block") - return true; - return false; + if (display.ToString() == "block") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "left", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, // todo should be based on the direction property + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "left", Type = CssTermType.String, } } }, // todo should be based on the direction property ComputedValue = null, }, @@ -406,7 +283,7 @@ class CssApplier Names = new[] { "text-decoration" }, Inherits = true, // todo need to read css 16.3.1 in full detail to understand how this is implemented. Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String, } } }, ComputedValue = null, }, @@ -430,7 +307,7 @@ class CssApplier Names = new[] { "letter-spacing", "word-spacing" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => ComputeAbsoluteLength(element, assignedValue, settings, null), }, @@ -446,7 +323,7 @@ class CssApplier Names = new[] { "white-space" }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String, } } }, ComputedValue = null, }, @@ -464,11 +341,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table-caption") - return true; - return false; + if (display.ToString() == "table-caption") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "top", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "top", Type = CssTermType.String, } } }, ComputedValue = null, }, @@ -486,11 +361,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table" || display.ToString() == "inline-table") - return true; - return false; + if (display.ToString() == "table" || display.ToString() == "inline-table") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "separate", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "separate", Type = CssTermType.String, } } }, ComputedValue = null, }, @@ -508,11 +381,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table" || display.ToString() == "inline-table") - return true; - return false; + if (display.ToString() == "table" || display.ToString() == "inline-table") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => ComputeAbsoluteLength(element, assignedValue, settings, null), // todo need to handle two lengths here }, @@ -530,9 +401,7 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table" || display.ToString() == "table-cell") - return true; - return false; + if (display.ToString() == "table" || display.ToString() == "table-cell") { return true; } return false; }, InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "show", } } }, ComputedValue = null, @@ -553,35 +422,28 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table-caption" || display.ToString() == "table" || display.ToString() == "inline-table") - return false; - return true; + if (display.ToString() == "table-caption" || display.ToString() == "table" || display.ToString() == "inline-table") { return false; } return true; }, - InitialValue = (element, settings) => + InitialValue = (element, settings) => { if (settings.DefaultBlockContentMargin != null) { - if (settings.DefaultBlockContentMargin == "auto") - return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }; - else if (settings.DefaultBlockContentMargin.ToLower().EndsWith("pt")) + if (settings.DefaultBlockContentMargin == "auto") { return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = CssTermType.String } } }; } else if (settings.DefaultBlockContentMargin.ToLower().EndsWith("pt")) { - string s1 = settings.DefaultBlockContentMargin.Substring(0, settings.DefaultBlockContentMargin.Length - 2); - double d1; - if (double.TryParse(s1, NumberStyles.Float, CultureInfo.InvariantCulture, out d1)) + var s1 = settings.DefaultBlockContentMargin.Substring(0, settings.DefaultBlockContentMargin.Length - 2); + if (double.TryParse(s1, NumberStyles.Float, CultureInfo.InvariantCulture, out var d1)) { - return new CssExpression { Terms = new List { new CssTerm { Value = d1.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT } } }; + return new CssExpression { Terms = new List { new CssTerm { Value = d1.ToString(CultureInfo.InvariantCulture), Type = CssTermType.Number, Unit = CssUnit.PT } } }; } } throw new OpenXmlPowerToolsException("invalid setting"); } - return new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT } } }; + return new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT } } }; }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -600,17 +462,13 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table-caption" || display.ToString() == "table" || display.ToString() == "inline-table") - return false; - return true; + if (display.ToString() == "table-caption" || display.ToString() == "table" || display.ToString() == "inline-table") { return false; } return true; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -630,19 +488,15 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - string dv = display.ToString(); + var dv = display.ToString(); if (dv == "table-row-group" || dv == "table-header-group" || dv == "table-footer-group" || dv == "table-row" || - dv == "table-column-group" || dv == "table-column") - return false; - return true; + dv == "table-column-group" || dv == "table-column") { return false; } return true; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -658,17 +512,11 @@ class CssApplier Names = new[] { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", }, Inherits = false, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { - string assignedValueStr = assignedValue.ToString(); - if (assignedValueStr == "thin") - return new CssExpression { Terms = new List { new CssTerm { Value = "0.75", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; - if (assignedValueStr == "medium") - return new CssExpression { Terms = new List { new CssTerm { Value = "3.0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; - if (assignedValueStr == "thick") - return new CssExpression { Terms = new List { new CssTerm { Value = "4.5", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; - return ComputeAbsoluteLength(element, assignedValue, settings, null); + var assignedValueStr = assignedValue.ToString(); + if (assignedValueStr == "thin") { return new CssExpression { Terms = new List { new CssTerm { Value = "0.75", Type = CssTermType.Number, Unit = CssUnit.PT, } } }; } if (assignedValueStr == "medium") { return new CssExpression { Terms = new List { new CssTerm { Value = "3.0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }; } if (assignedValueStr == "thick") { return new CssExpression { Terms = new List { new CssTerm { Value = "4.5", Type = CssTermType.Number, Unit = CssUnit.PT, } } }; } return ComputeAbsoluteLength(element, assignedValue, settings, null); }, }, @@ -684,7 +532,7 @@ class CssApplier Names = new[] { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", }, Inherits = false, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -703,7 +551,7 @@ class CssApplier Names = new[] { "display", }, Inherits = false, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "inline", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "inline", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -719,7 +567,7 @@ class CssApplier Names = new[] { "position", }, Inherits = false, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "static", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "static", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -735,7 +583,7 @@ class CssApplier Names = new[] { "float", }, Inherits = false, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -751,7 +599,7 @@ class CssApplier Names = new[] { "unicode-bidi", }, Inherits = false, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -767,8 +615,8 @@ class CssApplier Names = new[] { "background-color", }, Inherits = false, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "transparent", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, - ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "transparent", Type = CssTermType.String } } }, + ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = CssTermType.String } } }, }, // text-transform @@ -783,7 +631,7 @@ class CssApplier Names = new[] { "text-transform", }, Inherits = true, Includes = (e, settings) => true, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }, ComputedValue = null, }, @@ -801,11 +649,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table" || display.ToString() == "inline-table") - return true; - return false; + if (display.ToString() == "table" || display.ToString() == "inline-table") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = CssTermType.String, } } }, ComputedValue = null, }, @@ -823,11 +669,9 @@ class CssApplier Includes = (e, settings) => { var display = GetComputedPropertyValue(null, e, "display", settings); - if (display.ToString() == "table-cell") - return true; - return false; + if (display.ToString() == "table-cell") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "show", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "show", Type = CssTermType.String, } } }, ComputedValue = null, }, @@ -848,7 +692,7 @@ class CssApplier var display = GetComputedPropertyValue(null, e, "color", settings); return display; }, - ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }, + ComputedValue = (element, assignedValue, settings) => new CssExpression { Terms = new List { new CssTerm { Value = GetWmlColorFromExpression(assignedValue), Type = CssTermType.String } } }, }, // width @@ -864,31 +708,21 @@ class CssApplier Inherits = false, Includes = (e, settings) => { - if (e.Name == XhtmlNoNamespace.img) - return true; - var display = GetComputedPropertyValue(null, e, "display", settings); + if (e.Name == XhtmlNoNamespace.img) { return true; } var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); - if (dv == "inline" || dv == "table-row" || dv == "table-row-group") - return false; - return true; + if (dv == "inline" || dv == "table-row" || dv == "table-row-group") { return false; } return true; }, - InitialValue = (element, settings) => + InitialValue = (element, settings) => { if (element.Parent == null) { - double? pageWidth = (double?)settings.SectPr.Elements(W.pgSz).Attributes(W._w).FirstOrDefault(); - if (pageWidth == null) - pageWidth = 12240; - double? leftMargin = (double?)settings.SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); - if (leftMargin == null) - leftMargin = 1440; - double? rightMargin = (double?)settings.SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); - if (rightMargin == null) - rightMargin = 1440; - double width = (double)(pageWidth - leftMargin - rightMargin) / 20; - return new CssExpression { Terms = new List { new CssTerm { Value = width.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, Unit = CssUnit.PT, } } }; + var pageWidth = (double?)settings.SectPr.Elements(W.pgSz).Attributes(W._w).FirstOrDefault(); + if (pageWidth == null) { pageWidth = 12240; } var leftMargin = (double?)settings.SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); + if (leftMargin == null) { leftMargin = 1440; } var rightMargin = (double?)settings.SectPr.Elements(W.pgMar).Attributes(W.left).FirstOrDefault(); + if (rightMargin == null) { rightMargin = 1440; } var width = (double)(pageWidth - leftMargin - rightMargin) / 20; + return new CssExpression { Terms = new List { new CssTerm { Value = width.ToString(CultureInfo.InvariantCulture), Type = CssTermType.String, Unit = CssUnit.PT, } } }; } - return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }; + return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = CssTermType.String, } } }; }, ComputedValue = (element, assignedValue, settings) => { @@ -899,16 +733,16 @@ class CssApplier element.Name != XhtmlNoNamespace.table && assignedValue.IsAuto) { - PropertyInfo pi = PropertyInfoList.FirstOrDefault(p => p.Names.Contains("width")); - string display = GetComputedPropertyValue(pi, element, "display", settings).ToString(); + var pi = PropertyInfoList.FirstOrDefault(p => p.Names.Contains("width")); + var display = GetComputedPropertyValue(pi, element, "display", settings).ToString(); if (display != "inline") { - CssExpression parentPropertyValue = GetComputedPropertyValue(pi, element.Parent, "width", settings); + var parentPropertyValue = GetComputedPropertyValue(pi, element.Parent, "width", settings); return parentPropertyValue; } } - CssExpression valueForPercentage = null; - XElement elementToQuery = element.Parent; + CssExpression? valueForPercentage = null; + var elementToQuery = element.Parent; while (elementToQuery != null) { valueForPercentage = GetComputedPropertyValue(null, elementToQuery, "width", settings); @@ -923,7 +757,7 @@ class CssApplier return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, - + // min-width // Value: | | inherit // Initial: 0 @@ -939,17 +773,13 @@ class CssApplier { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); - if (dv == "inline" || dv == "table-row" || dv == "table-row-group") - return false; - return true; + if (dv == "inline" || dv == "table-row" || dv == "table-row-group") { return false; } return true; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -968,17 +798,13 @@ class CssApplier { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); - if (dv == "inline" || dv == "table-row" || dv == "table-row-group") - return false; - return true; + if (dv == "inline" || dv == "table-row" || dv == "table-row-group") { return false; } return true; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "width", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -995,24 +821,18 @@ class CssApplier Inherits = false, Includes = (e, settings) => { - if (e.Name == XhtmlNoNamespace.img) - return true; - var display = GetComputedPropertyValue(null, e, "display", settings); + if (e.Name == XhtmlNoNamespace.img) { return true; } var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); - if (dv == "inline" || dv == "table-row" || dv == "table-row-group") - return false; - return true; + if (dv == "inline" || dv == "table-row" || dv == "table-row-group") { return false; } return true; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, - + // min-height // Value: | | inherit // Initial: 0 @@ -1028,17 +848,13 @@ class CssApplier { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); - if (dv == "inline" || dv == "table-column" || dv == "table-column-group") - return false; - return true; + if (dv == "inline" || dv == "table-column" || dv == "table-column-group") { return false; } return true; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "0", Type = CssTermType.Number, Unit = CssUnit.PT, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, @@ -1057,20 +873,16 @@ class CssApplier { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); - if (dv == "inline" || dv == "table-column" || dv == "table-column-group") - return false; - return true; + if (dv == "inline" || dv == "table-column" || dv == "table-column-group") { return false; } return true; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => { - CssExpression valueForPercentage = null; - if (element.Parent != null) - valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); - return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); + CssExpression? valueForPercentage = null; + if (element.Parent != null) { valueForPercentage = GetComputedPropertyValue(null, element.Parent, "height", settings); } return ComputeAbsoluteLength(element, assignedValue, settings, valueForPercentage); }, }, - + // vertical-align // Value: baseline | sub | super | top | text-top | middle | bottom | text-bottom | // | | inherit @@ -1087,11 +899,9 @@ class CssApplier { var display = GetComputedPropertyValue(null, e, "display", settings); var dv = display.ToString(); - if (dv == "inline" || dv == "table-cell") - return true; - return false; + if (dv == "inline" || dv == "table-cell") { return true; } return false; }, - InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "baseline", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String, } } }, + InitialValue = (element, settings) => new CssExpression { Terms = new List { new CssTerm { Value = "baseline", Type = CssTermType.String, } } }, ComputedValue = (element, assignedValue, settings) => assignedValue, // todo fix }, @@ -1105,7 +915,7 @@ class CssApplier // Percentages: refer to height of containing block // Computed value: if specified as a length, the corresponding absolute length; if // specified as a percentage, the specified value; otherwise, ’auto’. - // + // // right // Value: | | auto | inherit // Initial: auto @@ -1114,7 +924,7 @@ class CssApplier // Percentages: refer to width of containing block // Computed value: if specified as a length, the corresponding absolute length; if // specified as a percentage, the specified value; otherwise, ’auto’. - // + // // bottom // Value: | | auto | inherit // Initial: auto @@ -1123,7 +933,7 @@ class CssApplier // Percentages: refer to height of containing block // Computed value: if specified as a length, the corresponding absolute length; if // specified as a percentage, the specified value; otherwise, ’auto’. - // + // // left // Value: | | auto | inherit // Initial: auto @@ -1142,7 +952,7 @@ class CssApplier // Inherited: no // Percentages: N/A // Computed value: as specified - // + // // z-index // Value: auto | integer | inherit // Initial: auto @@ -1173,9 +983,9 @@ public static void ApplyAllCss( out CssDocument userCssDoc, string annotatedHtmlDumpFileName) { - int propertySequence = 1; + var propertySequence = 1; - CssParser defaultCssParser = new CssParser(); + var defaultCssParser = new CssParser(); defaultCssDoc = defaultCssParser.ParseText(defaultCss); ApplyCssDocument( defaultCssDoc, @@ -1184,19 +994,7 @@ public static void ApplyAllCss( Property.HighOrderPriority.UserAgentHigh, ref propertySequence); - //// todo dump here, see if margin is set on body. - //if (annotatedHtmlDumpFileName != null) - //{ - // StringBuilder sb = new StringBuilder(); - // WriteXHtmlWithAnnotations(newXHtml, sb); - // File.WriteAllText(annotatedHtmlDumpFileName, sb.ToString()); - // Environment.Exit(0); - //} - - //DumpCss(userAgentCssDoc); - //Environment.Exit(0); - - CssParser userCssParser = new CssParser(); + var userCssParser = new CssParser(); userCssDoc = userCssParser.ParseText(userCss); ApplyCssDocument( userCssDoc, @@ -1205,10 +1003,7 @@ public static void ApplyAllCss( Property.HighOrderPriority.UserNormal, ref propertySequence); - //DumpCss(userCssDoc); - //Environment.Exit(0); - - CssParser authorCssParser = new CssParser(); + var authorCssParser = new CssParser(); authorCssDoc = authorCssParser.ParseText(authorCss); ApplyCssDocument( authorCssDoc, @@ -1231,7 +1026,7 @@ public static void ApplyAllCss( if (annotatedHtmlDumpFileName != null) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); WriteXHtmlWithAnnotations(newXHtml, sb); File.WriteAllText(annotatedHtmlDumpFileName, sb.ToString()); } @@ -1254,30 +1049,12 @@ private static void SetAllValues(XElement xHtml, HtmlToWmlConverterSettings sett } } -#if false - GetComputedPropertyValue - if (property is already computed) - return the computed value - if (property is set) - compute value - set the computed value - return the computed value - if (property is inherited (either because it was set to inherit, or because it is an inherited property)) - if (parent exists) - call GetComputedValue on parent - return the computed value - else - call GetInitialValue for property - compute value - set the computed value - return the computed value -#endif - public static CssExpression GetComputedPropertyValue(PropertyInfo propertyInfo, XElement element, string propertyName, - HtmlToWmlConverterSettings settings) + public static CssExpression GetComputedPropertyValue(PropertyInfo? propertyInfo, XElement element, string propertyName, + HtmlToWmlConverterSettings? settings) { // if (property is already computed) // return the computed value - Dictionary computedValues = element.Annotation>(); + var computedValues = element.Annotation>(); if (computedValues == null) { computedValues = new Dictionary(); @@ -1285,40 +1062,44 @@ public static CssExpression GetComputedPropertyValue(PropertyInfo propertyInfo, } if (computedValues.ContainsKey(propertyName)) { - CssExpression r = computedValues[propertyName]; + var r = computedValues[propertyName]; return r; } // if property is not set or property is set to inherited value, then get inherited or initialized value. - string pName = propertyName.ToLower(); + var pName = propertyName.ToLower(); if (propertyInfo == null) { propertyInfo = PropertyInfoList.FirstOrDefault(pi => pi.Names.Contains(pName)); if (propertyInfo == null) + { throw new OpenXmlPowerToolsException("all possible properties should be in the list"); + } } - Dictionary propList = element.Annotation>(); + var propList = element.Annotation>(); if (propList == null) { - CssExpression computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, false, settings); + var computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, false, settings); return computedValue; } if (!propList.ContainsKey(pName)) { - CssExpression computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, false, settings); + var computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, false, settings); return computedValue; } - Property prop = propList[pName]; - string propStr = prop.Expression.ToString(); + var prop = propList[pName]; + var propStr = prop.Expression.ToString(); if (propStr == "inherited" || propStr == "auto") { - CssExpression computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, true, settings); + var computedValue = GetInheritedOrInitializedValue(computedValues, propertyInfo, element, propertyName, true, settings); return computedValue; } // if property is set, then compute the value, return the computed value CssExpression computedValue2; if (propertyInfo.ComputedValue == null) + { computedValue2 = prop.Expression; + } else { computedValue2 = propertyInfo.ComputedValue(element, prop.Expression, settings); @@ -1340,16 +1121,21 @@ public static CssExpression GetInheritedOrInitializedValue(Dictionary { new CssTerm { Value = ".3", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; + var newExpr1 = new CssExpression { Terms = new List { new CssTerm { Value = ".3", Type = CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr1; } if (value == "medium") { - CssExpression newExpr2 = new CssExpression { Terms = new List { new CssTerm { Value = "1.20", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; + var newExpr2 = new CssExpression { Terms = new List { new CssTerm { Value = "1.20", Type = CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr2; } if (value == "thick") { - CssExpression newExpr3 = new CssExpression { Terms = new List { new CssTerm { Value = "1.80", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; + var newExpr3 = new CssExpression { Terms = new List { new CssTerm { Value = "1.80", Type = CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr3; } if (value == "auto" || value == "normal" || value == "none") + { return assignedValue; + } - CssUnit? unit = assignedValue.Terms.First().Unit; + var unit = assignedValue.Terms.First().Unit; if (unit == CssUnit.PT || unit == null) + { return assignedValue; + } if (unit == CssUnit.Percent && lengthForPercentage == null) - return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.String } } }; + { + return new CssExpression { Terms = new List { new CssTerm { Value = "auto", Type = CssTermType.String } } }; + } - double decValue; - if (!double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out decValue)) + if (!double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var decValue)) + { throw new OpenXmlPowerToolsException("value did not parse"); + } + if (negative) + { decValue = -decValue; + } double? newPtSize = null; if (unit == CssUnit.Percent) { - double ptSize; - if (!double.TryParse(lengthForPercentage.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out ptSize)) + if (!double.TryParse(lengthForPercentage.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var ptSize)) + { throw new OpenXmlPowerToolsException("did not return a double?"); + } + newPtSize = ptSize * decValue / 100d; } else if (unit == CssUnit.EM || unit == CssUnit.EX) { - CssExpression fontSize = GetComputedPropertyValue(null, element, "font-size", settings); - double decFontSize; - if (!double.TryParse(fontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out decFontSize)) + var fontSize = GetComputedPropertyValue(null, element, "font-size", settings); + if (!double.TryParse(fontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var decFontSize)) + { throw new OpenXmlPowerToolsException("Internal error"); - newPtSize = (unit == CssUnit.EM) ? decFontSize * decValue : decFontSize * decValue / 2; + } + + newPtSize = unit == CssUnit.EM ? decFontSize * decValue : decFontSize * decValue / 2; } else { if (unit == null && decValue == 0d) + { newPtSize = 0d; + } + if (unit == CssUnit.IN) + { newPtSize = decValue * 72.0d; + } + if (unit == CssUnit.CM) - newPtSize = (decValue / 2.54d) * 72.0d; + { + newPtSize = decValue / 2.54d * 72.0d; + } + if (unit == CssUnit.MM) - newPtSize = (decValue / 25.4d) * 72.0d; + { + newPtSize = decValue / 25.4d * 72.0d; + } + if (unit == CssUnit.PC) + { newPtSize = decValue * 12d; + } + if (unit == CssUnit.PX) + { newPtSize = decValue * 0.75d; + } } if (!newPtSize.HasValue) + { throw new OpenXmlPowerToolsException("Internal error: should not have reached this exception"); - CssExpression newExpr = new CssExpression { Terms = new List { new CssTerm { Value = newPtSize.Value.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; + } + + var newExpr = new CssExpression { Terms = new List { new CssTerm { Value = newPtSize.Value.ToString(CultureInfo.InvariantCulture), Type = CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr; } private static CssExpression ComputeAbsoluteFontSize(XElement element, CssExpression assignedValue, HtmlToWmlConverterSettings settings) { if (assignedValue.Terms.Count != 1) + { throw new OpenXmlPowerToolsException("Should not have a unit with more than one term, I think"); - string value = assignedValue.Terms.First().Value; - CssUnit? unit = assignedValue.Terms.First().Unit; + } + + var value = assignedValue.Terms.First().Value; + var unit = assignedValue.Terms.First().Unit; if (unit == CssUnit.PT) + { return assignedValue; + } + if (FontSizeMap.ContainsKey(value)) - return new CssExpression { Terms = new List { new CssTerm { Value = FontSizeMap[value].ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; + { + return new CssExpression { Terms = new List { new CssTerm { Value = FontSizeMap[value].ToString(CultureInfo.InvariantCulture), Type = CssTermType.Number, Unit = CssUnit.PT, } } }; + } // todo what should the calculation be for computing larger / smaller? if (value == "larger" || value == "smaller") { - CssExpression parentFontSize = GetComputedPropertyValue(null, element.Parent, "font-size", settings); - double ptSize; - if (!double.TryParse(parentFontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out ptSize)) + var parentFontSize = GetComputedPropertyValue(null, element.Parent, "font-size", settings); + if (!double.TryParse(parentFontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var ptSize)) + { throw new OpenXmlPowerToolsException("did not return a double?"); + } + double newPtSize2 = 0; if (value == "larger") { if (ptSize < 10) + { newPtSize2 = 10d; + } + if (ptSize == 10 || ptSize == 11) + { newPtSize2 = 12d; + } + if (ptSize == 12) + { newPtSize2 = 13.5d; + } + if (ptSize >= 13 && ptSize <= 15) + { newPtSize2 = 18d; + } + if (ptSize >= 16 && ptSize <= 20) + { newPtSize2 = 24d; + } + if (ptSize >= 21) + { newPtSize2 = 36d; + } } if (value == "smaller") { if (ptSize <= 11) + { newPtSize2 = 7.5d; + } + if (ptSize == 12) + { newPtSize2 = 10d; + } + if (ptSize >= 13 && ptSize <= 15) + { newPtSize2 = 12d; + } + if (ptSize >= 16 && ptSize <= 20) + { newPtSize2 = 13.5d; + } + if (ptSize >= 21 && ptSize <= 29) + { newPtSize2 = 18d; + } + if (ptSize >= 30) + { newPtSize2 = 24d; + } } - return new CssExpression { Terms = new List { new CssTerm { Value = newPtSize2.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; + return new CssExpression { Terms = new List { new CssTerm { Value = newPtSize2.ToString(CultureInfo.InvariantCulture), Type = CssTermType.Number, Unit = CssUnit.PT, } } }; } - double decValue; - if (!double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out decValue)) + if (!double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var decValue)) + { throw new OpenXmlPowerToolsException("em value did not parse"); + } + double? newPtSize = null; if (unit == CssUnit.EM || unit == CssUnit.EX || unit == CssUnit.Percent) { - CssExpression parentFontSize = GetComputedPropertyValue(null, element.Parent, "font-size", settings); - double ptSize; - if (!double.TryParse(parentFontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out ptSize)) + var parentFontSize = GetComputedPropertyValue(null, element.Parent, "font-size", settings); + if (!double.TryParse(parentFontSize.Terms.First().Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var ptSize)) + { throw new OpenXmlPowerToolsException("did not return a double?"); + } + if (unit == CssUnit.EM) + { newPtSize = ptSize * decValue; + } + if (unit == CssUnit.EX) + { newPtSize = ptSize / 2 * decValue; + } + if (unit == CssUnit.Percent) + { newPtSize = ptSize * decValue / 100d; + } } else { if (unit == CssUnit.IN) + { newPtSize = decValue * 72.0d; + } + if (unit == CssUnit.CM) - newPtSize = (decValue / 2.54d) * 72.0d; + { + newPtSize = decValue / 2.54d * 72.0d; + } + if (unit == CssUnit.MM) - newPtSize = (decValue / 25.4d) * 72.0d; + { + newPtSize = decValue / 25.4d * 72.0d; + } + if (unit == CssUnit.PC) + { newPtSize = decValue * 12d; + } + if (unit == CssUnit.PX) + { newPtSize = decValue * 0.75d; + } } if (!newPtSize.HasValue) + { throw new OpenXmlPowerToolsException("Internal error: should not have reached this exception"); - CssExpression newExpr = new CssExpression { Terms = new List { new CssTerm { Value = newPtSize.Value.ToString(CultureInfo.InvariantCulture), Type = OpenXmlPowerTools.HtmlToWml.CSS.CssTermType.Number, Unit = CssUnit.PT, } } }; + } + + var newExpr = new CssExpression { Terms = new List { new CssTerm { Value = newPtSize.Value.ToString(CultureInfo.InvariantCulture), Type = CssTermType.Number, Unit = CssUnit.PT, } } }; return newExpr; } - private static Dictionary FontSizeMap = new Dictionary() + private static readonly Dictionary FontSizeMap = new Dictionary() { { "xx-small", 7.5d }, { "x-small", 10d }, @@ -1558,9 +1453,9 @@ private static void ApplySelector( { if (DoesSelectorMatch(selector, element)) { - foreach (CssDeclaration declaration in ruleSet.Declarations) + foreach (var declaration in ruleSet.Declarations) { - Property prop = new Property() + var prop = new Property() { Name = declaration.Name.ToLower(), Expression = declaration.Expression, @@ -1580,19 +1475,28 @@ private static bool DoesSelectorMatch( CssSelector selector, XElement element) { - int currentSimpleSelector = selector.SimpleSelectors.Count() - 1; - XElement currentElement = element; + var currentSimpleSelector = selector.SimpleSelectors.Count - 1; + var currentElement = element; while (true) { if (!DoesSimpleSelectorMatch(selector.SimpleSelectors[currentSimpleSelector], currentElement)) + { return false; + } + if (currentSimpleSelector == 0) + { return true; + } + if (selector.SimpleSelectors[currentSimpleSelector].Combinator == CssCombinator.ChildOf) { currentElement = currentElement.Parent; if (currentElement == null) + { return false; + } + currentSimpleSelector--; continue; } @@ -1600,14 +1504,17 @@ private static bool DoesSelectorMatch( { currentElement = currentElement.ElementsBeforeSelf().Reverse().FirstOrDefault(); if (currentElement == null) + { return false; + } + currentSimpleSelector--; continue; } if (selector.SimpleSelectors[currentSimpleSelector].Combinator == null) { - bool continueOuter = false; - foreach (XElement ancestor in element.Ancestors()) + var continueOuter = false; + foreach (var ancestor in element.Ancestors()) { if (DoesSimpleSelectorMatch(selector.SimpleSelectors[currentSimpleSelector - 1], ancestor)) { @@ -1618,7 +1525,10 @@ private static bool DoesSelectorMatch( } } if (continueOuter) + { continue; + } + return false; } } @@ -1628,42 +1538,59 @@ private static bool DoesSimpleSelectorMatch( CssSimpleSelector simpleSelector, XElement element) { - bool elemantNameMatch = true; - bool classNameMatch = true; - bool childSimpleSelectorMatch = true; - bool idMatch = true; - bool attributeMatch = true; + var elemantNameMatch = true; + var classNameMatch = true; + var childSimpleSelectorMatch = true; + var idMatch = true; + var attributeMatch = true; if (simpleSelector.Pseudo != null) + { return false; - if (simpleSelector.ElementName != null && simpleSelector.ElementName != "" && simpleSelector.ElementName != "*") + } + + if (!string.IsNullOrEmpty(simpleSelector.ElementName) && simpleSelector.ElementName != "*") + { elemantNameMatch = element.Name.ToString() == simpleSelector.ElementName.ToString(); + } + if (elemantNameMatch) { if (simpleSelector.Class != null && simpleSelector.Class != "") + { classNameMatch = ClassesOf(element).Contains(simpleSelector.Class); + } + if (classNameMatch) { if (simpleSelector.Child != null) + { childSimpleSelectorMatch = DoesSimpleSelectorMatch(simpleSelector.Child, element); + } + if (childSimpleSelectorMatch) { if (simpleSelector.ID != null && simpleSelector.ID != "") { - string id = (string)element.Attribute("ID"); + var id = (string)element.Attribute("ID"); if (id == null) + { id = (string)element.Attribute("id"); + } + idMatch = simpleSelector.ID == id; } if (idMatch) { if (simpleSelector.Attribute != null) + { attributeMatch = DoesAttributeMatch(simpleSelector.Attribute, element); + } } } } } - bool result = + var result = elemantNameMatch && classNameMatch && childSimpleSelectorMatch && @@ -1672,29 +1599,41 @@ private static bool DoesSimpleSelectorMatch( return result; } - private static bool DoesAttributeMatch(OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute attribute, XElement element) + private static bool DoesAttributeMatch(CssAttribute attribute, XElement element) { - string attName = attribute.Operand.ToLower(); - string attValue = (string)element.Attribute(attName); + var attName = attribute.Operand.ToLower(); + var attValue = (string)element.Attribute(attName); if (attValue == null) + { return false; + } + if (attribute.Operator == null) + { return true; - string value = attribute.Value; + } + + var value = attribute.Value; switch (attribute.Operator) { case CssAttributeOperator.Equals: return attValue == value; + case CssAttributeOperator.BeginsWith: return attValue.StartsWith(value); + case CssAttributeOperator.Contains: return attValue.Contains(value); + case CssAttributeOperator.EndsWith: return attValue.EndsWith(value); + case CssAttributeOperator.InList: return attValue.Split(' ').Contains(value); + case CssAttributeOperator.Hyphenated: return attValue.Split('-')[0] == value; + default: return false; } @@ -1702,36 +1641,36 @@ private static bool DoesAttributeMatch(OpenXmlPowerTools.HtmlToWml.CSS.CssAttrib private static int CountIdAttributesInSimpleSelector(CssSimpleSelector simpleSelector) { - int count = simpleSelector.ID != null ? 1 : 0 + + var count = simpleSelector.ID != null ? 1 : 0 + (simpleSelector.Child != null ? CountIdAttributesInSimpleSelector(simpleSelector.Child) : 0); return count; } private static int CountIdAttributesInSelector(CssSelector selector) { - int count = selector.SimpleSelectors.Select(ss => CountIdAttributesInSimpleSelector(ss)).Sum(); + var count = selector.SimpleSelectors.Select(ss => CountIdAttributesInSimpleSelector(ss)).Sum(); return count; } private static int CountAttributesInSimpleSelector(CssSimpleSelector simpleSelector) { - int count = (simpleSelector.Attribute != null ? 1 : 0) + - ((simpleSelector.Class != null && simpleSelector.Class != "") ? 1 : 0) + + var count = (simpleSelector.Attribute != null ? 1 : 0) + + (simpleSelector.Class != null && simpleSelector.Class != "" ? 1 : 0) + (simpleSelector.Child != null ? CountAttributesInSimpleSelector(simpleSelector.Child) : 0); return count; } private static int CountAttributesInSelector(CssSelector selector) { - int count = selector.SimpleSelectors.Select(ss => CountAttributesInSimpleSelector(ss)).Sum(); + var count = selector.SimpleSelectors.Select(ss => CountAttributesInSimpleSelector(ss)).Sum(); return count; } private static int CountElementNamesInSimpleSelector(CssSimpleSelector simpleSelector) { - int count = (simpleSelector.ElementName != null && + var count = simpleSelector.ElementName != null && simpleSelector.ElementName != "" && - simpleSelector.ElementName != "*") + simpleSelector.ElementName != "*" ? 1 : 0 + (simpleSelector.Child != null ? CountElementNamesInSimpleSelector(simpleSelector.Child) : 0); return count; @@ -1739,7 +1678,7 @@ private static int CountElementNamesInSimpleSelector(CssSimpleSelector simpleSel private static int CountElementNamesInSelector(CssSelector selector) { - int count = selector.SimpleSelectors.Select(ss => CountElementNamesInSimpleSelector(ss)).Sum(); + var count = selector.SimpleSelectors.Select(ss => CountElementNamesInSimpleSelector(ss)).Sum(); return count; } @@ -1747,21 +1686,23 @@ private static void AddPropertyToElement( XElement element, Property property) { - //if (property.Name == "direction") - // Console.WriteLine(1); - Dictionary propList = element.Annotation>(); + var propList = element.Annotation>(); if (propList == null) { propList = new Dictionary(); element.AddAnnotation(propList); } if (!propList.ContainsKey(property.Name)) + { propList.Add(property.Name, property); + } else { - Property current = propList[property.Name]; - if (((System.IComparable)property).CompareTo(current) == 1) + var current = propList[property.Name]; + if (((IComparable)property).CompareTo(current) == 1) + { propList[property.Name] = property; + } } } @@ -1770,20 +1711,27 @@ private static void AddPropertyToDictionary( Property property) { if (!propList.ContainsKey(property.Name)) + { propList.Add(property.Name, property); + } else { - Property current = propList[property.Name]; - if (((System.IComparable)property).CompareTo(current) == 1) + var current = propList[property.Name]; + if (((IComparable)property).CompareTo(current) == 1) + { propList[property.Name] = property; + } } } private static string[] ClassesOf(XElement element) { - string classesString = (string)element.Attribute("class"); + var classesString = (string)element.Attribute("class"); if (classesString == null) - return new string[0]; + { + return Array.Empty(); + } + return classesString.Split(' '); } @@ -1796,7 +1744,7 @@ private static void ApplyDeclarationsToElement( { foreach (var declaration in ruleSet.Declarations) { - Property prop = new Property() + var prop = new Property() { Name = declaration.Name.ToLower(), Expression = declaration.Expression, @@ -1827,14 +1775,14 @@ private static void ApplyStyleAttributes(XElement xHtml, ref int propertySequenc { foreach (var element in xHtml.DescendantsAndSelf()) { - XAttribute styleAtt = element.Attribute(XhtmlNoNamespace.style); + var styleAtt = element.Attribute(XhtmlNoNamespace.style); if (styleAtt != null) { - string style = (string)styleAtt; - string cssString = element.Name + "{" + style + "}"; + var style = (string)styleAtt; + var cssString = element.Name + "{" + style + "}"; cssString = cssString.Replace('\"', '\''); - CssParser cssParser = new CssParser(); - CssDocument cssDoc = cssParser.ParseText(cssString); + var cssParser = new CssParser(); + var cssDoc = cssParser.ParseText(cssString); ApplyCssToElement( cssDoc, element, @@ -1842,11 +1790,11 @@ private static void ApplyStyleAttributes(XElement xHtml, ref int propertySequenc Property.HighOrderPriority.StyleAttributeHigh, ref propertySequence); } - XAttribute dirAtt = element.Attribute(XhtmlNoNamespace.dir); + var dirAtt = element.Attribute(XhtmlNoNamespace.dir); if (dirAtt != null) { - string dir = dirAtt.Value.ToLower(); - Property prop = new Property() + var dir = dirAtt.Value.ToLower(); + var prop = new Property() { Name = "direction", Expression = new CssExpression { Terms = new List { new CssTerm { Value = dir, Type = CssTermType.String } } }, @@ -1889,7 +1837,7 @@ private class ShorthandPropertiesInfo public string Pattern; } - private static ShorthandPropertiesInfo[] ShorthandProperties = new[] + private static readonly ShorthandPropertiesInfo[] ShorthandProperties = new[] { new ShorthandPropertiesInfo { @@ -1922,13 +1870,13 @@ private static void ExpandShorthandProperties(XElement xHtml, HtmlToWmlConverter { foreach (var element in xHtml.DescendantsAndSelf()) { - ExpandShorthandPropertiesForElement(element, settings); + ExpandShorthandPropertiesForElement(element); } } - private static void ExpandShorthandPropertiesForElement(XElement element, HtmlToWmlConverterSettings settings) + private static void ExpandShorthandPropertiesForElement(XElement element) { - Dictionary propertyList = element.Annotation>(); + var propertyList = element.Annotation>(); if (propertyList == null) { propertyList = new Dictionary(); @@ -1936,12 +1884,12 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } foreach (var kvp in propertyList.ToList()) { - Property p = kvp.Value; + var p = kvp.Value; if (p.Name == "border") { - CssExpression borderColor; - CssExpression borderWidth; - CssExpression borderStyle; + CssExpression? borderColor; + CssExpression? borderWidth; + CssExpression? borderStyle; if (p.Expression.Terms.Count == 1 && p.Expression.Terms.First().Value == "inherit") { borderColor = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; @@ -1950,23 +1898,22 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } else { - //borderColor = GetComputedPropertyValue(null, element, "color", settings); - //borderWidth = new Expression { Terms = new List { new Term { Value = "medium", Type = TermType.String } } }; - //borderStyle = new Expression { Terms = new List { new Term { Value = "none", Type = TermType.String } } }; borderColor = null; borderWidth = null; borderStyle = null; foreach (var term in p.Expression.Terms) { - CssDataType dataType = GetDatatypeFromBorderTerm(term); + var dataType = GetDatatypeFromBorderTerm(term); switch (dataType) { case CssDataType.Color: borderColor = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BorderWidth: borderWidth = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BorderStyle: borderStyle = new CssExpression { Terms = new List { term } }; break; @@ -1977,7 +1924,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo { if (borderWidth != null) { - Property bwp = new Property + var bwp = new Property { Name = "border-" + side + "-width", Expression = borderWidth, @@ -1991,7 +1938,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } if (borderStyle != null) { - Property bsp = new Property + var bsp = new Property { Name = "border-" + side + "-style", Expression = borderStyle, @@ -2005,7 +1952,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } if (borderColor != null) { - Property bc = new Property + var bc = new Property { Name = "border-" + side + "-color", Expression = borderColor, @@ -2022,10 +1969,10 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } if (p.Name == "border-top" || p.Name == "border-right" || p.Name == "border-bottom" || p.Name == "border-left") { - CssExpression borderColor; - CssExpression borderWidth; - CssExpression borderStyle; - if (p.Expression.Terms.Count() == 1 && p.Expression.Terms.First().Value == "inherit") + CssExpression? borderColor; + CssExpression? borderWidth; + CssExpression? borderStyle; + if (p.Expression.Terms.Count == 1 && p.Expression.Terms.First().Value == "inherit") { borderColor = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; borderWidth = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; @@ -2033,23 +1980,22 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } else { - //borderColor = GetComputedPropertyValue(null, element, "color", settings); - //borderWidth = new Expression { Terms = new List { new Term { Value = "medium", Type = TermType.String } } }; - //borderStyle = new Expression { Terms = new List { new Term { Value = "none", Type = TermType.String } } }; borderColor = null; borderWidth = null; borderStyle = null; foreach (var term in p.Expression.Terms) { - CssDataType dataType = GetDatatypeFromBorderTerm(term); + var dataType = GetDatatypeFromBorderTerm(term); switch (dataType) { case CssDataType.Color: borderColor = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BorderWidth: borderWidth = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BorderStyle: borderStyle = new CssExpression { Terms = new List { term } }; break; @@ -2058,7 +2004,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } if (borderWidth != null) { - Property bwp = new Property + var bwp = new Property { Name = p.Name + "-width", Expression = borderWidth, @@ -2072,7 +2018,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } if (borderStyle != null) { - Property bsp = new Property + var bsp = new Property { Name = p.Name + "-style", Expression = borderStyle, @@ -2086,7 +2032,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } if (borderColor != null) { - Property bc = new Property + var bc = new Property { Name = p.Name + "-color", Expression = borderColor, @@ -2119,22 +2065,24 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo listStyleImage = new CssExpression { Terms = new List { new CssTerm { Value = "none", Type = CssTermType.String } } }; foreach (var term in p.Expression.Terms) { - CssDataType dataType = GetDatatypeFromListStyleTerm(term); + var dataType = GetDatatypeFromListStyleTerm(term); switch (dataType) { case CssDataType.ListStyleType: listStyleType = new CssExpression { Terms = new List { term } }; break; + case CssDataType.ListStylePosition: listStylePosition = new CssExpression { Terms = new List { term } }; break; + case CssDataType.ListStyleImage: listStyleImage = new CssExpression { Terms = new List { term } }; break; } } } - Property lst = new Property + var lst = new Property { Name = "list-style-type", Expression = listStyleType, @@ -2145,7 +2093,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, lst); - Property lsp = new Property + var lsp = new Property { Name = "list-style-position", Expression = listStylePosition, @@ -2156,7 +2104,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, lsp); - Property lsi = new Property + var lsi = new Property { Name = "list-style-image", Expression = listStyleImage, @@ -2196,38 +2144,42 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo Terms = new List { new CssTerm { Value = "0", - Unit = OpenXmlPowerTools.HtmlToWml.CSS.CssUnit.Percent, + Unit = CssUnit.Percent, Type = CssTermType.Number }, new CssTerm { Value = "0", - Unit = OpenXmlPowerTools.HtmlToWml.CSS.CssUnit.Percent, + Unit = CssUnit.Percent, Type = CssTermType.Number }, } }; - List backgroundPositionList = new List(); + var backgroundPositionList = new List(); foreach (var term in p.Expression.Terms) { - CssDataType dataType = GetDatatypeFromBackgroundTerm(term); + var dataType = GetDatatypeFromBackgroundTerm(term); switch (dataType) { case CssDataType.BackgroundColor: backgroundColor = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BackgroundImage: backgroundImage = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BackgroundRepeat: backgroundRepeat = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BackgroundAttachment: backgroundAttachment = new CssExpression { Terms = new List { term } }; break; + case CssDataType.BackgroundPosition: backgroundPositionList.Add(term); break; } } - if (backgroundPositionList.Count() == 1) + if (backgroundPositionList.Count == 1) { backgroundPosition = new CssExpression { @@ -2240,7 +2192,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } }; } - if (backgroundPositionList.Count() == 2) + if (backgroundPositionList.Count == 2) { backgroundPosition = new CssExpression { @@ -2248,7 +2200,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo }; } } - Property bc = new Property + var bc = new Property { Name = "background-color", Expression = backgroundColor, @@ -2259,7 +2211,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bc); - Property bgi = new Property + var bgi = new Property { Name = "background-image", Expression = backgroundImage, @@ -2270,7 +2222,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bgi); - Property bgr = new Property + var bgr = new Property { Name = "background-repeat", Expression = backgroundRepeat, @@ -2281,7 +2233,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bgr); - Property bga = new Property + var bga = new Property { Name = "background-attachment", Expression = backgroundAttachment, @@ -2292,7 +2244,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, bga); - Property bgp = new Property + var bgp = new Property { Name = "background-position", Expression = backgroundPosition, @@ -2314,7 +2266,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo CssExpression fontSize; CssExpression lineHeight; CssExpression fontFamily; - if (p.Expression.Terms.Count() == 1 && p.Expression.Terms.First().Value == "inherit") + if (p.Expression.Terms.Count == 1 && p.Expression.Terms.First().Value == "inherit") { fontStyle = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; fontVarient = new CssExpression { Terms = new List { new CssTerm { Value = "inherit", Type = CssTermType.String } } }; @@ -2331,39 +2283,51 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo fontSize = new CssExpression { Terms = new List { new CssTerm { Value = "medium", Type = CssTermType.String } } }; lineHeight = new CssExpression { Terms = new List { new CssTerm { Value = "normal", Type = CssTermType.String } } }; fontFamily = new CssExpression { Terms = new List { new CssTerm { Value = "serif", Type = CssTermType.String } } }; - List fontFamilyList = new List(); + var fontFamilyList = new List(); foreach (var term in p.Expression.Terms) { - CssDataType dataType = GetDatatypeFromFontTerm(term); + var dataType = GetDatatypeFromFontTerm(term); switch (dataType) { case CssDataType.FontStyle: fontStyle = new CssExpression { Terms = new List { term } }; break; + case CssDataType.FontVarient: fontVarient = new CssExpression { Terms = new List { term } }; break; + case CssDataType.FontWeight: fontWeight = new CssExpression { Terms = new List { term } }; break; + case CssDataType.FontSize: fontSize = new CssExpression { Terms = new List { term } }; break; + case CssDataType.Length: if (term.SeparatorChar == "/") + { lineHeight = new CssExpression { Terms = new List { term } }; + } else + { fontSize = new CssExpression { Terms = new List { term } }; + } + break; + case CssDataType.FontFamily: fontFamilyList.Add(term); break; } } if (fontFamilyList.Count > 0) + { fontFamily = new CssExpression { Terms = fontFamilyList }; + } } - Property fs = new Property + var fs = new Property { Name = "font-style", Expression = fontStyle, @@ -2374,7 +2338,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fs); - Property fv = new Property + var fv = new Property { Name = "font-varient", Expression = fontVarient, @@ -2385,7 +2349,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fv); - Property fw = new Property + var fw = new Property { Name = "font-weight", Expression = fontWeight, @@ -2396,7 +2360,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fw); - Property fsz = new Property + var fsz = new Property { Name = "font-size", Expression = fontSize, @@ -2407,7 +2371,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, fsz); - Property lh = new Property + var lh = new Property { Name = "line-height", Expression = lineHeight, @@ -2418,7 +2382,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo SequenceNumber = p.SequenceNumber, }; AddPropertyToDictionary(propertyList, lh); - Property ff = new Property + var ff = new Property { Name = "font-family", Expression = fontFamily, @@ -2441,9 +2405,9 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo case 1: foreach (var direction in new[] { "top", "right", "bottom", "left" }) { - Property ep = new Property() + var ep = new Property() { - Name = String.Format(shPr.Pattern, direction), + Name = string.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, @@ -2454,12 +2418,13 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo AddPropertyToDictionary(propertyList, ep); } break; + case 2: foreach (var direction in new[] { "top", "bottom" }) { - Property ep = new Property() + var ep = new Property() { - Name = String.Format(shPr.Pattern, direction), + Name = string.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, @@ -2471,9 +2436,9 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } foreach (var direction in new[] { "left", "right" }) { - Property ep = new Property() + var ep = new Property() { - Name = String.Format(shPr.Pattern, direction), + Name = string.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(1).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, @@ -2484,10 +2449,11 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo AddPropertyToDictionary(propertyList, ep); } break; + case 3: - Property ep3 = new Property() + var ep3 = new Property() { - Name = String.Format(shPr.Pattern, "top"), + Name = string.Format(shPr.Pattern, "top"), Expression = new CssExpression { Terms = new List { p.Expression.Terms.First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, @@ -2498,9 +2464,9 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo AddPropertyToDictionary(propertyList, ep3); foreach (var direction in new[] { "left", "right" }) { - Property ep2 = new Property() + var ep2 = new Property() { - Name = String.Format(shPr.Pattern, direction), + Name = string.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(1).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, @@ -2510,9 +2476,9 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo }; AddPropertyToDictionary(propertyList, ep2); } - Property ep4 = new Property() + var ep4 = new Property() { - Name = String.Format(shPr.Pattern, "bottom"), + Name = string.Format(shPr.Pattern, "bottom"), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(2).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, @@ -2522,13 +2488,14 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo }; AddPropertyToDictionary(propertyList, ep4); break; + case 4: - int skip = 0; + var skip = 0; foreach (var direction in new[] { "top", "right", "bottom", "left" }) { - Property ep = new Property() + var ep = new Property() { - Name = String.Format(shPr.Pattern, direction), + Name = string.Format(shPr.Pattern, direction), Expression = new CssExpression { Terms = new List { p.Expression.Terms.Skip(skip++).First() } }, HighOrderSort = p.HighOrderSort, IdAttributesInSelector = p.IdAttributesInSelector, @@ -2545,7 +2512,7 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo } } - private static string[] BackgroundRepeatValues = new[] + private static readonly string[] BackgroundRepeatValues = new[] { "repeat", "repeat-x", @@ -2553,13 +2520,13 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo "no-repeat", }; - private static string[] BackgroundAttachmentValues = new[] + private static readonly string[] BackgroundAttachmentValues = new[] { "scroll", "fixed", }; - private static string[] BackgroundPositionValues = new[] + private static readonly string[] BackgroundPositionValues = new[] { "left", "right", @@ -2571,13 +2538,25 @@ private static void ExpandShorthandPropertiesForElement(XElement element, HtmlTo private static CssDataType GetDatatypeFromBackgroundTerm(CssTerm term) { if (term.IsColor) + { return CssDataType.BackgroundColor; + } + if (BackgroundRepeatValues.Contains(term.Value.ToLower())) + { return CssDataType.BackgroundRepeat; + } + if (BackgroundAttachmentValues.Contains(term.Value.ToLower())) + { return CssDataType.BackgroundAttachment; + } + if (term.Function != null) + { return CssDataType.BackgroundImage; + } + if (term.Unit == CssUnit.CM || term.Unit == CssUnit.EM || term.Unit == CssUnit.IN || @@ -2585,19 +2564,25 @@ private static CssDataType GetDatatypeFromBackgroundTerm(CssTerm term) term.Unit == CssUnit.PT || term.Unit == CssUnit.PX || term.Unit == CssUnit.Percent) + { return CssDataType.BackgroundPosition; + } + if (BackgroundPositionValues.Contains(term.Value.ToLower())) + { return CssDataType.BackgroundPosition; + } + return CssDataType.BackgroundPosition; } - private static string[] ListStylePositionValues = new[] + private static readonly string[] ListStylePositionValues = new[] { "inside", "outside", }; - private static string[] BorderStyleValues = new[] + private static readonly string[] BorderStyleValues = new[] { "none", "hidden", @@ -2618,11 +2603,14 @@ private static CssDataType GetDatatypeFromBorderTerm(CssTerm term) return CssDataType.Color; } if (BorderStyleValues.Contains(term.Value.ToLower())) + { return CssDataType.BorderStyle; + } + return CssDataType.BorderWidth; } - private static string[] ListStyleTypeValues = new[] + private static readonly string[] ListStyleTypeValues = new[] { "disc", "circle", @@ -2643,24 +2631,30 @@ private static CssDataType GetDatatypeFromBorderTerm(CssTerm term) private static CssDataType GetDatatypeFromListStyleTerm(CssTerm term) { if (ListStyleTypeValues.Contains(term.Value.ToLower())) + { return CssDataType.ListStyleType; + } + if (ListStylePositionValues.Contains(term.Value.ToLower())) + { return CssDataType.ListStylePosition; + } + return CssDataType.ListStyleImage; } - private static string[] FontStyleValues = new[] + private static readonly string[] FontStyleValues = new[] { "italic", "oblique", }; - private static string[] FontVarientValues = new[] + private static readonly string[] FontVarientValues = new[] { "small-caps", }; - private static string[] FontWeightValues = new[] + private static readonly string[] FontWeightValues = new[] { "bold", "bolder", @@ -2679,13 +2673,25 @@ private static CssDataType GetDatatypeFromListStyleTerm(CssTerm term) private static CssDataType GetDatatypeFromFontTerm(CssTerm term) { if (FontStyleValues.Contains(term.Value.ToLower())) + { return CssDataType.FontStyle; + } + if (FontVarientValues.Contains(term.Value.ToLower())) + { return CssDataType.FontVarient; + } + if (FontWeightValues.Contains(term.Value.ToLower())) + { return CssDataType.FontWeight; + } + if (FontSizeMap.ContainsKey(term.Value.ToLower())) + { return CssDataType.FontSize; + } + if (term.Unit == CssUnit.CM || term.Unit == CssUnit.EM || term.Unit == CssUnit.IN || @@ -2693,67 +2699,74 @@ private static CssDataType GetDatatypeFromFontTerm(CssTerm term) term.Unit == CssUnit.PT || term.Unit == CssUnit.PX || term.Unit == CssUnit.Percent) + { return CssDataType.Length; + } + return CssDataType.FontFamily; } public class PropertyInfo { - public string[] Names; + public string[]? Names; public bool Inherits; - public Func Includes; - public Func InitialValue; - public Func ComputedValue; + public Func? Includes; + public Func? InitialValue; + public Func? ComputedValue; } private static void WriteXHtmlWithAnnotations(XElement element, StringBuilder sb) { - int depth = element.Ancestors().Count() * 2; - XElement dummyElement = new XElement(element.Name, element.Attributes()); - sb.Append(String.Format("{0}{1}", "".PadRight(depth), dummyElement) + Environment.NewLine); - Dictionary propList = element.Annotation>(); + var depth = element.Ancestors().Count() * 2; + var dummyElement = new XElement(element.Name, element.Attributes()); + sb.Append(string.Format("{0}{1}", "".PadRight(depth), dummyElement) + Environment.NewLine); + var propList = element.Annotation>(); if (propList != null) { sb.Append("".PadRight(depth + 2) + "Properties from Stylesheets" + Environment.NewLine); sb.Append("".PadRight(depth + 2) + "===========================" + Environment.NewLine); foreach (var kvp in propList.OrderBy(p => p.Key).ThenBy(p => p.Value)) { - Property prop = kvp.Value; - string propString = String.Format("{0} High:{1} Id:{2} Att:{3} Ell:{4} Seq:{5}", + var prop = kvp.Value; + var propString = string.Format("{0} High:{1} Id:{2} Att:{3} Ell:{4} Seq:{5}", (prop.Name + ":" + prop.Expression + " ").PadRight(50 - depth + 2, '.'), (int)prop.HighOrderSort, prop.IdAttributesInSelector, prop.AttributesInSelector, prop.ElementNamesInSelector, prop.SequenceNumber); - sb.Append(String.Format("{0}{1}", "".PadRight(depth + 2), propString) + Environment.NewLine); + sb.Append(string.Format("{0}{1}", "".PadRight(depth + 2), propString) + Environment.NewLine); } sb.Append(Environment.NewLine); } - Dictionary computedProperties = element.Annotation>(); + var computedProperties = element.Annotation>(); if (computedProperties != null) { sb.Append("".PadRight(depth + 2) + "Computed Properties" + Environment.NewLine); sb.Append("".PadRight(depth + 2) + "===================" + Environment.NewLine); foreach (var prop in computedProperties.OrderBy(cp => cp.Key)) { - string propString = prop.Key + ":" + prop.Value; - sb.Append(String.Format("{0}{1}", "".PadRight(depth + 2), propString) + Environment.NewLine); + var propString = prop.Key + ":" + prop.Value; + sb.Append(string.Format("{0}{1}", "".PadRight(depth + 2), propString) + Environment.NewLine); } sb.Append(Environment.NewLine); } foreach (var child in element.Elements()) + { WriteXHtmlWithAnnotations(child, sb); + } } public static string DumpCss(CssDocument css) { - StringBuilder sb = new StringBuilder(); - int indent = 0; + var sb = new StringBuilder(); + var indent = 0; Pr(sb, indent, "CSS Tree Dump"); Pr(sb, indent, "============="); - Pr(sb, indent, "Directives count: {0}", css.Directives.Count()); - Pr(sb, indent, "RuleSet count: {0}", css.RuleSets.Count()); + Pr(sb, indent, "Directives count: {0}", css.Directives.Count); + Pr(sb, indent, "RuleSet count: {0}", css.RuleSets.Count); foreach (var rs in css.RuleSets) + { DumpRuleSet(sb, indent, rs); + } Pr(sb, indent, ""); return sb.ToString(); @@ -2767,11 +2780,10 @@ private static void DumpFunction(StringBuilder sb, int indent, CssFunction f) indent++; Pr(sb, indent, "Name: {0}", f.Name); DumpExpression(sb, indent, f.Expression); - indent--; } } - private static void DumpAttribute(StringBuilder sb, int indent, OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute a) + private static void DumpAttribute(StringBuilder sb, int indent, CssAttribute a) { Pr(sb, indent, "Attribute: {0}", a); if (a != null) @@ -2781,7 +2793,6 @@ private static void DumpAttribute(StringBuilder sb, int indent, OpenXmlPowerTool Pr(sb, indent, "Operator: {0}", a.Operator); Pr(sb, indent, "OperatorString: {0}", a.CssOperatorString); Pr(sb, indent, "Value: {0}", a.Value); - indent--; } } @@ -2804,16 +2815,16 @@ private static void DumpSimpleSelector(StringBuilder sb, int indent, CssSimpleSe Pr(sb, indent, "Pseudo: {0}", s.Pseudo); indent--; } - indent--; } private static void DumpSelectors(StringBuilder sb, int indent, CssSelector s) { indent++; - Pr(sb, indent, "SimpleSelectors count: {0}", s.SimpleSelectors.Count()); + Pr(sb, indent, "SimpleSelectors count: {0}", s.SimpleSelectors.Count); foreach (var ss in s.SimpleSelectors) + { DumpSimpleSelector(sb, indent, ss); - indent--; + } } private static void DumpTerm(StringBuilder sb, int indent, CssTerm t) @@ -2830,17 +2841,17 @@ private static void DumpTerm(StringBuilder sb, int indent, CssTerm t) Pr(sb, indent, "Unit: {0}", t.Unit); Pr(sb, indent, "UnitString: {0}", t.UnitString); Pr(sb, indent, "Value: {0}", t.Value); - indent--; } private static void DumpExpression(StringBuilder sb, int indent, CssExpression e) { Pr(sb, indent, "Expression >{0}<", e.ToString()); indent++; - Pr(sb, indent, "Terms count: {0}", e.Terms.Count()); + Pr(sb, indent, "Terms count: {0}", e.Terms.Count); foreach (var t in e.Terms) + { DumpTerm(sb, indent, t); - indent--; + } } private static void DumpDeclarations(StringBuilder sb, int indent, CssDeclaration d) @@ -2852,7 +2863,6 @@ private static void DumpDeclarations(StringBuilder sb, int indent, CssDeclaratio DumpExpression(sb, indent, d.Expression); Pr(sb, indent, "Important: {0}", d.Important); indent--; - indent--; } private static void DumpRuleSet(StringBuilder sb, int indent, CssRuleSet rs) @@ -2860,39 +2870,39 @@ private static void DumpRuleSet(StringBuilder sb, int indent, CssRuleSet rs) indent++; Pr(sb, indent, "RuleSet"); indent++; - Pr(sb, indent, "Selectors count: {0}", rs.Selectors.Count()); + Pr(sb, indent, "Selectors count: {0}", rs.Selectors.Count); foreach (var s in rs.Selectors) + { DumpSelectors(sb, indent, s); - Pr(sb, indent, "Declarations count: {0}", rs.Declarations.Count()); + } + + Pr(sb, indent, "Declarations count: {0}", rs.Declarations.Count); foreach (var d in rs.Declarations) + { DumpDeclarations(sb, indent, d); - indent--; + } + indent--; } private static void Pr(StringBuilder sb, int indent, string format, object o) { if (o == null) + { return; - string text = String.Format(format, o); - StringBuilder sb2 = new StringBuilder("".PadRight(indent * 2) + text); - //sb2.Replace("&", "&"); - //sb2.Replace("<", "<"); - //sb2.Replace(">", ">"); + } + + var text = string.Format(format, o); + var sb2 = new StringBuilder("".PadRight(indent * 2) + text); sb.Append(sb2); sb.Append(Environment.NewLine); - //Console.WriteLine(sb2); } private static void Pr(StringBuilder sb, int indent, string text) { - StringBuilder sb2 = new StringBuilder("".PadRight(indent * 2) + text); - //sb2.Replace("&", "&"); - //sb2.Replace("<", "<"); - //sb2.Replace(">", ">"); + var sb2 = new StringBuilder("".PadRight(indent * 2) + text); sb.Append(sb2); sb.Append(Environment.NewLine); - //Console.WriteLine(sb2); } public class Property : IComparable @@ -2904,6 +2914,7 @@ public class Property : IComparable public int AttributesInSelector { get; set; } public int ElementNamesInSelector { get; set; } public int SequenceNumber { get; set; } + public enum HighOrderPriority { InitialValue = 0, @@ -2919,34 +2930,58 @@ public enum HighOrderPriority UserHigh = 10, }; - int System.IComparable.CompareTo(Property other) + int IComparable.CompareTo(Property other) { // if this is less than other, return -1 // if this is greater than other, return 1 - int gt = 1; - int lt = -1; - if (this.HighOrderSort < other.HighOrderSort) + var gt = 1; + var lt = -1; + if (HighOrderSort < other.HighOrderSort) + { return lt; - if (this.HighOrderSort > other.HighOrderSort) + } + + if (HighOrderSort > other.HighOrderSort) + { return gt; - if (this.IdAttributesInSelector < other.IdAttributesInSelector) + } + + if (IdAttributesInSelector < other.IdAttributesInSelector) + { return lt; - if (this.IdAttributesInSelector > other.IdAttributesInSelector) + } + + if (IdAttributesInSelector > other.IdAttributesInSelector) + { return gt; - if (this.AttributesInSelector < other.AttributesInSelector) + } + + if (AttributesInSelector < other.AttributesInSelector) + { return lt; - if (this.AttributesInSelector > other.AttributesInSelector) + } + + if (AttributesInSelector > other.AttributesInSelector) + { return gt; - if (this.ElementNamesInSelector < other.ElementNamesInSelector) + } + + if (ElementNamesInSelector < other.ElementNamesInSelector) + { return lt; - if (this.ElementNamesInSelector > other.ElementNamesInSelector) + } + + if (ElementNamesInSelector > other.ElementNamesInSelector) + { return gt; - return this.SequenceNumber.CompareTo(other.SequenceNumber); + } + + return SequenceNumber.CompareTo(other.SequenceNumber); } } - private static Dictionary ColorMap = new Dictionary() + private static readonly Dictionary ColorMap = new Dictionary() { { "maroon", "800000" }, { "red", "FF0000" }, @@ -2974,42 +3009,48 @@ int System.IComparable.CompareTo(Property other) public static string GetWmlColorFromExpression(CssExpression color) { // todo have to handle all forms of colors here - if (color.Terms.Count() == 1) + if (color.Terms.Count == 1) { - CssTerm term = color.Terms.First(); + var term = color.Terms.First(); if (term.Type == CssTermType.Function && term.Function.Name.ToUpper() == "RGB" && term.Function.Expression.Terms.Count == 3) { - List lt = term.Function.Expression.Terms; + var lt = term.Function.Expression.Terms; if (lt.First().Unit == CssUnit.Percent) { - string v1 = lt.First().Value; - string v2 = lt.ElementAt(1).Value; - string v3 = lt.ElementAt(2).Value; - string colorInHex = String.Format("{0:x2}{1:x2}{2:x2}", (int)((float.Parse(v1) / 100.0) * 255), - (int)((float.Parse(v2) / 100.0) * 255), (int)((float.Parse(v3) / 100.0) * 255)); + var v1 = lt.First().Value; + var v2 = lt.ElementAt(1).Value; + var v3 = lt.ElementAt(2).Value; + var colorInHex = string.Format("{0:x2}{1:x2}{2:x2}", (int)(float.Parse(v1) / 100.0 * 255), + (int)(float.Parse(v2) / 100.0 * 255), (int)(float.Parse(v3) / 100.0 * 255)); return colorInHex; } else { - string v1 = lt.First().Value; - string v2 = lt.ElementAt(1).Value; - string v3 = lt.ElementAt(2).Value; - string colorInHex = String.Format("{0:x2}{1:x2}{2:x2}", int.Parse(v1), int.Parse(v2), int.Parse(v3)); + var v1 = lt.First().Value; + var v2 = lt.ElementAt(1).Value; + var v3 = lt.ElementAt(2).Value; + var colorInHex = string.Format("{0:x2}{1:x2}{2:x2}", int.Parse(v1), int.Parse(v2), int.Parse(v3)); return colorInHex; } } - string value = term.Value; + var value = term.Value; if (value.Substring(0, 1) == "#" && value.Length == 4) { - string e = ConvertSingleDigit(value.Substring(1, 1)) + + var e = ConvertSingleDigit(value.Substring(1, 1)) + ConvertSingleDigit(value.Substring(2, 1)) + ConvertSingleDigit(value.Substring(3, 1)); return e; } if (value.Substring(0, 1) == "#") + { return value.Substring(1); + } + if (ColorMap.ContainsKey(value)) + { return ColorMap[value]; + } + return value; } return "000000"; @@ -3020,724 +3061,4 @@ private static string ConvertSingleDigit(string singleDigit) return singleDigit + singleDigit; } } -} - -#if false -color -Value: | inherit -Initial: depends on UA -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -margin-top, margin-bottom -Value: | inherit -Initial: 0 -Applies to: all elements except elements with table display types other than table-caption, table, and inline-table - all elements except th, td, tr -Inherited: no -Percentages: refer to width of containing block -Computed value: the percentage as specified or the absolute length - -margin-right, margin-left -Value: | inherit -Initial: 0 -Applies to: all elements except elements with table display types other than table-caption, table, and inline-table - all elements except th, td, tr -Inherited: no -Percentages: refer to width of containing block -Computed value: the percentage as specified or the absolute length - -padding-top, padding-right, padding-bottom, padding-left -Value: | inherit -Initial: 0 -Applies to: all elements except table-row-group, table-header-group, - table-footer-group, table-row, table-column-group and table-column - all elements except tr -Inherited: no -Percentages: refer to width of containing block -Computed value: the percentage as specified or the absolute length - -border-top-width, border-right-width, border-bottom-width, border-left-width -Value: | inherit -Initial: medium -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: absolute length; '0' if the border style is 'none' or 'hidden' - -border-top-color, border-right-color, border-bottom-color, border-left-color -Value: | transparent | inherit -Initial: the value of the color property -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: when taken from the ’color’ property, the computed value of - ’color’; otherwise, as specified - -border-top-style, border-right-style, border-bottom-style, border-left-style -Value: | inherit -Initial: none -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as specified - -display -Value: inline | block | list-item | inline-block | table | inline-table | - table-row-group | table-header-group | table-footer-group | - table-row | table-column-group | table-column | table-cell | - table-caption | none | inherit -Initial: inline -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: see text - -position -Value: static | relative | absolute | fixed | inherit -Initial: static -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as specified - -top -Value: | | auto | inherit -Initial: auto -Applies to: positioned elements -Inherited: no -Percentages: refer to height of containing block -Computed value: if specified as a length, the corresponding absolute length; if - specified as a percentage, the specified value; otherwise, ’auto’. - -right -Value: | | auto | inherit -Initial: auto -Applies to: positioned elements -Inherited: no -Percentages: refer to width of containing block -Computed value: if specified as a length, the corresponding absolute length; if - specified as a percentage, the specified value; otherwise, ’auto’. - -bottom -Value: | | auto | inherit -Initial: auto -Applies to: positioned elements -Inherited: no -Percentages: refer to height of containing block -Computed value: if specified as a length, the corresponding absolute length; if - specified as a percentage, the specified value; otherwise, ’auto’. - -left -Value: | | auto | inherit -Initial: auto -Applies to: positioned elements -Inherited: no -Percentages: refer to width of containing block -Computed value: if specified as a length, the corresponding absolute length; if - specified as a percentage, the specified value; otherwise, ’auto’. - -float -Value: left | right | none | inherit -Initial: none -Applies to: all, but see 9.7 p. 153 -Inherited: no -Percentages: N/A -Computed value: as specified - -clear -Value: none | left | right | both | inherit -Initial: none -Applies to: block-level elements -Inherited: no -Percentages: N/A -Computed value: as specified - -z-index -Value: auto | integer | inherit -Initial: auto -Applies to: positioned elements -Inherited: no -Percentages: N/A -Computed value: as spec - -direction -Value: ltr | rtl | inherit -Initial: ltr -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -unicode-bidi -Value: normal | embed | bidi-override | inherit -Initial: normal -Applies to: all elements, but see prose -Inherited: no -Percentages: N/A -Computed value: as spec - -width -Value: | | auto | inherit -Initial: auto -Applies to: all elements but non-replaced in-line elements, table rows, and row groups -Inherited: no -Percentages: refer to width of containing block -Computed value: the percentage or 'auto' as specified or the absolute length - -min-width -Value: | | inherit -Initial: 0 -Applies to: all elements but non-replaced in-line elements, table rows, and row groups -Inherited: no -Percentages: refer to width of containing block -Computed value: the percentage as spec or the absolute length - -max-width -Value: | | none | inherit -Initial: none -Applies to: all elements but non-replaced in-line elements, table rows, and row groups -Inherited: no -Percentages: refer to width of containing block -Computed value: the percentage as spec or the absolute length - -height -Value: | | auto | inherit -Initial: auto -Applies to: all elements but non-replaced in-line elements, table columns, and column groups -Inherited: no -Percentages: see prose -Computed value: the percentage as spec or the absolute length - -min-height -Value: | | inherit -Initial: 0 -Applies to: all elements but non-replaced in-line elements, table columns, and column groups -Inherited: no -Percentages: see prose -Computed value: the percentage as spec or the absolute length - -max-height -Value: | | none | inherit -Initial: none -Applies to: all elements but non-replaced in-line elements, table columns, and column groups -Inherited: no -Percentages: refer to height of containing block -Computed value: the percentage as spec or the absolute length - -line-height -Value: normal | | | | -Initial: normal -Applies to: all elements -Inherited: yes -Percentages: refer to the font size of the element itself -Computed value: for and the absolute value, otherwise as specified. - -vertical-align -Value: baseline | sub | super | top | text-top | middle | bottom | text-bottom | - | | inherit -Initial: baseline -Applies to: inline-level and 'table-cell' elements -Inherited: no -Percentages: refer to the line height of the element itself -Computed value: for and the absolute length, otherwise as specified. - -visibility -Value: visible | hidden | collapse | inherit -Initial: visible -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -list-style-type -Value: disc | circle | square | decimal | decimal-leading-zero | - lower-roman | upper-roman | lower-greek | lower-latin | - upper-latin | armenian | georgian | lower-alpha | upper-alpha | - none | inherit -Initial: disc -Applies to: elements with display: list-item -Inherited: yes -Percentages: N/A -Computed value: as spec - -list-style-image -Value: | none | inherit -Initial: none -Applies to: elements with ’display: list-item’ -Inherited: yes -Percentages: N/A -Computed value: absolute URI or ’none’ - -list-style-position -Value: inside | outside | inherit -Initial: outside -Applies to: elements with ’display: list-item’ -Inherited: yes -Percentages: N/A -Computed value: as spec - -background-color -Value: | transparent | inherit -Initial: transparent -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as spec - -font-family -Value: [[ | ] [, | - ]* ] | inherit -Initial: depends on user agent -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -font-style -Value: normal | italic | oblique | inherit -Initial: normal -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -font-variant -Value: normal | small-caps | inherit -Initial: normal -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -font-weight -Value: normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | - 600 | 700 | 800 | 900 | inherit -Initial: normal -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: see text - -font-size -Value: | | | | - inherit -Initial: medium -Applies to: all elements -Inherited: yes -Percentages: refer to inherited font size -Computed value: absolute length - -text-indent -Value: | | inherit -Initial: 0 -Applies to: block containers -Inherited: yes -Percentages: refer to width of containing block -Computed value: the percentage as specified or the absolute length - -text-align -Value: left | right | center | justify | inherit -Initial: a nameless value that acts as ’left’ if ’direction’ is ’ltr’, ’right’ if - ’direction’ is ’rtl’ -Applies to: block containers -Inherited: yes -Percentages: N/A -Computed value: the initial value or as spec - -text-decoration -Value: none | [ underline || overline || line-through || blink ] | inherit -Initial: none -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as spec - -letter-spacing -Value: normal | | inherit -Initial: normal -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: ’normal’ or absolute length - -word-spacing -Value: normal | | inherit -Initial: normal -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: for ’normal’ the value 0; otherwise the absolute length - -text-transform -Value: capitalize | uppercase | lowercase | none | inherit -Initial: none -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -white-space -Value: normal | pre | nowrap | pre-wrap | pre-line | inherit -Initial: normal -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -caption-side -Value: top | bottom | inherit -Initial: top -Applies to: 'table-caption' elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -table-layout -Value: auto | fixed | inherit -Initial: auto -Applies to: ’table’ and ’inline-table’ elements -Inherited: no -Percentages: N/A -Computed value: as spec - -border-collapse -Value: collapse | separate | inherit -Initial: separate -Applies to: ’table’ and ’inline-table’ elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -border-spacing -Value: ? | inherit -Initial: 0 -Applies to: ’table’ and ’inline-table’ elements -Inherited: yes -Percentages: N/A -Computed value: two absolute lengths - -empty-cells -Value: show | hide | inherit -Initial: show -Applies to: 'table-cell' elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -Shorthand Properties - -margin -Value: {1,4} | inherit -Initial: see individual props -Applies to: all elements except elements with table display types other than table-caption, table, and inline-table -Inherited: no -Percentages: refer to width of containing block -Computed value: see individual props - set one - sets all four values - set two - first is top/bottom, second is right/left - set three - first is top, second is right/left, third is bottom - set four - top right bottom left - -padding -Value: {1-4} | inherit -Initial: see individual props -Applies to: all elements except table-row-group, table-header-group, - table-footer-group, table-row, table-column-group and table-column -Inherited: no -Percentages: refer to width of containing block -Computed value: see individual props - set one - sets all four values - set two - first is top/bottom, second is right/left - set four - top right bottom left - -border-width -Value: {1-4} | inherit -Initial: see individual props -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: see individual props - set one - sets all four values - set two - first is top/bottom, second is right/left - set four - top right bottom left - -border-color -Value: [ | transparent]{1-4} | inherit -Initial: see individual props -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: see individual props - set one - sets all four values - set two - first is top/bottom, second is right/left - set four - top right bottom left - -border-style -Value: {1-4} | inherit -Initial: see individual props -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: see individual props - set one - sets all four values - set two - first is top/bottom, second is right/left - set four - top right bottom left - -border-top, border-right, border-bottom, border-left, border -Value: [ || || ] | inherit -Initial: see individ -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: see individ - -list-style -Value: [ <’list-style-type’> || <’list-style-position’> || <’list-style-image’> - ] | inherit -Initial: see individual props -Applies to: elements with ’display: list-item’ -Inherited: yes -Percentages: N/A -Computed value: see individual props - -background -Value: [<’background-color’> || <’background-image’> || <’background- - repeat’> || <’background-attachment’> || <’background- - position’>] | inherit -Initial: see individual props -Applies to: all elements -Inherited: no -Percentages: allowed on background-position -Computed value: see individual props - -font -Value: [ [ <’font-style’> || <’font-variant’> || <’font-weight’> ]? - <’font-size’> [ / <’line-height’> ]? <’font-family’> ] | caption | - icon | menu | message-box | small-caption | status-bar | - inherit -Initial: see individual props -Applies to: all elements -Inherited: yes -Percentages: see individual props -Computed value: see individual props - -probably not support - -overflow -Value: visible | hidden | scroll | auto | inherit -Initial: visible -Applies to: block containers -Inherited: no -Percentages: N/A -Computed value: as spec - -clip -Value: | auto | inherit -Initial: auto -Applies to: absolutely positioned elements -Inherited: no -Percentages: N/A -Computed value: auto if spec as 'auto', otherwise a rectangle with four values, each of which is - 'auto' if spec as 'auto' and the computed length otherwise - -content -Value: normal | none | [ | | | attr() - | open-quote | close-quote | no-open-quote | no-close-quote - ]+ | inherit -Initial: normal -Applies to: :before and :after pseudo elements -Inherited: no -Percentages: N/A -Computed value: On elements, always computes to ’normal’. On :before and - :after, if ’normal’ is specified, computes to ’none’. Otherwise, - for URI values, the absolute URI; for attr() values, the resulting - string; for other keywords, as specified. - -quotes -Value: [ ]+ | none | inherit -Initial: depends on user agent -Applies to: all elements -Inherited: yes -Percentages: N/A -Computed value: as spec - -counter-reset -Value: [ ? ]+ | none | inherit -Initial: none -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as spec - -counter-increment -Value: [ ? ]+ | none | inherit -Initial: none -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as spec - -background-image -Value: | none | inherit -Initial: none -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: absolute URI or none - -background-repeat -Value: repeat | repeat-x | repeat-y | no-repeat | inherit -Initial: repeat -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as spec - -background-attachment -Value: scroll | fixed | inherit -Initial: scroll -Applies to: all elements -Inherited: no -Percentages: N/A -Computed value: as spec - -background-position -Value: [ [ | | left | center | right ] [ - | | top | center | bottom ]? ] | [ [ left | center | - right ] || [ top | center | bottom ] ] | inherit -Initial: 0% 0% -Applies to: all elements -Inherited: no -Percentages: refer to the size of the box itself -Computed value: for the absolute value, otherwise a percentage -#endif - -#if false - -background-color -border-bottom-color -border-bottom-style -border-bottom-width -border-collapse -border-left-color -border-left-style -border-left-width -border-right-color -border-right-style -border-right-width -border-spacing -border-top-color -border-top-style -border-top-width -bottom -caption-side -clear -color -direction -display -empty-cells -float -font-family -font-size -font-style -font-variant -font-weight -height -left -letter-spacing -line-height -list-style-image -list-style-position -list-style-type -margin-bottom -margin-left -margin-right -margin-top -max-height -max-width -min-height -min-width -padding-bottom -padding-left -padding-right -padding-top -position -right -table-layout -text-align -text-decoration -text-indent -text-transform -top -unicode-bidi -vertical-align -visibility -white-space -width -word-spacing -z-index - -attributes -========== -meta -style -_class -href - -don't know -========== -colspan -caption -title -hr -border -http_equiv -content -name -width -height -src -alt -id -descr -type - -elements -======== -html -head -body -div -p -h1 -h2 -h3 -h4 -h5 -h6 -h7 -h8 -h9 -a -b -table -tr -td -br -img -span -blockquote -sub -sup -ol -ul -li -strong -em -tbody - -#endif - +} \ No newline at end of file diff --git a/OpenXmlPowerTools/HtmlToWmlCssParser.cs b/OpenXmlPowerTools/HtmlToWmlCssParser.cs index 56d86d58..c9f36ecb 100644 --- a/OpenXmlPowerTools/HtmlToWmlCssParser.cs +++ b/OpenXmlPowerTools/HtmlToWmlCssParser.cs @@ -1,111 +1,71 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2012-2015. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -Published at http://OpenXmlDeveloper.org -Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx - -***************************************************************************/ - +using SkiaSharp; using System; using System.Collections; using System.Collections.Generic; -using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Text; -namespace OpenXmlPowerTools.HtmlToWml.CSS +namespace Codeuctivity.OpenXmlPowerTools { public class CssAttribute { - private string m_operand; - private CssAttributeOperator? m_op = null; - private string m_val; + public string Operand { get; set; } - public string Operand - { - get { - return m_operand; - } - set { - m_operand = value; - } - } + public CssAttributeOperator? Operator { get; set; } = null; - public CssAttributeOperator? Operator + public string? CssOperatorString { - get { - return m_op; - } - set { - m_op = value; - } - } - - public string CssOperatorString - { - get { - if (this.m_op.HasValue) + get + { + if (Operator.HasValue) { - return this.m_op.Value.ToString(); + return Operator.Value.ToString(); } else { return null; } } - set { - this.m_op = (CssAttributeOperator)Enum.Parse(typeof(CssAttributeOperator), value); - } + set => Operator = (CssAttributeOperator)Enum.Parse(typeof(CssAttributeOperator), value); } - public string Value - { - get { - return m_val; - } - set { - m_val = value; - } - } + public string Value { get; set; } public override string ToString() { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("[{0}", m_operand); - if (m_op.HasValue) + var sb = new StringBuilder(); + sb.AppendFormat("[{0}", Operand); + if (Operator.HasValue) { - switch (m_op.Value) + switch (Operator.Value) { case CssAttributeOperator.Equals: sb.Append("="); break; + case CssAttributeOperator.InList: sb.Append("~="); break; + case CssAttributeOperator.Hyphenated: sb.Append("|="); break; + case CssAttributeOperator.BeginsWith: sb.Append("$="); break; + case CssAttributeOperator.EndsWith: sb.Append("^="); break; + case CssAttributeOperator.Contains: sb.Append("*="); break; } - sb.Append(m_val); + sb.Append(Value); } sb.Append("]"); return sb.ToString(); @@ -129,35 +89,16 @@ public enum CssCombinator PrecededBy, } - public class CssDocument : ItfRuleSetContainer + public class CssDocument : IRuleSetContainer { - private List m_dirs = new List(); - private List m_rulesets = new List(); + public IList Directives { get; set; } = new List(); - public List Directives - { - get { - return m_dirs; - } - set { - m_dirs = value; - } - } - - public List RuleSets - { - get { - return m_rulesets; - } - set { - m_rulesets = value; - } - } + public IList RuleSets { get; set; } = new List(); public override string ToString() { - StringBuilder sb = new StringBuilder(); - foreach (CssDirective cssDir in m_dirs) + var sb = new StringBuilder(); + foreach (var cssDir in Directives) { sb.AppendFormat("{0}" + Environment.NewLine, cssDir.ToString()); } @@ -165,7 +106,7 @@ public override string ToString() { sb.Append(Environment.NewLine); } - foreach (CssRuleSet rules in m_rulesets) + foreach (var rules in RuleSets) { sb.AppendFormat("{0}" + Environment.NewLine, rules.ToString()); @@ -176,130 +117,38 @@ public override string ToString() public class CssDeclaration { - private string m_name; - private CssExpression m_expression; - private bool m_important; + public string Name { get; set; } - public string Name - { - get { - return m_name; - } - set { - m_name = value; - } - } - - public bool Important - { - get { - return m_important; - } - set { - m_important = value; - } - } + public bool Important { get; set; } - public CssExpression Expression - { - get { - return m_expression; - } - set { - m_expression = value; - } - } + public CssExpression Expression { get; set; } public override string ToString() { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.AppendFormat("{0}: {1}{2}", - m_name, - m_expression.ToString(), - m_important ? " !important" : ""); + Name, + Expression.ToString(), + Important ? " !important" : ""); return sb.ToString(); } } - public class CssDirective : ItfDeclarationContainer, ItfRuleSetContainer + public class CssDirective : IDeclarationContainer, IRuleSetContainer { - private CssDirectiveType m_type; - private string m_name; - private CssExpression m_expression; - private List m_mediums = new List(); - private List m_directives = new List(); - private List m_rulesets = new List(); - private List m_declarations = new List(); - - public CssDirectiveType Type - { - get { - return this.m_type; - } - set { - this.m_type = value; - } - } + public CssDirectiveType Type { get; set; } - public string Name - { - get { - return this.m_name; - } - set { - this.m_name = value; - } - } + public string Name { get; set; } - public CssExpression Expression - { - get { - return this.m_expression; - } - set { - this.m_expression = value; - } - } + public CssExpression Expression { get; set; } - public List Mediums - { - get { - return this.m_mediums; - } - set { - this.m_mediums = value; - } - } + public IList Mediums { get; set; } = new List(); - public List Directives - { - get { - return this.m_directives; - } - set { - this.m_directives = value; - } - } + public IList Directives { get; set; } = new List(); - public List RuleSets - { - get { - return this.m_rulesets; - } - set { - this.m_rulesets = value; - } - } + public IList RuleSets { get; set; } = new List(); - public List Declarations - { - get { - return this.m_declarations; - } - set { - this.m_declarations = value; - } - } + public IList Declarations { get; set; } = new List(); public override string ToString() { @@ -308,33 +157,37 @@ public override string ToString() public string ToString(int indentLevel) { - string start = "".PadRight(indentLevel, '\t'); + var start = "".PadRight(indentLevel, '\t'); - switch (m_type) + switch (Type) { case CssDirectiveType.Charset: return ToCharSetString(start); + case CssDirectiveType.Page: return ToPageString(start); + case CssDirectiveType.Media: return ToMediaString(indentLevel); + case CssDirectiveType.Import: return ToImportString(); + case CssDirectiveType.FontFace: return ToFontFaceString(start); } - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); - sb.AppendFormat("{0} ", m_name); + sb.AppendFormat("{0} ", Name); - if (m_expression != null) + if (Expression != null) { - sb.AppendFormat("{0} ", m_expression); + sb.AppendFormat("{0} ", Expression); } - bool first = true; - foreach (CssMedium med in m_mediums) + var first = true; + foreach (var med in Mediums) { if (first) { @@ -348,7 +201,7 @@ public string ToString(int indentLevel) sb.Append(med.ToString()); } - bool HasBlock = (this.m_declarations.Count > 0 || this.m_directives.Count > 0 || this.m_rulesets.Count > 0); + var HasBlock = Declarations.Count > 0 || Directives.Count > 0 || RuleSets.Count > 0; if (!HasBlock) { @@ -358,18 +211,18 @@ public string ToString(int indentLevel) sb.Append(" {" + Environment.NewLine + start); - foreach (CssDirective dir in m_directives) + foreach (var dir in Directives) { sb.AppendFormat("{0}" + Environment.NewLine, dir.ToCharSetString(start + "\t")); } - foreach (CssRuleSet rules in m_rulesets) + foreach (var rules in RuleSets) { sb.AppendFormat("{0}" + Environment.NewLine, rules.ToString(indentLevel + 1)); } first = true; - foreach (CssDeclaration decl in m_declarations) + foreach (var decl in Declarations) { if (first) { @@ -389,11 +242,11 @@ public string ToString(int indentLevel) private string ToFontFaceString(string start) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.Append("@font-face {"); - bool first = true; - foreach (CssDeclaration decl in m_declarations) + var first = true; + foreach (var decl in Declarations) { if (first) { @@ -413,14 +266,14 @@ private string ToFontFaceString(string start) private string ToImportString() { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.Append("@import "); - if (m_expression != null) + if (Expression != null) { - sb.AppendFormat("{0} ", m_expression); + sb.AppendFormat("{0} ", Expression); } - bool first = true; - foreach (CssMedium med in m_mediums) + var first = true; + foreach (var med in Mediums) { if (first) { @@ -439,11 +292,11 @@ private string ToImportString() private string ToMediaString(int indentLevel) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.Append("@media"); - bool first = true; - foreach (CssMedium medium in m_mediums) + var first = true; + foreach (var medium in Mediums) { if (first) { @@ -458,7 +311,7 @@ private string ToMediaString(int indentLevel) } sb.Append(" {" + Environment.NewLine); - foreach (CssRuleSet ruleset in m_rulesets) + foreach (var ruleset in RuleSets) { sb.AppendFormat("{0}" + Environment.NewLine, ruleset.ToString(indentLevel + 1)); } @@ -469,16 +322,16 @@ private string ToMediaString(int indentLevel) private string ToPageString(string start) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.Append("@page "); - if (m_expression != null) + if (Expression != null) { - sb.AppendFormat("{0} ", m_expression); + sb.AppendFormat("{0} ", Expression); } sb.Append("{" + Environment.NewLine); - bool first = true; - foreach (CssDeclaration decl in m_declarations) + var first = true; + foreach (var decl in Declarations) { if (first) { @@ -498,9 +351,9 @@ private string ToPageString(string start) private string ToCharSetString(string start) { - return string.Format("{2}{0} {1}", - m_name, - m_expression.ToString(), + return string.Format("{2}{0} {1}", + Name, + Expression.ToString(), start); } } @@ -518,55 +371,21 @@ public enum CssDirectiveType public class CssExpression { - private List m_terms = new List(); + public List Terms { get; set; } = new List(); - public List Terms - { - get { - return m_terms; - } - set { - m_terms = value; - } - } + public bool IsNotAuto => this != null && ToString() != "auto"; - public bool IsNotAuto - { - get - { - return (this != null && this.ToString() != "auto"); - } - } - - public bool IsAuto - { - get - { - return (this != null && this.ToString() == "auto"); - } - } + public bool IsAuto => this != null && ToString() == "auto"; - public bool IsNotNormal - { - get - { - return (this != null && this.ToString() != "normal"); - } - } + public bool IsNotNormal => this != null && ToString() != "normal"; - public bool IsNormal - { - get - { - return (this != null && this.ToString() == "normal"); - } - } + public bool IsNormal => this != null && ToString() == "normal"; public override string ToString() { - StringBuilder sb = new StringBuilder(); - bool first = true; - foreach (CssTerm term in m_terms) + var sb = new StringBuilder(); + var first = true; + foreach (var term in Terms) { if (first) { @@ -574,7 +393,7 @@ public override string ToString() } else { - sb.AppendFormat("{0} ", + sb.AppendFormat("{0} ", term.Separator.HasValue ? term.Separator.Value.ToString() : ""); } sb.Append(term.ToString()); @@ -606,18 +425,17 @@ public static explicit operator TPoint(CssExpression e) // will only be called on expression that is in terms of points public static explicit operator Twip(CssExpression length) { - if (length.Terms.Count() == 1) + if (length.Terms.Count == 1) { - CssTerm term = length.Terms.First(); - if (term.Unit == CssUnit.PT) + var term = length.Terms.First(); + if (term.Unit == CssUnit.PT && double.TryParse(term?.Value?.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out var ptValue)) { - double ptValue; - if (double.TryParse(term.Value.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out ptValue)) + if (term.Sign == '-') { - if (term.Sign == '-') - ptValue = -ptValue; - return new Twip((long)(ptValue * 20)); + ptValue = -ptValue; } + + return new Twip((long)(ptValue * 20)); } } return 0; @@ -626,37 +444,18 @@ public static explicit operator Twip(CssExpression length) public class CssFunction { - private string m_name; - private CssExpression m_expression; + public string Name { get; set; } - public string Name - { - get { - return m_name; - } - set { - m_name = value; - } - } - - public CssExpression Expression - { - get { - return m_expression; - } - set { - m_expression = value; - } - } + public CssExpression Expression { get; set; } public override string ToString() { - StringBuilder sb = new StringBuilder(); - sb.AppendFormat("{0}(", m_name); - if (m_expression != null) + var sb = new StringBuilder(); + sb.AppendFormat("{0}(", Name); + if (Expression != null) { - bool first = true; - foreach (CssTerm t in m_expression.Terms) + var first = true; + foreach (var t in Expression.Terms) { if (first) { @@ -667,7 +466,7 @@ public override string ToString() sb.Append(", "); } - bool quote = false; + var quote = false; if (t.Type == CssTermType.String && !t.Value.EndsWith("=")) { quote = true; @@ -688,19 +487,19 @@ public override string ToString() } } - public interface ItfDeclarationContainer + public interface IDeclarationContainer { - List Declarations { get; set; } + IList Declarations { get; set; } } - public interface ItfRuleSetContainer + public interface IRuleSetContainer { - List RuleSets { get; set; } + IList RuleSets { get; set; } } - public interface ItfSelectorContainer + public interface ISelectorContainer { - List Selectors { get; set; } + IList Selectors { get; set; } } public enum CssMedium @@ -719,49 +518,21 @@ public enum CssMedium public class CssPropertyValue { - private CssValueType m_type; - private CssUnit m_unit; - private string m_value; - - public CssValueType Type - { - get { - return this.m_type; - } - set { - this.m_type = value; - } - } + public CssValueType Type { get; set; } - public CssUnit Unit - { - get { - return this.m_unit; - } - set { - this.m_unit = value; - } - } + public CssUnit Unit { get; set; } - public string Value - { - get { - return this.m_value; - } - set { - this.m_value = value; - } - } + public string Value { get; set; } public override string ToString() { - StringBuilder sb = new StringBuilder(m_value); - if (m_type == CssValueType.Unit) + var sb = new StringBuilder(Value); + if (Type == CssValueType.Unit) { - sb.Append(m_unit.ToString().ToLower()); + sb.Append(Unit.ToString().ToLower()); } sb.Append(" ["); - sb.Append(m_type.ToString()); + sb.Append(Type.ToString()); sb.Append("]"); return sb.ToString(); } @@ -770,12 +541,12 @@ public bool IsColor { get { - if (((m_type == CssValueType.Hex) - || (m_type == CssValueType.String && m_value.StartsWith("#"))) - && (m_value.Length == 6 || (m_value.Length == 7 && m_value.StartsWith("#")))) + if ((Type == CssValueType.Hex + || Type == CssValueType.String && Value.StartsWith("#")) + && (Value.Length == 6 || Value.Length == 7 && Value.StartsWith("#"))) { - bool hex = true; - foreach (char c in m_value) + var hex = true; + foreach (var c in Value) { if (!char.IsDigit(c) && c != '#' @@ -798,10 +569,10 @@ public bool IsColor } return hex; } - else if (m_type == CssValueType.String) + else if (Type == CssValueType.String) { - bool number = true; - foreach (char c in m_value) + var number = true; + foreach (var c in Value) { if (!char.IsDigit(c)) { @@ -811,7 +582,7 @@ public bool IsColor } if (number) { return false; } - if (ColorParser.IsValidName(m_value)) + if (ColorParser.IsValidName(Value)) { return true; } @@ -820,64 +591,50 @@ public bool IsColor } } - public Color ToColor() + public SKColor ToColor() { - string hex = "000000"; - if (m_type == CssValueType.Hex) + var hex = "000000"; + if (Type == CssValueType.Hex) { - if (m_value.Length == 7 && m_value.StartsWith("#")) + if (Value.Length == 7 && Value.StartsWith("#")) { - hex = m_value.Substring(1); + hex = Value.Substring(1); } - else if (m_value.Length == 6) + else if (Value.Length == 6) { - hex = m_value; + hex = Value; } } else { - if (ColorParser.TryFromName(m_value, out var c)) + if (ColorParser.TryFromName(Value, out var c)) { return c; } } - int r = ConvertFromHex(hex.Substring(0, 2)); - int g = ConvertFromHex(hex.Substring(2, 2)); - int b = ConvertFromHex(hex.Substring(4)); - return Color.FromArgb(r, g, b); + var r = ConvertFromHex(hex.Substring(0, 2)); + var g = ConvertFromHex(hex.Substring(2, 2)); + var b = ConvertFromHex(hex.Substring(4)); + return new SKColor(r, g, b); } - private int ConvertFromHex(string input) + private byte ConvertFromHex(string input) { int val; - int result = 0; - for (int i = 0; i < input.Length; i++) - { - string chunk = input.Substring(i, 1).ToUpper(); - switch (chunk) - { - case "A": - val = 10; - break; - case "B": - val = 11; - break; - case "C": - val = 12; - break; - case "D": - val = 13; - break; - case "E": - val = 14; - break; - case "F": - val = 15; - break; - default: - val = int.Parse(chunk); - break; - } + var result = 0; + for (var i = 0; i < input.Length; i++) + { + var chunk = input.Substring(i, 1).ToUpper(); + val = chunk switch + { + "A" => 10, + "B" => 11, + "C" => 12, + "D" => 13, + "E" => 14, + "F" => 15, + _ => int.Parse(chunk), + }; if (i == 0) { result += val * 16; @@ -887,34 +644,15 @@ private int ConvertFromHex(string input) result += val; } } - return result; + return BitConverter.GetBytes(result)[0]; } } - public class CssRuleSet : ItfDeclarationContainer + public class CssRuleSet : IDeclarationContainer { - private List m_selectors = new List(); - private List m_declarations = new List(); - - public List Selectors - { - get { - return m_selectors; - } - set { - m_selectors = value; - } - } + public IList Selectors { get; set; } = new List(); - public List Declarations - { - get { - return m_declarations; - } - set { - m_declarations = value; - } - } + public IList Declarations { get; set; } = new List(); public override string ToString() { @@ -923,31 +661,31 @@ public override string ToString() public string ToString(int indentLevel) { - string start = ""; - for (int i = 0; i < indentLevel; i++) + var start = new StringBuilder(); + for (var i = 0; i < indentLevel; i++) { - start += "\t"; + start.Append("\t"); } - StringBuilder sb = new StringBuilder(); - bool first = true; - foreach (CssSelector sel in m_selectors) + var sb = new StringBuilder(); + var first = true; + foreach (var sel in Selectors) { - if (first) - { - first = false; - sb.Append(start); - } - else - { - sb.Append(", "); + if (first) + { + first = false; + sb.Append(start); + } + else + { + sb.Append(", "); } sb.Append(sel.ToString()); } sb.Append(" {" + Environment.NewLine); sb.Append(start); - foreach (CssDeclaration dec in m_declarations) + foreach (var dec in Declarations) { sb.AppendFormat("\t{0};" + Environment.NewLine + "{1}", dec.ToString(), start); } @@ -959,23 +697,13 @@ public string ToString(int indentLevel) public class CssSelector { - private List m_simpleSelectors = new List(); - - public List SimpleSelectors - { - get { - return m_simpleSelectors; - } - set { - m_simpleSelectors = value; - } - } + public List SimpleSelectors { get; set; } = new List(); public override string ToString() { - StringBuilder sb = new StringBuilder(); - bool first = true; - foreach (CssSimpleSelector ss in m_simpleSelectors) + var sb = new StringBuilder(); + var first = true; + foreach (var ss in SimpleSelectors) { if (first) { @@ -993,152 +721,92 @@ public override string ToString() public class CssSimpleSelector { - private CssCombinator? m_combinator = null; - private string m_elementname; - private string m_id; private string m_cls; - private CssAttribute m_attribute; - private string m_pseudo; - private CssFunction m_function; private CssSimpleSelector m_child; - public CssCombinator? Combinator - { - get { - return m_combinator; - } - set { - m_combinator = value; - } - } - public string CombinatorString + public CssCombinator? Combinator { get; set; } = null; + + public string? CombinatorString { - get { - if (this.m_combinator.HasValue) + get + { + if (Combinator.HasValue) { - return m_combinator.ToString(); + return Combinator.ToString(); } else { return null; } } - set { - this.m_combinator = (CssCombinator)Enum.Parse(typeof(CssCombinator), value); - } + set => Combinator = (CssCombinator)Enum.Parse(typeof(CssCombinator), value); } - public string ElementName - { - get { - return m_elementname; - } - set { - m_elementname = value; - } - } + public string ElementName { get; set; } - public string ID - { - get { - return m_id; - } - set { - m_id = value; - } - } + public string ID { get; set; } public string Class { - get { - return m_cls; - } - set { - m_cls = value; - } + get => m_cls; + set => m_cls = value; } - public string Pseudo - { - get { - return m_pseudo; - } - set { - m_pseudo = value; - } - } + public string Pseudo { get; set; } - public CssAttribute Attribute - { - get { - return m_attribute; - } - set { - m_attribute = value; - } - } + public CssAttribute Attribute { get; set; } - public CssFunction Function - { - get { - return m_function; - } - set { - m_function = value; - } - } + public CssFunction Function { get; set; } public CssSimpleSelector Child { - get { - return m_child; - } - set { - m_child = value; - } + get => m_child; + set => m_child = value; } public override string ToString() { - StringBuilder sb = new StringBuilder(); - if (m_combinator.HasValue) + var sb = new StringBuilder(); + if (Combinator.HasValue) { - switch (m_combinator.Value) + switch (Combinator.Value) { - case OpenXmlPowerTools.HtmlToWml.CSS.CssCombinator.PrecededImmediatelyBy: - sb.Append(" + "); + case CssCombinator.PrecededImmediatelyBy: + sb.Append(" + "); break; - case OpenXmlPowerTools.HtmlToWml.CSS.CssCombinator.ChildOf: - sb.Append(" > "); + + case CssCombinator.ChildOf: + sb.Append(" > "); break; - case OpenXmlPowerTools.HtmlToWml.CSS.CssCombinator.PrecededBy: - sb.Append(" ~ "); + + case CssCombinator.PrecededBy: + sb.Append(" ~ "); break; } } - if (m_elementname != null) + if (ElementName != null) { - sb.Append(m_elementname); + sb.Append(ElementName); } - if (m_id != null) + if (ID != null) { - sb.AppendFormat("#{0}", m_id); + sb.AppendFormat("#{0}", ID); } if (m_cls != null) { sb.AppendFormat(".{0}", m_cls); } - if (m_pseudo != null) + if (Pseudo != null) { - sb.AppendFormat(":{0}", m_pseudo); + sb.AppendFormat(":{0}", Pseudo); } - if (m_attribute != null) + if (Attribute != null) { - sb.Append(m_attribute.ToString()); + sb.Append(Attribute.ToString()); } - if (m_function != null) + if (Function != null) { - sb.Append(m_function.ToString()); + sb.Append(Function.ToString()); } if (m_child != null) { @@ -1154,164 +822,71 @@ public override string ToString() public class CssTag { - private CssTagType m_tagtype; - private string m_name; - private string m_cls; - private string m_pseudo; - private string m_id; - private char m_parentrel = '\0'; - private CssTag m_subtag; - private List m_attribs = new List(); + public CssTagType TagType { get; set; } - public CssTagType TagType - { - get { - return m_tagtype; - } - set { - m_tagtype = value; - } - } + public bool IsIDSelector => Id != null; - public bool IsIDSelector - { - get { - return m_id != null; - } - } + public bool HasName => Name != null; - public bool HasName - { - get { - return m_name != null; - } - } + public bool HasClass => Class != null; - public bool HasClass - { - get { - return m_cls != null; - } - } + public bool HasPseudoClass => Pseudo != null; - public bool HasPseudoClass - { - get { - return m_pseudo != null; - } - } + public string Name { get; set; } - public string Name - { - get { - return m_name; - } - set { - m_name = value; - } - } - - public string Class - { - get { - return m_cls; - } - set { - m_cls = value; - } - } + public string Class { get; set; } - public string Pseudo - { - get { - return m_pseudo; - } - set { - m_pseudo = value; - } - } + public string Pseudo { get; set; } - public string Id - { - get { - return m_id; - } - set { - m_id = value; - } - } + public string Id { get; set; } - public char ParentRelationship - { - get { - return m_parentrel; - } - set { - m_parentrel = value; - } - } + public char ParentRelationship { get; set; } = '\0'; - public CssTag SubTag - { - get { - return m_subtag; - } - set { - m_subtag = value; - } - } + public CssTag SubTag { get; set; } - public List Attributes - { - get { - return m_attribs; - } - set { - m_attribs = value; - } - } + public List Attributes { get; set; } = new List(); public override string ToString() { - StringBuilder sb = new StringBuilder(ToShortString()); + var sb = new StringBuilder(ToShortString()); - if (m_subtag != null) + if (SubTag != null) { sb.Append(" "); - sb.Append(m_subtag.ToString()); + sb.Append(SubTag.ToString()); } return sb.ToString(); } public string ToShortString() { - StringBuilder sb = new StringBuilder(); - if (m_parentrel != '\0') + var sb = new StringBuilder(); + if (ParentRelationship != '\0') { - sb.AppendFormat("{0} ", m_parentrel.ToString()); + sb.AppendFormat("{0} ", ParentRelationship.ToString()); } if (HasName) { - sb.Append(m_name); + sb.Append(Name); } - foreach (string atr in m_attribs) + foreach (var atr in Attributes) { sb.AppendFormat("[{0}]", atr); } if (HasClass) { sb.Append("."); - sb.Append(m_cls); + sb.Append(Class); } if (IsIDSelector) { sb.Append("#"); - sb.Append(m_id); + sb.Append(Id); } if (HasPseudoClass) { sb.Append(":"); - sb.Append(m_pseudo); + sb.Append(Pseudo); } return sb.ToString(); } @@ -1329,143 +904,82 @@ public enum CssTagType public class CssTerm { - private char? m_separator; - private char? m_sign; - private CssTermType m_type; - private string m_val; - private CssUnit? m_unit; - private CssFunction m_function; - - public char? Separator - { - get { - return m_separator; - } - set { - m_separator = value; - } - } - public string SeparatorChar - { - get { - return m_separator.HasValue ? this.m_separator.Value.ToString() : null; - } - set { - m_separator = !string.IsNullOrEmpty(value) ? value[0] : '\0'; - } - } + public char? Separator { get; set; } - public char? Sign + public string? SeparatorChar { - get { - return m_sign; - } - set { - m_sign = value; - } - } - public string SignChar - { - get { - return this.m_sign.HasValue ? this.m_sign.Value.ToString() : null; - } - set { - this.m_sign = !string.IsNullOrEmpty(value) ? value[0] : '\0'; - } + get => Separator.HasValue ? Separator.Value.ToString() : null; + set => Separator = !string.IsNullOrEmpty(value) ? value?[0] : '\0'; } - public CssTermType Type - { - get { - return m_type; - } - set { - m_type = value; - } - } + public char? Sign { get; set; } - public string Value + public string? SignChar { - get { - return m_val; - } - set { - m_val = value; - } + get => Sign?.ToString() ?? null; + set => Sign = !string.IsNullOrEmpty(value) ? value![0] : '\0'; } - public CssUnit? Unit - { - get { - return m_unit; - } - set { - m_unit = value; - } - } - public string UnitString + public CssTermType Type { get; set; } + + public string? Value { get; set; } + + public CssUnit? Unit { get; set; } + + public string? UnitString { - get { - if (this.m_unit.HasValue) + get + { + if (Unit.HasValue) { - return this.m_unit.ToString(); + return Unit.ToString(); } else { return null; } } - set { - this.m_unit = (CssUnit)Enum.Parse(typeof(CssUnit), value); - } + set => Unit = (CssUnit)Enum.Parse(typeof(CssUnit), value); } - public CssFunction Function - { - get { - return m_function; - } - set { - m_function = value; - } - } + public CssFunction Function { get; set; } public override string ToString() { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); - if (m_type == CssTermType.Function) + if (Type == CssTermType.Function) { - sb.Append(m_function.ToString()); + sb.Append(Function.ToString()); } - else if (m_type == CssTermType.Url) + else if (Type == CssTermType.Url) { - sb.AppendFormat("url('{0}')", m_val); + sb.AppendFormat("url('{0}')", Value); } - else if (m_type == CssTermType.Unicode) + else if (Type == CssTermType.Unicode) { - sb.AppendFormat("U\\{0}", m_val.ToUpper()); + sb.AppendFormat("U\\{0}", Value.ToUpper()); } - else if (m_type == CssTermType.Hex) + else if (Type == CssTermType.Hex) { - sb.Append(m_val.ToUpper()); + sb.Append(Value.ToUpper()); } else { - if (m_sign.HasValue) + if (Sign.HasValue) { - sb.Append(m_sign.Value); + sb.Append(Sign.Value); } - sb.Append(m_val); - if (m_unit.HasValue) + sb.Append(Value); + if (Unit.HasValue) { - if (m_unit.Value == OpenXmlPowerTools.HtmlToWml.CSS.CssUnit.Percent) + if (Unit.Value == CssUnit.Percent) { sb.Append("%"); } else { - sb.Append(CssUnitOutput.ToString(m_unit.Value)); + sb.Append(CssUnitOutput.ToString(Unit.Value)); } } } @@ -1477,13 +991,12 @@ public bool IsColor { get { - if (((m_type == CssTermType.Hex) - || (m_type == CssTermType.String && m_val.StartsWith("#"))) - && (m_val.Length == 6 || m_val.Length == 3 || ((m_val.Length == 7 || m_val.Length == 4) - && m_val.StartsWith("#")))) + if ((Type == CssTermType.Hex || Type == CssTermType.String && Value.StartsWith("#")) + && (Value.Length == 6 || Value.Length == 3 || (Value.Length == 7 || Value.Length == 4) + && Value.StartsWith("#"))) { - bool hex = true; - foreach (char c in m_val) + var hex = true; + foreach (var c in Value) { if (!char.IsDigit(c) && c != '#' @@ -1506,10 +1019,10 @@ public bool IsColor } return hex; } - else if (m_type == CssTermType.String) + else if (Type == CssTermType.String) { - bool number = true; - foreach (char c in m_val) + var number = true; + foreach (var c in Value) { if (!char.IsDigit(c)) { @@ -1517,57 +1030,41 @@ public bool IsColor break; } } - if (number) { + if (number) + { return false; } - if (ColorParser.IsValidName(m_val)) + if (ColorParser.IsValidName(Value)) { return true; } } - else if (m_type == CssTermType.Function) + else if (Type == CssTermType.Function && ((Function.Name.ToLower().Equals("hsl") || Function.Name.ToLower().Equals("rgb")) && Function.Expression.Terms.Count == 3 || + (Function.Name.ToLower().Equals("hsla") || Function.Name.ToLower().Equals("rgba")) && Function.Expression.Terms.Count == 4)) { - if ((m_function.Name.ToLower().Equals("rgb") && m_function.Expression.Terms.Count == 3) - || (m_function.Name.ToLower().Equals("rgba") && m_function.Expression.Terms.Count == 4) - ) + for (var i = 0; i < Function.Expression.Terms.Count; i++) { - for (int i = 0; i < m_function.Expression.Terms.Count; i++) + if (Function.Expression.Terms[i].Type != CssTermType.Number) { - if (m_function.Expression.Terms[i].Type != CssTermType.Number) - { - return false; - } - } - return true; - } - else if ((m_function.Name.ToLower().Equals("hsl") && m_function.Expression.Terms.Count == 3) - || (m_function.Name.ToLower().Equals("hsla") && m_function.Expression.Terms.Count == 4) - ) - { - for (int i = 0; i < m_function.Expression.Terms.Count; i++) - { - if (m_function.Expression.Terms[i].Type != CssTermType.Number) - { - return false; - } + return false; } - return true; } + return true; } return false; } } - private int GetRGBValue(CssTerm t) + private byte GetRGBValue(CssTerm t) { try { - if (t.Unit.HasValue && t.Unit.Value == OpenXmlPowerTools.HtmlToWml.CSS.CssUnit.Percent) + if (t.Unit.HasValue && t.Unit.Value == CssUnit.Percent) { - return (int)(255f * float.Parse(t.Value) / 100f); + return BitConverter.GetBytes((int)(255f * float.Parse(t.Value) / 100f))[0]; } - return int.Parse(t.Value); + return BitConverter.GetBytes(int.Parse(t.Value))[0]; } catch { } return 0; @@ -1583,119 +1080,116 @@ private int GetHueValue(CssTerm t) return 0; } - public Color ToColor() + public SKColor ToColor() { - string hex = "000000"; - if (m_type == CssTermType.Hex) + var hex = "000000"; + if (Type == CssTermType.Hex) { - if ((m_val.Length == 7 || m_val.Length == 4) && m_val.StartsWith("#")) + if ((Value.Length == 7 || Value.Length == 4) && Value.StartsWith("#")) { - hex = m_val.Substring(1); + hex = Value.Substring(1); } - else if (m_val.Length == 6 || m_val.Length == 3) + else if (Value.Length == 6 || Value.Length == 3) { - hex = m_val; + hex = Value; } } - else if (m_type == CssTermType.Function) + else if (Type == CssTermType.Function) { - if ((m_function.Name.ToLower().Equals("rgb") && m_function.Expression.Terms.Count == 3) - || (m_function.Name.ToLower().Equals("rgba") && m_function.Expression.Terms.Count == 4) + if (Function.Name.ToLower().Equals("rgb") && Function.Expression.Terms.Count == 3 + || Function.Name.ToLower().Equals("rgba") && Function.Expression.Terms.Count == 4 ) { - int fr = 0, fg = 0, fb = 0; - for (int i = 0; i < m_function.Expression.Terms.Count; i++) + byte fr = 0, fg = 0, fb = 0; + for (var i = 0; i < Function.Expression.Terms.Count; i++) { - if (m_function.Expression.Terms[i].Type != CssTermType.Number) - { - return Color.Black; + if (Function.Expression.Terms[i].Type != CssTermType.Number) + { + return SKColors.Black; } switch (i) { - case 0: fr = GetRGBValue(m_function.Expression.Terms[i]); + case 0: + fr = GetRGBValue(Function.Expression.Terms[i]); break; - case 1: fg = GetRGBValue(m_function.Expression.Terms[i]); + + case 1: + fg = GetRGBValue(Function.Expression.Terms[i]); break; - case 2: fb = GetRGBValue(m_function.Expression.Terms[i]); + + case 2: + fb = GetRGBValue(Function.Expression.Terms[i]); break; } } - return Color.FromArgb(fr, fg, fb); + return new SKColor(fr, fg, fb); } - else if ((m_function.Name.ToLower().Equals("hsl") && m_function.Expression.Terms.Count == 3) - || (m_function.Name.Equals("hsla") && m_function.Expression.Terms.Count == 4) + else if (Function.Name.ToLower().Equals("hsl") && Function.Expression.Terms.Count == 3 + || Function.Name.Equals("hsla") && Function.Expression.Terms.Count == 4 ) { int h = 0, s = 0, v = 0; - for (int i = 0; i < m_function.Expression.Terms.Count; i++) + for (var i = 0; i < Function.Expression.Terms.Count; i++) { - if (m_function.Expression.Terms[i].Type != CssTermType.Number) { return Color.Black; } + if (Function.Expression.Terms[i].Type != CssTermType.Number) { return SKColors.Black; } switch (i) { - case 0: h = GetHueValue(m_function.Expression.Terms[i]); + case 0: + h = GetHueValue(Function.Expression.Terms[i]); break; - case 1: s = GetRGBValue(m_function.Expression.Terms[i]); + + case 1: + s = GetRGBValue(Function.Expression.Terms[i]); break; - case 2: v = GetRGBValue(m_function.Expression.Terms[i]); + + case 2: + v = GetRGBValue(Function.Expression.Terms[i]); break; } } - HueSatVal hsv = new HueSatVal(h, s, v); + var hsv = new HueSatVal(h, s, v); return hsv.Color; } } else { - if (ColorParser.TryFromName(m_val, out var c)) + if (ColorParser.TryFromName(Value, out var c)) { return c; } } if (hex.Length == 3) { - string temp = ""; - foreach (char c in hex) + var temp = ""; + foreach (var c in hex) { temp += c.ToString() + c.ToString(); } hex = temp; } - int r = ConvertFromHex(hex.Substring(0, 2)); - int g = ConvertFromHex(hex.Substring(2, 2)); - int b = ConvertFromHex(hex.Substring(4)); - return Color.FromArgb(r, g, b); + var r = ConvertFromHex(hex.Substring(0, 2)); + var g = ConvertFromHex(hex.Substring(2, 2)); + var b = ConvertFromHex(hex.Substring(4)); + return new SKColor(r, g, b); } - private int ConvertFromHex(string input) + + private byte ConvertFromHex(string input) { int val; - int result = 0; - for (int i = 0; i < input.Length; i++) - { - string chunk = input.Substring(i, 1).ToUpper(); - switch (chunk) - { - case "A": - val = 10; - break; - case "B": - val = 11; - break; - case "C": - val = 12; - break; - case "D": - val = 13; - break; - case "E": - val = 14; - break; - case "F": - val = 15; - break; - default: - val = int.Parse(chunk); - break; - } + var result = 0; + for (var i = 0; i < input.Length; i++) + { + var chunk = input.Substring(i, 1).ToUpper(); + val = chunk switch + { + "A" => 10, + "B" => 11, + "C" => 12, + "D" => 13, + "E" => 14, + "F" => 15, + _ => int.Parse(chunk), + }; if (i == 0) { result += val * 16; @@ -1705,7 +1199,7 @@ private int ConvertFromHex(string input) result += val; } } - return result; + return BitConverter.GetBytes(result)[0]; } } @@ -1779,13 +1273,12 @@ public enum CssValueType public class CssParser { - private List m_errors = new List(); private CssDocument m_doc; public CssDocument ParseText(string content) { - MemoryStream mem = new MemoryStream(); - byte[] bytes = ASCIIEncoding.ASCII.GetBytes(content); + var mem = new MemoryStream(); + var bytes = Encoding.ASCII.GetBytes(content); mem.Write(bytes, 0, bytes.Length); try { @@ -1793,7 +1286,7 @@ public CssDocument ParseText(string content) } catch (OpenXmlPowerToolsException e) { - string msg = e.Message + ". CSS => " + content; + var msg = e.Message + ". CSS => " + content; throw new OpenXmlPowerToolsException(msg); } } @@ -1801,85 +1294,54 @@ public CssDocument ParseText(string content) // following method should be private, as it does not properly re-throw OpenXmlPowerToolsException private CssDocument ParseStream(Stream stream) { - Scanner scanner = new Scanner(stream); - Parser parser = new Parser(scanner); + var scanner = new Scanner(stream); + var parser = new Parser(scanner); parser.Parse(); m_doc = parser.CssDoc; return m_doc; } - public CssDocument CSSDocument - { - get { return m_doc; } - } + public CssDocument CSSDocument => m_doc; - public List Errors - { - get { return m_errors; } - } + public List Errors { get; } = new List(); } // Hue Sat and Val values from 0 - 255. internal struct HueSatVal { - private int m_hue; - private int m_sat; - private int m_val; public HueSatVal(int h, int s, int v) { - m_hue = h; - m_sat = s; - m_val = v; + Hue = h; + Saturation = s; + Value = v; } - public HueSatVal(Color color) + + public HueSatVal(SKColor color) { - m_hue = 0; - m_sat = 0; - m_val = 0; + Hue = 0; + Saturation = 0; + Value = 0; ConvertFromRGB(color); } - public int Hue - { - get { - return m_hue; - } - set { - m_hue = value; - } - } - public int Saturation - { - get { - return m_sat; - } - set { - m_sat = value; - } - } - public int Value - { - get { - return m_val; - } - set { - m_val = value; - } - } - public Color Color + + public int Hue { get; set; } + + public int Saturation { get; set; } + + public int Value { get; set; } + + public SKColor Color { - get { - return ConvertToRGB(); - } - set { - ConvertFromRGB(value); - } + get => ConvertToRGB(); + set => ConvertFromRGB(value); } - private void ConvertFromRGB(Color color) + + private void ConvertFromRGB(SKColor color) { double min; double max; double delta; - double r = (double)color.R / 255.0d; - double g = (double)color.G / 255.0d; - double b = (double)color.B / 255.0d; + var r = CalcRedConponent(color); + var g = CalcGreenConponent(color); + var b = CalcBlueConponent(color); double h; double s; double v; min = Math.Min(Math.Min(r, g), b); @@ -1896,7 +1358,7 @@ private void ConvertFromRGB(Color color) s = delta / max; if (r == max) { - h = (60D * ((g - b) / delta)) % 360.0d; + h = 60D * ((g - b) / delta) % 360.0d; } else if (g == max) { @@ -1917,7 +1379,22 @@ private void ConvertFromRGB(Color color) Value = (int)(v * 255.0d); } - private Color ConvertToRGB() + private double CalcRedConponent(SKColor color) + { + return color.Red; + } + + private double CalcGreenConponent(SKColor color) + { + return color.Green; + } + + private double CalcBlueConponent(SKColor color) + { + return color.Blue; + } + + private SKColor ConvertToRGB() { double h; double s; @@ -1926,9 +1403,9 @@ private Color ConvertToRGB() double g = 0; double b = 0; - h = ((double)Hue / 255.0d * 360.0d) % 360.0d; - s = (double)Saturation / 255.0d; - v = (double)Value / 255.0d; + h = Hue / 255.0d * 360.0d % 360.0d; + s = Saturation / 255.0d; + v = Value / 255.0d; if (s == 0) { @@ -1947,13 +1424,13 @@ private Color ConvertToRGB() double sectorPos; sectorPos = h / 60.0d; - sectorNumber = (int)(Math.Floor(sectorPos)); + sectorNumber = (int)Math.Floor(sectorPos); fractionalPart = sectorPos - sectorNumber; p = v * (1.0d - s); - q = v * (1.0d - (s * fractionalPart)); - t = v * (1.0d - (s * (1.0d - fractionalPart))); + q = v * (1.0d - s * fractionalPart); + t = v * (1.0d - s * (1.0d - fractionalPart)); switch (sectorNumber) { @@ -1962,26 +1439,31 @@ private Color ConvertToRGB() g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; @@ -1989,7 +1471,7 @@ private Color ConvertToRGB() break; } } - return Color.FromArgb((int)(r * 255.0d), (int)(g * 255.0d), (int)(b * 255.0d)); + return new SKColor((byte)(r * 255.0d), (byte)(g * 255.0d), (byte)(b * 255.0d)); } public static bool operator !=(HueSatVal left, HueSatVal right) @@ -1999,7 +1481,7 @@ private Color ConvertToRGB() public static bool operator ==(HueSatVal left, HueSatVal right) { - return (left.Hue == right.Hue && left.Value == right.Value && left.Saturation == right.Saturation); + return left.Hue == right.Hue && left.Value == right.Value && left.Saturation == right.Saturation; } public override bool Equals(object obj) @@ -2022,26 +1504,26 @@ public class Parser public const int c_whitespace = 4; public const int c_maxT = 49; - const bool T = true; - const bool x = false; - const int minErrDist = 2; + private const bool T = true; + private const bool x = false; + private const int minErrDist = 2; - public Scanner m_scanner; - public Errors m_errors; + public Scanner Scanner { get; set; } + public Errors Errors { get; set; } - public CssToken m_lastRecognizedToken; - public CssToken m_lookaheadToken; - int errDist = minErrDist; + public CssToken LastRecognizedToken { get; set; } + public CssToken LookaheadToken { get; set; } + private int errDist = minErrDist; - public CssDocument CssDoc; + public CssDocument CssDoc { get; set; } - bool IsInHex(string value) + private bool IsInHex(string value) { if (value.Length == 7) { return false; } - if (value.Length + m_lookaheadToken.m_tokenValue.Length > 7) + if (value.Length + LookaheadToken.TokenValue.Length > 7) { return false; } @@ -2049,7 +1531,7 @@ bool IsInHex(string value) { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "a", "b", "c", "d", "e", "f" }; - foreach (char c in m_lookaheadToken.m_tokenValue) + foreach (var c in LookaheadToken.TokenValue) { if (!hexes.Contains(c.ToString())) { @@ -2059,131 +1541,89 @@ bool IsInHex(string value) return true; } - bool IsUnitOfLength() + private bool IsUnitOfLength() { - if (m_lookaheadToken.m_tokenKind != 1) + if (LookaheadToken.TokenKind != 1) { return false; } - System.Collections.Generic.List units = new System.Collections.Generic.List( + var units = new List( new string[] { "em", "ex", "px", "gd", "rem", "vw", "vh", "vm", "ch", "mm", "cm", "in", "pt", "pc", "deg", "grad", "rad", "turn", "ms", "s", "hz", "khz" }); - return units.Contains(m_lookaheadToken.m_tokenValue.ToLower()); - } - - bool IsNumber() - { - if (m_lookaheadToken.m_tokenValue.Length > 0) - { - return char.IsDigit(m_lookaheadToken.m_tokenValue[0]); - } - return false; + return units.Contains(LookaheadToken.TokenValue.ToLower()); } public Parser(Scanner scanner) { - this.m_scanner = scanner; - m_errors = new Errors(); + Scanner = scanner; + Errors = new Errors(); } - void SyntaxErr(int n) + private void SyntaxErr(int n) { - if (errDist >= minErrDist) - m_errors.SyntaxError(m_lookaheadToken.m_tokenLine, m_lookaheadToken.m_tokenColumn, n); + if (errDist >= minErrDist) + { + Errors.SyntaxError(LookaheadToken.TokenLine, LookaheadToken.TokenColumn, n); + } + errDist = 0; } public void SemanticErr(string msg) { if (errDist >= minErrDist) - m_errors.SemanticError(m_lastRecognizedToken.m_tokenLine, m_lastRecognizedToken.m_tokenColumn, msg); + { + Errors.SemanticError(LastRecognizedToken.TokenLine, LastRecognizedToken.TokenColumn, msg); + } + errDist = 0; } - void Get() + private void Get() { - for (;;) - { - m_lastRecognizedToken = m_lookaheadToken; - m_lookaheadToken = m_scanner.Scan(); - if (m_lookaheadToken.m_tokenKind <= c_maxT) - { - ++errDist; - break; + for (; ; ) + { + LastRecognizedToken = LookaheadToken; + LookaheadToken = Scanner.Scan(); + if (LookaheadToken.TokenKind <= c_maxT) + { + ++errDist; + break; } - m_lookaheadToken = m_lastRecognizedToken; + LookaheadToken = LastRecognizedToken; } } - void Expect(int n) + private void Expect(int n) { - if (m_lookaheadToken.m_tokenKind == n) - Get(); - else + if (LookaheadToken.TokenKind == n) { - SyntaxErr(n); - } - } - - bool StartOf(int s) - { - return set[s, m_lookaheadToken.m_tokenKind]; - } - - void ExpectWeak(int n, int follow) - { - if (m_lookaheadToken.m_tokenKind == n) Get(); + } else { SyntaxErr(n); - while (!StartOf(follow)) - Get(); } } - - bool WeakSeparator(int n, int syFol, int repFol) + private bool StartOf(int s) { - int kind = m_lookaheadToken.m_tokenKind; - if (kind == n) - { - Get(); - return true; - } - else if (StartOf(repFol)) - { - return false; - } - else - { - SyntaxErr(n); - while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) - { - Get(); - kind = m_lookaheadToken.m_tokenKind; - } - return StartOf(syFol); - } + return set[s, LookaheadToken.TokenKind]; } - - void Css3() + private void Css3() { CssDoc = new CssDocument(); - CssRuleSet rset = null; - CssDirective dir = null; - - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - while (m_lookaheadToken.m_tokenKind == 5 || m_lookaheadToken.m_tokenKind == 6) + while (LookaheadToken.TokenKind == 5 || LookaheadToken.TokenKind == 6) { - if (m_lookaheadToken.m_tokenKind == 5) + if (LookaheadToken.TokenKind == 5) { Get(); } @@ -2196,17 +1636,17 @@ void Css3() { if (StartOf(2)) { - RuleSet(out rset); + RuleSet(out var rset); CssDoc.RuleSets.Add(rset); } else { - Directive(out dir); + Directive(out var dir); CssDoc.Directives.Add(dir); } - while (m_lookaheadToken.m_tokenKind == 5 || m_lookaheadToken.m_tokenKind == 6) + while (LookaheadToken.TokenKind == 5 || LookaheadToken.TokenKind == 6) { - if (m_lookaheadToken.m_tokenKind == 5) + if (LookaheadToken.TokenKind == 5) { Get(); } @@ -2215,133 +1655,108 @@ void Css3() Get(); } } - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } } - void RuleSet(out CssRuleSet rset) + private void RuleSet(out CssRuleSet rset) { rset = new CssRuleSet(); - CssSelector sel = null; - CssDeclaration dec = null; - - Selector(out sel); + Selector(out var sel); rset.Selectors.Add(sel); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - while (m_lookaheadToken.m_tokenKind == 25) + while (LookaheadToken.TokenKind == 25) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } Selector(out sel); rset.Selectors.Add(sel); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } Expect(26); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } if (StartOf(3)) { - Declaration(out dec); + Declaration(out var dec); rset.Declarations.Add(dec); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - while (m_lookaheadToken.m_tokenKind == 27) + while (LookaheadToken.TokenKind == 27) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - if (m_lookaheadToken.m_tokenValue.Equals("}")) - { - Get(); - return; + if (LookaheadToken.TokenValue.Equals("}")) + { + Get(); + return; } Declaration(out dec); rset.Declarations.Add(dec); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } - if (m_lookaheadToken.m_tokenKind == 27) + if (LookaheadToken.TokenKind == 27) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } } Expect(28); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } - void Directive(out CssDirective dir) + private void Directive(out CssDirective dir) { dir = new CssDirective(); - CssDeclaration dec = null; - CssRuleSet rset = null; - CssExpression exp = null; - CssDirective dr = null; - string ident = null; - CssMedium m; - Expect(23); - dir.Name = "@"; - if (m_lookaheadToken.m_tokenKind == 24) - { - Get(); - dir.Name += "-"; - } - Identity(out ident); - dir.Name += ident; - switch (dir.Name.ToLower()) - { - case "@media": - dir.Type = CssDirectiveType.Media; - break; - case "@import": - dir.Type = CssDirectiveType.Import; - break; - case "@charset": - dir.Type = CssDirectiveType.Charset; - break; - case "@page": - dir.Type = CssDirectiveType.Page; - break; - case "@font-face": - dir.Type = CssDirectiveType.FontFace; - break; - case "@namespace": - dir.Type = CssDirectiveType.Namespace; - break; - default: - dir.Type = CssDirectiveType.Other; - break; + dir.Name = "@"; + if (LookaheadToken.TokenKind == 24) + { + Get(); + dir.Name += "-"; } - - while (m_lookaheadToken.m_tokenKind == 4) + Identity(out var ident); + dir.Name += ident; + dir.Type = dir.Name.ToLower() switch + { + "@media" => CssDirectiveType.Media, + "@import" => CssDirectiveType.Import, + "@charset" => CssDirectiveType.Charset, + "@page" => CssDirectiveType.Page, + "@font-face" => CssDirectiveType.FontFace, + "@namespace" => CssDirectiveType.Namespace, + _ => CssDirectiveType.Other, + }; + while (LookaheadToken.TokenKind == 4) { Get(); } @@ -2349,22 +1764,22 @@ void Directive(out CssDirective dir) { if (StartOf(5)) { - Medium(out m); + Medium(out var m); dir.Mediums.Add(m); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - while (m_lookaheadToken.m_tokenKind == 25) + while (LookaheadToken.TokenKind == 25) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } Medium(out m); dir.Mediums.Add(m); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } @@ -2372,18 +1787,18 @@ void Directive(out CssDirective dir) } else { - Exprsn(out exp); + Exprsn(out var exp); dir.Expression = exp; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } } - if (m_lookaheadToken.m_tokenKind == 26) + if (LookaheadToken.TokenKind == 26) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } @@ -2393,35 +1808,35 @@ void Directive(out CssDirective dir) { if (dir.Type == CssDirectiveType.Page || dir.Type == CssDirectiveType.FontFace) { - Declaration(out dec); + Declaration(out var dec); dir.Declarations.Add(dec); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - while (m_lookaheadToken.m_tokenKind == 27) + while (LookaheadToken.TokenKind == 27) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - if (m_lookaheadToken.m_tokenValue.Equals("}")) - { - Get(); - return; + if (LookaheadToken.TokenValue.Equals("}")) + { + Get(); + return; } Declaration(out dec); dir.Declarations.Add(dec); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } - if (m_lookaheadToken.m_tokenKind == 27) + if (LookaheadToken.TokenKind == 27) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } @@ -2429,18 +1844,18 @@ void Directive(out CssDirective dir) } else if (StartOf(2)) { - RuleSet(out rset); + RuleSet(out var rset); dir.RuleSets.Add(rset); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } else { - Directive(out dr); + Directive(out var dr); dir.Directives.Add(dr); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } @@ -2448,74 +1863,79 @@ void Directive(out CssDirective dir) } } Expect(28); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } - else if (m_lookaheadToken.m_tokenKind == 27) + else if (LookaheadToken.TokenKind == 27) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } - else SyntaxErr(50); + else + { + SyntaxErr(50); + } } - void QuotedString(out string qs) + private void QuotedString(out string qs) { qs = ""; - if (m_lookaheadToken.m_tokenKind == 7) + if (LookaheadToken.TokenKind == 7) { Get(); while (StartOf(7)) { Get(); - qs += m_lastRecognizedToken.m_tokenValue; - if (m_lookaheadToken.m_tokenValue.Equals("'") && !m_lastRecognizedToken.m_tokenValue.Equals("\\")) - { - break; + qs += LastRecognizedToken.TokenValue; + if (LookaheadToken.TokenValue.Equals("'") && !LastRecognizedToken.TokenValue.Equals("\\")) + { + break; } } Expect(7); } - else if (m_lookaheadToken.m_tokenKind == 8) + else if (LookaheadToken.TokenKind == 8) { Get(); while (StartOf(8)) { Get(); - qs += m_lastRecognizedToken.m_tokenValue; - if (m_lookaheadToken.m_tokenValue.Equals("\"") && !m_lastRecognizedToken.m_tokenValue.Equals("\\")) - { - break; + qs += LastRecognizedToken.TokenValue; + if (LookaheadToken.TokenValue.Equals("\"") && !LastRecognizedToken.TokenValue.Equals("\\")) + { + break; } } Expect(8); } - else SyntaxErr(51); - + else + { + SyntaxErr(51); + } } - void URI(out string url) + private void URI(out string url) { url = ""; Expect(9); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - if (m_lookaheadToken.m_tokenKind == 10) + if (LookaheadToken.TokenKind == 10) { Get(); } - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - if (m_lookaheadToken.m_tokenKind == 7 || m_lookaheadToken.m_tokenKind == 8) + if (LookaheadToken.TokenKind == 7 || LookaheadToken.TokenKind == 8) { QuotedString(out url); } @@ -2524,28 +1944,32 @@ void URI(out string url) while (StartOf(10)) { Get(); - url += m_lastRecognizedToken.m_tokenValue; - if (m_lookaheadToken.m_tokenValue.Equals(")")) - { - break; + url += LastRecognizedToken.TokenValue; + if (LookaheadToken.TokenValue.Equals(")")) + { + break; } } } - else SyntaxErr(52); - while (m_lookaheadToken.m_tokenKind == 4) + else + { + SyntaxErr(52); + } + + while (LookaheadToken.TokenKind == 4) { Get(); } - if (m_lookaheadToken.m_tokenKind == 11) + if (LookaheadToken.TokenKind == 11) { Get(); } } - void Medium(out CssMedium m) + private void Medium(out CssMedium m) { m = CssMedium.all; - switch (m_lookaheadToken.m_tokenKind) + switch (LookaheadToken.TokenKind) { case 12: { @@ -2611,10 +2035,10 @@ void Medium(out CssMedium m) } } - void Identity(out string ident) + private void Identity(out string ident) { ident = ""; - switch (m_lookaheadToken.m_tokenKind) + switch (LookaheadToken.TokenKind) { case 1: { @@ -2683,26 +2107,25 @@ void Identity(out string ident) } default: SyntaxErr(54); break; } - ident += m_lastRecognizedToken.m_tokenValue; + ident += LastRecognizedToken.TokenValue; } - void Exprsn(out CssExpression exp) + private void Exprsn(out CssExpression exp) { exp = new CssExpression(); char? sep = null; - CssTerm trm = null; - Term(out trm); + Term(out var trm); exp.Terms.Add(trm); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } while (StartOf(11)) { - if (m_lookaheadToken.m_tokenKind == 25 || m_lookaheadToken.m_tokenKind == 46) + if (LookaheadToken.TokenKind == 25 || LookaheadToken.TokenKind == 46) { - if (m_lookaheadToken.m_tokenKind == 46) + if (LookaheadToken.TokenKind == 46) { Get(); sep = '/'; @@ -2712,92 +2135,89 @@ void Exprsn(out CssExpression exp) Get(); sep = ','; } - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } Term(out trm); - if (sep.HasValue) - { - trm.Separator = sep.Value; + if (sep.HasValue) + { + trm.Separator = sep.Value; } exp.Terms.Add(trm); sep = null; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } } - void Declaration(out CssDeclaration dec) + private void Declaration(out CssDeclaration dec) { dec = new CssDeclaration(); - CssExpression exp = null; - string ident = ""; - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); dec.Name += "-"; } - Identity(out ident); + Identity(out var ident); dec.Name += ident; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } Expect(43); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - Exprsn(out exp); + Exprsn(out var exp); dec.Expression = exp; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - if (m_lookaheadToken.m_tokenKind == 44) + if (LookaheadToken.TokenKind == 44) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } Expect(45); dec.Important = true; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } } - void Selector(out CssSelector sel) + private void Selector(out CssSelector sel) { sel = new CssSelector(); - CssSimpleSelector ss = null; CssCombinator? cb = null; - SimpleSelector(out ss); + SimpleSelector(out var ss); sel.SimpleSelectors.Add(ss); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } while (StartOf(12)) { - if (m_lookaheadToken.m_tokenKind == 29 || m_lookaheadToken.m_tokenKind == 30 || m_lookaheadToken.m_tokenKind == 31) + if (LookaheadToken.TokenKind == 29 || LookaheadToken.TokenKind == 30 || LookaheadToken.TokenKind == 31) { - if (m_lookaheadToken.m_tokenKind == 29) + if (LookaheadToken.TokenKind == 29) { Get(); cb = CssCombinator.PrecededImmediatelyBy; } - else if (m_lookaheadToken.m_tokenKind == 30) + else if (LookaheadToken.TokenKind == 30) { Get(); cb = CssCombinator.ChildOf; @@ -2808,37 +2228,38 @@ void Selector(out CssSelector sel) cb = CssCombinator.PrecededBy; } } - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } SimpleSelector(out ss); - if (cb.HasValue) - { - ss.Combinator = cb.Value; + if (cb.HasValue) + { + ss.Combinator = cb.Value; } sel.SimpleSelectors.Add(ss); cb = null; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } } } - void SimpleSelector(out CssSimpleSelector ss) + private void SimpleSelector(out CssSimpleSelector ss) { - ss = new CssSimpleSelector(); - ss.ElementName = ""; - string psd = null; - OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute atb = null; - CssSimpleSelector parent = ss; - string ident = null; - + ss = new CssSimpleSelector + { + ElementName = "" + }; + var parent = ss; + string psd; + CssAttribute atb; + string ident; if (StartOf(3)) { - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); ss.ElementName += "-"; @@ -2846,36 +2267,36 @@ void SimpleSelector(out CssSimpleSelector ss) Identity(out ident); ss.ElementName += ident; } - else if (m_lookaheadToken.m_tokenKind == 32) + else if (LookaheadToken.TokenKind == 32) { Get(); ss.ElementName = "*"; } else if (StartOf(13)) { - if (m_lookaheadToken.m_tokenKind == 33) + if (LookaheadToken.TokenKind == 33) { Get(); - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); ss.ID = "-"; } Identity(out ident); - if (ss.ID == null) - { - ss.ID = ident; - } - else - { - ss.ID += ident; + if (ss.ID == null) + { + ss.ID = ident; + } + else + { + ss.ID += ident; } } - else if (m_lookaheadToken.m_tokenKind == 34) + else if (LookaheadToken.TokenKind == 34) { Get(); ss.Class = ""; - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); ss.Class += "-"; @@ -2883,7 +2304,7 @@ void SimpleSelector(out CssSimpleSelector ss) Identity(out ident); ss.Class += ident; } - else if (m_lookaheadToken.m_tokenKind == 35) + else if (LookaheadToken.TokenKind == 35) { Attrib(out atb); ss.Attribute = atb; @@ -2894,14 +2315,18 @@ void SimpleSelector(out CssSimpleSelector ss) ss.Pseudo = psd; } } - else SyntaxErr(55); + else + { + SyntaxErr(55); + } + while (StartOf(13)) { - CssSimpleSelector child = new CssSimpleSelector(); - if (m_lookaheadToken.m_tokenKind == 33) + var child = new CssSimpleSelector(); + if (LookaheadToken.TokenKind == 33) { Get(); - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); child.ID = "-"; @@ -2916,11 +2341,11 @@ void SimpleSelector(out CssSimpleSelector ss) child.ID += "-"; } } - else if (m_lookaheadToken.m_tokenKind == 34) + else if (LookaheadToken.TokenKind == 34) { Get(); child.Class = ""; - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); child.Class += "-"; @@ -2928,7 +2353,7 @@ void SimpleSelector(out CssSimpleSelector ss) Identity(out ident); child.Class += ident; } - else if (m_lookaheadToken.m_tokenKind == 35) + else if (LookaheadToken.TokenKind == 35) { Attrib(out atb); child.Attribute = atb; @@ -2940,31 +2365,29 @@ void SimpleSelector(out CssSimpleSelector ss) } parent.Child = child; parent = child; - } } - void Attrib(out OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute atb) + private void Attrib(out CssAttribute atb) { - atb = new OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute(); - atb.Value = ""; - string quote = null; - string ident = null; - + atb = new CssAttribute + { + Value = "" + }; Expect(35); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - Identity(out ident); + Identity(out var ident); atb.Operand = ident; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } if (StartOf(14)) { - switch (m_lookaheadToken.m_tokenKind) + switch (LookaheadToken.TokenKind) { case 36: { @@ -3003,13 +2426,13 @@ void Attrib(out OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute atb) break; } } - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } if (StartOf(3)) { - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); atb.Value += "-"; @@ -3017,13 +2440,17 @@ void Attrib(out OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute atb) Identity(out ident); atb.Value += ident; } - else if (m_lookaheadToken.m_tokenKind == 7 || m_lookaheadToken.m_tokenKind == 8) + else if (LookaheadToken.TokenKind == 7 || LookaheadToken.TokenKind == 8) { - QuotedString(out quote); + QuotedString(out var quote); atb.Value = quote; } - else SyntaxErr(56); - while (m_lookaheadToken.m_tokenKind == 4) + else + { + SyntaxErr(56); + } + + while (LookaheadToken.TokenKind == 4) { Get(); } @@ -3031,73 +2458,68 @@ void Attrib(out OpenXmlPowerTools.HtmlToWml.CSS.CssAttribute atb) Expect(42); } - void Pseudo(out string pseudo) + private void Pseudo(out string pseudo) { pseudo = ""; - CssExpression exp = null; - string ident = null; - Expect(43); - if (m_lookaheadToken.m_tokenKind == 43) + if (LookaheadToken.TokenKind == 43) { Get(); } - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); pseudo += "-"; } - Identity(out ident); + Identity(out var ident); pseudo += ident; - if (m_lookaheadToken.m_tokenKind == 10) + if (LookaheadToken.TokenKind == 10) { Get(); - pseudo += m_lastRecognizedToken.m_tokenValue; - while (m_lookaheadToken.m_tokenKind == 4) + pseudo += LastRecognizedToken.TokenValue; + while (LookaheadToken.TokenKind == 4) { Get(); } - Exprsn(out exp); + Exprsn(out var exp); pseudo += exp.ToString(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } Expect(11); - pseudo += m_lastRecognizedToken.m_tokenValue; + pseudo += LastRecognizedToken.TokenValue; } } - void Term(out CssTerm trm) + private void Term(out CssTerm trm) { trm = new CssTerm(); - string val = ""; - CssExpression exp = null; - string ident = null; - - if (m_lookaheadToken.m_tokenKind == 7 || m_lookaheadToken.m_tokenKind == 8) + var val = ""; + string ident; + if (LookaheadToken.TokenKind == 7 || LookaheadToken.TokenKind == 8) { QuotedString(out val); trm.Value = val; trm.Type = CssTermType.String; } - else if (m_lookaheadToken.m_tokenKind == 9) + else if (LookaheadToken.TokenKind == 9) { URI(out val); trm.Value = val; trm.Type = CssTermType.Url; } - else if (m_lookaheadToken.m_tokenKind == 47) + else if (LookaheadToken.TokenKind == 47) { Get(); Identity(out ident); trm.Value = "U\\" + ident; trm.Type = CssTermType.Unicode; } - else if (m_lookaheadToken.m_tokenKind == 33) + else if (LookaheadToken.TokenKind == 33) { HexValue(out val); trm.Value = val; @@ -3105,8 +2527,8 @@ void Term(out CssTerm trm) } else if (StartOf(15)) { - bool minus = false; - if (m_lookaheadToken.m_tokenKind == 24) + var minus = false; + if (LookaheadToken.TokenKind == 24) { Get(); minus = true; @@ -3122,60 +2544,63 @@ void Term(out CssTerm trm) } if (StartOf(17)) { - while (m_lookaheadToken.m_tokenKind == 34 || m_lookaheadToken.m_tokenKind == 36 || m_lookaheadToken.m_tokenKind == 43) + while (LookaheadToken.TokenKind == 34 || LookaheadToken.TokenKind == 36 || LookaheadToken.TokenKind == 43) { - if (m_lookaheadToken.m_tokenKind == 43) + if (LookaheadToken.TokenKind == 43) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; if (StartOf(18)) { - if (m_lookaheadToken.m_tokenKind == 43) + if (LookaheadToken.TokenKind == 43) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; } - if (m_lookaheadToken.m_tokenKind == 24) + if (LookaheadToken.TokenKind == 24) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; } Identity(out ident); trm.Value += ident; } - else if (m_lookaheadToken.m_tokenKind == 33) + else if (LookaheadToken.TokenKind == 33) { HexValue(out val); trm.Value += val; } else if (StartOf(19)) { - while (m_lookaheadToken.m_tokenKind == 3) + while (LookaheadToken.TokenKind == 3) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; } - if (m_lookaheadToken.m_tokenKind == 34) + if (LookaheadToken.TokenKind == 34) { Get(); trm.Value += "."; - while (m_lookaheadToken.m_tokenKind == 3) + while (LookaheadToken.TokenKind == 3) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; } } } - else SyntaxErr(57); + else + { + SyntaxErr(57); + } } - else if (m_lookaheadToken.m_tokenKind == 34) + else if (LookaheadToken.TokenKind == 34) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; - if (m_lookaheadToken.m_tokenKind == 24) + trm.Value += LastRecognizedToken.TokenValue; + if (LookaheadToken.TokenKind == 24) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; } Identity(out ident); trm.Value += ident; @@ -3183,11 +2608,11 @@ void Term(out CssTerm trm) else { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; - if (m_lookaheadToken.m_tokenKind == 24) + trm.Value += LastRecognizedToken.TokenValue; + if (LookaheadToken.TokenKind == 24) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; } if (StartOf(16)) { @@ -3196,32 +2621,37 @@ void Term(out CssTerm trm) } else if (StartOf(19)) { - while (m_lookaheadToken.m_tokenKind == 3) + while (LookaheadToken.TokenKind == 3) { Get(); - trm.Value += m_lastRecognizedToken.m_tokenValue; + trm.Value += LastRecognizedToken.TokenValue; } } - else SyntaxErr(58); + else + { + SyntaxErr(58); + } } } } - if (m_lookaheadToken.m_tokenKind == 10) + if (LookaheadToken.TokenKind == 10) { Get(); - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } - Exprsn(out exp); - CssFunction func = new CssFunction(); - func.Name = trm.Value; - func.Expression = exp; + Exprsn(out var exp); + var func = new CssFunction + { + Name = trm.Value, + Expression = exp + }; trm.Value = null; trm.Function = func; trm.Type = CssTermType.Function; - while (m_lookaheadToken.m_tokenKind == 4) + while (LookaheadToken.TokenKind == 4) { Get(); } @@ -3230,55 +2660,48 @@ void Term(out CssTerm trm) } else if (StartOf(15)) { - if (m_lookaheadToken.m_tokenKind == 29) + if (LookaheadToken.TokenKind == 29) { Get(); trm.Sign = '+'; } if (minus) { trm.Sign = '-'; } - while (m_lookaheadToken.m_tokenKind == 3) + while (LookaheadToken.TokenKind == 3) { Get(); - val += m_lastRecognizedToken.m_tokenValue; + val += LastRecognizedToken.TokenValue; } - if (m_lookaheadToken.m_tokenKind == 34) + if (LookaheadToken.TokenKind == 34) { Get(); - val += m_lastRecognizedToken.m_tokenValue; - while (m_lookaheadToken.m_tokenKind == 3) + val += LastRecognizedToken.TokenValue; + while (LookaheadToken.TokenKind == 3) { Get(); - val += m_lastRecognizedToken.m_tokenValue; + val += LastRecognizedToken.TokenValue; } } if (StartOf(20)) { - if (m_lookaheadToken.m_tokenValue.ToLower().Equals("n")) + if (LookaheadToken.TokenValue.ToLower().Equals("n")) { Expect(22); - val += m_lastRecognizedToken.m_tokenValue; - if (m_lookaheadToken.m_tokenKind == 24 || m_lookaheadToken.m_tokenKind == 29) + val += LastRecognizedToken.TokenValue; + if (LookaheadToken.TokenKind == 24 || LookaheadToken.TokenKind == 29) { - if (m_lookaheadToken.m_tokenKind == 29) - { - Get(); - val += m_lastRecognizedToken.m_tokenValue; - } - else - { - Get(); - val += m_lastRecognizedToken.m_tokenValue; - } + Get(); + val += LastRecognizedToken.TokenValue; + Expect(3); - val += m_lastRecognizedToken.m_tokenValue; - while (m_lookaheadToken.m_tokenKind == 3) + val += LastRecognizedToken.TokenValue; + while (LookaheadToken.TokenKind == 3) { Get(); - val += m_lastRecognizedToken.m_tokenValue; + val += LastRecognizedToken.TokenValue; } } } - else if (m_lookaheadToken.m_tokenKind == 48) + else if (LookaheadToken.TokenKind == 48) { Get(); trm.Unit = CssUnit.Percent; @@ -3294,227 +2717,172 @@ void Term(out CssTerm trm) } catch { - m_errors.SemanticError(m_lastRecognizedToken.m_tokenLine, m_lastRecognizedToken.m_tokenColumn, string.Format("Unrecognized unit '{0}'", ident)); + Errors.SemanticError(LastRecognizedToken.TokenLine, LastRecognizedToken.TokenColumn, string.Format("Unrecognized unit '{0}'", ident)); } - } } } trm.Value = val; trm.Type = CssTermType.Number; } - else SyntaxErr(59); + else + { + SyntaxErr(59); + } + } + else + { + SyntaxErr(60); } - else SyntaxErr(60); } - void HexValue(out string val) + private void HexValue(out string val) { val = ""; - bool found = false; + var found = false; Expect(33); - val += m_lastRecognizedToken.m_tokenValue; + val += LastRecognizedToken.TokenValue; if (StartOf(19)) { - while (m_lookaheadToken.m_tokenKind == 3) + while (LookaheadToken.TokenKind == 3) { Get(); - val += m_lastRecognizedToken.m_tokenValue; + val += LastRecognizedToken.TokenValue; } } else if (IsInHex(val)) { Expect(1); - val += m_lastRecognizedToken.m_tokenValue; found = true; + val += LastRecognizedToken.TokenValue; found = true; } - else SyntaxErr(61); + else + { + SyntaxErr(61); + } + if (!found && IsInHex(val)) { Expect(1); - val += m_lastRecognizedToken.m_tokenValue; + val += LastRecognizedToken.TokenValue; } } public void Parse() { - m_lookaheadToken = new CssToken(); - m_lookaheadToken.m_tokenValue = ""; + LookaheadToken = new CssToken + { + TokenValue = "" + }; Get(); Css3(); Expect(0); } - static readonly bool[,] set = { - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,T, T,x,x,x, x,x,x,x, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,x,x,x, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,x,T, T,x,x,T, T,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,T,T,T, x,T,x,x, x,T,T,x, x,x,x,x, x,x,x,x, x,x,T,T, T,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,T, T,x,x,x, T,x,x,x, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, - {x,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, - {x,T,T,T, T,T,T,T, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, - {x,T,T,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, - {x,T,T,T, x,T,T,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, - {x,T,x,T, T,x,x,T, T,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,T,x,x, x,T,x,x, x,T,T,x, x,x,x,x, x,x,x,x, x,x,T,T, T,x,x}, - {x,T,x,x, T,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,T,T,T, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x}, - {x,T,x,T, T,x,x,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, T,T,T,T, x,x,x,x, x,x,x,T, T,x,T,T, T,x,x}, - {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, - {x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, T,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, - {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, - {x,T,x,T, T,x,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, T,T,T,T, T,x,x,x, x,x,x,T, T,x,T,T, T,x,x}, - {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x} - }; + private static readonly bool[,] set = { + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,T, T,x,x,x, x,x,x,x, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, + {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,x,x,x, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, + {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,T,x,T, T,x,x,T, T,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,T,T,T, x,T,x,x, x,T,T,x, x,x,x,x, x,x,x,x, x,x,T,T, T,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,T, T,x,x,x, T,x,x,x, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, + {x,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, + {x,T,T,T, T,T,T,T, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, + {x,T,T,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, + {x,T,T,T, x,T,T,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x}, + {x,T,x,T, T,x,x,T, T,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,T,x,x, x,T,x,x, x,T,T,x, x,x,x,x, x,x,x,x, x,x,T,T, T,x,x}, + {x,T,x,x, T,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,T,T,T, T,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x}, + {x,T,x,T, T,x,x,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, T,T,T,T, x,x,x,x, x,x,x,T, T,x,T,T, T,x,x}, + {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,x,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, T,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, + {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x}, + {x,T,x,T, T,x,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, T,T,T,T, T,x,x,x, x,x,x,T, T,x,T,T, T,x,x}, + {x,T,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x} + }; } - public class Errors { - public int numberOfErrorsDetected = 0; - public string errMsgFormat = "CssParser error: line {0} col {1}: {2}"; + public int NumberOfErrorsDetected { get; set; } = 0; + public string ErrMsgFormat { get; set; } = "CssParser error: line {0} col {1}: {2}"; public virtual void SyntaxError(int line, int col, int n) { - string s; - switch (n) - { - case 0: s = "EOF expected"; - break; - case 1: s = "identifier expected"; - break; - case 2: s = "newline expected"; - break; - case 3: s = "digit expected"; - break; - case 4: s = "whitespace expected"; - break; - case 5: s = "\"\" expected"; - break; - case 7: s = "\"\'\" expected"; - break; - case 8: s = "\"\"\" expected"; - break; - case 9: s = "\"url\" expected"; - break; - case 10: s = "\"(\" expected"; - break; - case 11: s = "\")\" expected"; - break; - case 12: s = "\"all\" expected"; - break; - case 13: s = "\"aural\" expected"; - break; - case 14: s = "\"braille\" expected"; - break; - case 15: s = "\"embossed\" expected"; - break; - case 16: s = "\"handheld\" expected"; - break; - case 17: s = "\"print\" expected"; - break; - case 18: s = "\"projection\" expected"; - break; - case 19: s = "\"screen\" expected"; - break; - case 20: s = "\"tty\" expected"; - break; - case 21: s = "\"tv\" expected"; - break; - case 22: s = "\"n\" expected"; - break; - case 23: s = "\"@\" expected"; - break; - case 24: s = "\"-\" expected"; - break; - case 25: s = "\",\" expected"; - break; - case 26: s = "\"{\" expected"; - break; - case 27: s = "\";\" expected"; - break; - case 28: s = "\"}\" expected"; - break; - case 29: s = "\"+\" expected"; - break; - case 30: s = "\">\" expected"; - break; - case 31: s = "\"~\" expected"; - break; - case 32: s = "\"*\" expected"; - break; - case 33: s = "\"#\" expected"; - break; - case 34: s = "\".\" expected"; - break; - case 35: s = "\"[\" expected"; - break; - case 36: s = "\"=\" expected"; - break; - case 37: s = "\"~=\" expected"; - break; - case 38: s = "\"|=\" expected"; - break; - case 39: s = "\"$=\" expected"; - break; - case 40: s = "\"^=\" expected"; - break; - case 41: s = "\"*=\" expected"; - break; - case 42: s = "\"]\" expected"; - break; - case 43: s = "\":\" expected"; - break; - case 44: s = "\"!\" expected"; - break; - case 45: s = "\"important\" expected"; - break; - case 46: s = "\"/\" expected"; - break; - case 47: s = "\"U\\\\\" expected"; - break; - case 48: s = "\"%\" expected"; - break; - case 49: s = "??? expected"; - break; - case 50: s = "invalid directive"; - break; - case 51: s = "invalid QuotedString"; - break; - case 52: s = "invalid URI"; - break; - case 53: s = "invalid medium"; - break; - case 54: s = "invalid identity"; - break; - case 55: s = "invalid simpleselector"; - break; - case 56: s = "invalid attrib"; - break; - case 57: s = "invalid term"; - break; - case 58: s = "invalid term"; - break; - case 59: s = "invalid term"; - break; - case 60: s = "invalid term"; - break; - case 61: s = "invalid HexValue"; - break; - - default: s = "error " + n; - break; - } - var errorString = string.Format(errMsgFormat, line, col, s); + var s = n switch + { + 0 => "EOF expected", + 1 => "identifier expected", + 2 => "newline expected", + 3 => "digit expected", + 4 => "whitespace expected", + 5 => "\"\" expected", + 7 => "\"\'\" expected", + 8 => "\"\"\" expected", + 9 => "\"url\" expected", + 10 => "\"(\" expected", + 11 => "\")\" expected", + 12 => "\"all\" expected", + 13 => "\"aural\" expected", + 14 => "\"braille\" expected", + 15 => "\"embossed\" expected", + 16 => "\"handheld\" expected", + 17 => "\"print\" expected", + 18 => "\"projection\" expected", + 19 => "\"screen\" expected", + 20 => "\"tty\" expected", + 21 => "\"tv\" expected", + 22 => "\"n\" expected", + 23 => "\"@\" expected", + 24 => "\"-\" expected", + 25 => "\",\" expected", + 26 => "\"{\" expected", + 27 => "\";\" expected", + 28 => "\"}\" expected", + 29 => "\"+\" expected", + 30 => "\">\" expected", + 31 => "\"~\" expected", + 32 => "\"*\" expected", + 33 => "\"#\" expected", + 34 => "\".\" expected", + 35 => "\"[\" expected", + 36 => "\"=\" expected", + 37 => "\"~=\" expected", + 38 => "\"|=\" expected", + 39 => "\"$=\" expected", + 40 => "\"^=\" expected", + 41 => "\"*=\" expected", + 42 => "\"]\" expected", + 43 => "\":\" expected", + 44 => "\"!\" expected", + 45 => "\"important\" expected", + 46 => "\"/\" expected", + 47 => "\"U\\\\\" expected", + 48 => "\"%\" expected", + 49 => "??? expected", + 50 => "invalid directive", + 51 => "invalid QuotedString", + 52 => "invalid URI", + 53 => "invalid medium", + 54 => "invalid identity", + 55 => "invalid simpleselector", + 56 => "invalid attrib", + 57 => "invalid term", + 58 => "invalid term", + 59 => "invalid term", + 60 => "invalid term", + 61 => "invalid HexValue", + _ => "error " + n, + }; + var errorString = string.Format(ErrMsgFormat, line, col, s); throw new OpenXmlPowerToolsException(errorString); } public virtual void SemanticError(int line, int col, string s) { - var errorString = string.Format(errMsgFormat, line, col, s); + var errorString = string.Format(ErrMsgFormat, line, col, s); throw new OpenXmlPowerToolsException(errorString); } @@ -3525,7 +2893,7 @@ public virtual void SemanticError(string s) public virtual void Warning(int line, int col, string s) { - var errorString = string.Format(errMsgFormat, line, col, s); + var errorString = string.Format(ErrMsgFormat, line, col, s); throw new OpenXmlPowerToolsException(errorString); } @@ -3535,58 +2903,59 @@ public virtual void Warning(string s) } } - - public class FatalError : Exception - { - public FatalError(string m) : base(m) { } - } - public class CssToken { - public int m_tokenKind; - public int m_tokenPositionInBytes; - public int m_tokenPositionInCharacters; - public int m_tokenColumn; - public int m_tokenLine; - public string m_tokenValue; - public CssToken m_nextToken; + public int TokenKind { get; set; } + public int TokenPositionInBytes { get; set; } + public int TokenPositionInCharacters { get; set; } + public int TokenColumn { get; set; } + public int TokenLine { get; set; } + public string TokenValue { get; set; } + public CssToken NextToken { get; set; } } public class CssBuffer { public const int EOF = char.MaxValue + 1; - const int MIN_BUFFER_LENGTH = 1024; - const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; - byte[] m_inputBuffer; - int m_bufferStart; - int m_bufferLength; - int m_inputStreamLength; - int m_currentPositionInBuffer; - Stream m_inputStream; - bool m_isUserStream; + private const int MIN_BUFFER_LENGTH = 1024; + private const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; + private byte[] m_inputBuffer; + private int m_bufferStart; + private int m_bufferLength; + private int m_inputStreamLength; + private int m_currentPositionInBuffer; + private Stream? m_inputStream; + private readonly bool m_isUserStream; public CssBuffer(Stream s, bool isUserStream) { - m_inputStream = s; this.m_isUserStream = isUserStream; + m_inputStream = s; m_isUserStream = isUserStream; if (m_inputStream.CanSeek) { m_inputStreamLength = (int)m_inputStream.Length; m_bufferLength = Math.Min(m_inputStreamLength, MAX_BUFFER_LENGTH); - m_bufferStart = Int32.MaxValue; + m_bufferStart = int.MaxValue; } else { m_inputStreamLength = m_bufferLength = m_bufferStart = 0; } - m_inputBuffer = new byte[(m_bufferLength > 0) ? m_bufferLength : MIN_BUFFER_LENGTH]; + m_inputBuffer = new byte[m_bufferLength > 0 ? m_bufferLength : MIN_BUFFER_LENGTH]; if (m_inputStreamLength > 0) + { Pos = 0; + } else + { m_currentPositionInBuffer = 0; + } + if (m_bufferLength == m_inputStreamLength && m_inputStream.CanSeek) + { Close(); + } } protected CssBuffer(CssBuffer b) @@ -3601,7 +2970,10 @@ protected CssBuffer(CssBuffer b) m_isUserStream = b.m_isUserStream; } - ~CssBuffer() { Close(); } + ~CssBuffer() + { + Close(); + } protected void Close() { @@ -3620,7 +2992,6 @@ public virtual int Read() } else if (Pos < m_inputStreamLength) { - Pos = Pos; return m_inputBuffer[m_currentPositionInBuffer++]; } else if (m_inputStream != null && !m_inputStream.CanSeek && ReadNextStreamChunk() > 0) @@ -3635,37 +3006,41 @@ public virtual int Read() public int Peek() { - int curPos = Pos; - int ch = Read(); + var curPos = Pos; + var ch = Read(); Pos = curPos; return ch; } public string GetString(int beg, int end) { - int len = 0; - char[] buf = new char[end - beg]; - int oldPos = Pos; + var len = 0; + var buf = new char[end - beg]; + var oldPos = Pos; Pos = beg; while (Pos < end) + { buf[len++] = (char)Read(); + } + Pos = oldPos; - return new String(buf, 0, len); + return new string(buf, 0, len); } public int Pos { - get { return m_currentPositionInBuffer + m_bufferStart; } + get => m_currentPositionInBuffer + m_bufferStart; set { if (value >= m_inputStreamLength && m_inputStream != null && !m_inputStream.CanSeek) { - while (value >= m_inputStreamLength && ReadNextStreamChunk() > 0) ; + while (value >= m_inputStreamLength && ReadNextStreamChunk() > 0) + { } } if (value < 0 || value > m_inputStreamLength) { - throw new FatalError("buffer out of bounds access, position: " + value); + throw new ArgumentException("buffer out of bounds access, position: " + value); } if (value >= m_bufferStart && value < m_bufferStart + m_bufferLength) @@ -3687,27 +3062,29 @@ public int Pos private int ReadNextStreamChunk() { - int free = m_inputBuffer.Length - m_bufferLength; + var free = m_inputBuffer.Length - m_bufferLength; if (free == 0) { - byte[] newBuf = new byte[m_bufferLength * 2]; + var newBuf = new byte[m_bufferLength * 2]; Array.Copy(m_inputBuffer, newBuf, m_bufferLength); m_inputBuffer = newBuf; free = m_bufferLength; } - int read = m_inputStream.Read(m_inputBuffer, m_bufferLength, free); + var read = m_inputStream.Read(m_inputBuffer, m_bufferLength, free); if (read > 0) { - m_inputStreamLength = m_bufferLength = (m_bufferLength + read); + m_inputStreamLength = m_bufferLength += read; return read; } return 0; } } - public class UTF8Buffer : CssBuffer + public class HtmlToWmlCssParser : CssBuffer { - public UTF8Buffer(CssBuffer b) : base(b) { } + public HtmlToWmlCssParser(CssBuffer b) : base(b) + { + } public override int Read() { @@ -3715,37 +3092,37 @@ public override int Read() do { ch = base.Read(); - } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EOF)); + } while (ch >= 128 && (ch & 0xC0) != 0xC0 && ch != EOF); if (ch < 128 || ch == EOF) { // nothing to do } else if ((ch & 0xF0) == 0xF0) { - int c1 = ch & 0x07; + var c1 = ch & 0x07; ch = base.Read(); - int c2 = ch & 0x3F; + var c2 = ch & 0x3F; ch = base.Read(); - int c3 = ch & 0x3F; + var c3 = ch & 0x3F; ch = base.Read(); - int c4 = ch & 0x3F; - ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4; + var c4 = ch & 0x3F; + ch = ((c1 << 6 | c2) << 6 | c3) << 6 | c4; } else if ((ch & 0xE0) == 0xE0) { - int c1 = ch & 0x0F; + var c1 = ch & 0x0F; ch = base.Read(); - int c2 = ch & 0x3F; + var c2 = ch & 0x3F; ch = base.Read(); - int c3 = ch & 0x3F; - ch = (((c1 << 6) | c2) << 6) | c3; + var c3 = ch & 0x3F; + ch = (c1 << 6 | c2) << 6 | c3; } else if ((ch & 0xC0) == 0xC0) { - int c1 = ch & 0x1F; + var c1 = ch & 0x1F; ch = base.Read(); - int c2 = ch & 0x3F; - ch = (c1 << 6) | c2; + var c2 = ch & 0x3F; + ch = c1 << 6 | c2; } return ch; } @@ -3753,52 +3130,82 @@ public override int Read() public class Scanner { - const char END_OF_LINE = '\n'; - const int c_eof = 0; - const int c_maxT = 49; - const int c_noSym = 49; - const int c_maxTokenLength = 128; + private const char END_OF_LINE = '\n'; + private const int c_eof = 0; + private const int c_maxT = 49; + private const int c_noSym = 49; + private const int c_maxTokenLength = 128; - public CssBuffer m_scannerBuffer; + public CssBuffer ScannerBuffer { get; set; } - CssToken m_currentToken; - int m_currentInputCharacter; - int m_currentCharacterBytePosition; - int m_unicodeCharacterPosition; - int m_columnNumberOfCurrentCharacter; - int m_lineNumberOfCurrentCharacter; - int m_eolInComment; - static readonly Hashtable s_start; + private CssToken m_currentToken; + private int m_currentInputCharacter; + private int m_currentCharacterBytePosition; + private int m_unicodeCharacterPosition; + private int m_columnNumberOfCurrentCharacter; + private int m_lineNumberOfCurrentCharacter; + private int m_eolInComment; + private static readonly Hashtable s_start; - CssToken m_tokensAlreadyPeeked; - CssToken m_currentPeekToken; + private CssToken m_tokensAlreadyPeeked; + private CssToken m_currentPeekToken; - char[] m_textOfCurrentToken = new char[c_maxTokenLength]; - int m_lengthOfCurrentToken; + private char[] m_textOfCurrentToken = new char[c_maxTokenLength]; + private int m_lengthOfCurrentToken; static Scanner() { s_start = new Hashtable(128); - for (int i = 65; i <= 84; ++i) + for (var i = 65; i <= 84; ++i) + { s_start[i] = 1; - for (int i = 86; i <= 90; ++i) + } + + for (var i = 86; i <= 90; ++i) + { s_start[i] = 1; - for (int i = 95; i <= 95; ++i) + } + + for (var i = 95; i <= 95; ++i) + { s_start[i] = 1; - for (int i = 97; i <= 122; ++i) + } + + for (var i = 97; i <= 122; ++i) + { s_start[i] = 1; - for (int i = 10; i <= 10; ++i) + } + + for (var i = 10; i <= 10; ++i) + { s_start[i] = 2; - for (int i = 13; i <= 13; ++i) + } + + for (var i = 13; i <= 13; ++i) + { s_start[i] = 2; - for (int i = 48; i <= 57; ++i) + } + + for (var i = 48; i <= 57; ++i) + { s_start[i] = 3; - for (int i = 9; i <= 9; ++i) + } + + for (var i = 9; i <= 9; ++i) + { s_start[i] = 4; - for (int i = 11; i <= 12; ++i) + } + + for (var i = 11; i <= 12; ++i) + { s_start[i] = 4; - for (int i = 32; i <= 32; ++i) + } + + for (var i = 32; i <= 32; ++i) + { s_start[i] = 4; + } + s_start[60] = 5; s_start[45] = 40; s_start[39] = 11; @@ -3828,30 +3235,22 @@ static Scanner() s_start[85] = 43; s_start[37] = 39; s_start[CssBuffer.EOF] = -1; - } public Scanner(string fileName) { - try - { - Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); - m_scannerBuffer = new CssBuffer(stream, false); - Init(); - } - catch (IOException) - { - throw new FatalError("Cannot open file " + fileName); - } + Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + ScannerBuffer = new CssBuffer(stream, false); + Init(); } public Scanner(Stream s) { - m_scannerBuffer = new CssBuffer(s, true); + ScannerBuffer = new CssBuffer(s, true); Init(); } - void Init() + private void Init() { m_currentCharacterBytePosition = -1; m_lineNumberOfCurrentCharacter = 1; @@ -3862,14 +3261,14 @@ void Init() if (m_currentInputCharacter == 0xEF) { NextCh(); - int ch1 = m_currentInputCharacter; + var ch1 = m_currentInputCharacter; NextCh(); - int ch2 = m_currentInputCharacter; + var ch2 = m_currentInputCharacter; if (ch1 != 0xBB || ch2 != 0xBF) { - throw new FatalError(String.Format("illegal byte order mark: EF {0,2:X} {1,2:X}", ch1, ch2)); + throw new NotSupportedException(string.Format("illegal byte order mark: EF {0,2:X} {1,2:X}", ch1, ch2)); } - m_scannerBuffer = new UTF8Buffer(m_scannerBuffer); + ScannerBuffer = new HtmlToWmlCssParser(ScannerBuffer); m_columnNumberOfCurrentCharacter = 0; m_unicodeCharacterPosition = -1; NextCh(); @@ -3877,7 +3276,7 @@ void Init() m_currentPeekToken = m_tokensAlreadyPeeked = new CssToken(); } - void NextCh() + private void NextCh() { if (m_eolInComment > 0) { @@ -3886,25 +3285,27 @@ void NextCh() } else { - m_currentCharacterBytePosition = m_scannerBuffer.Pos; - m_currentInputCharacter = m_scannerBuffer.Read(); + m_currentCharacterBytePosition = ScannerBuffer.Pos; + m_currentInputCharacter = ScannerBuffer.Read(); m_columnNumberOfCurrentCharacter++; m_unicodeCharacterPosition++; - if (m_currentInputCharacter == '\r' && m_scannerBuffer.Peek() != '\n') + if (m_currentInputCharacter == '\r' && ScannerBuffer.Peek() != '\n') + { m_currentInputCharacter = END_OF_LINE; + } + if (m_currentInputCharacter == END_OF_LINE) { m_lineNumberOfCurrentCharacter++; m_columnNumberOfCurrentCharacter = 0; } } - } - void AddCh() + private void AddCh() { if (m_lengthOfCurrentToken >= m_textOfCurrentToken.Length) { - char[] newBuf = new char[2 * m_textOfCurrentToken.Length]; + var newBuf = new char[2 * m_textOfCurrentToken.Length]; Array.Copy(m_textOfCurrentToken, 0, newBuf, 0, m_textOfCurrentToken.Length); m_textOfCurrentToken = newBuf; } @@ -3915,14 +3316,14 @@ void AddCh() } } - bool Comment0() + private bool Comment0() { int level = 1, pos0 = m_currentCharacterBytePosition, line0 = m_lineNumberOfCurrentCharacter, col0 = m_columnNumberOfCurrentCharacter, charPos0 = m_unicodeCharacterPosition; NextCh(); if (m_currentInputCharacter == '*') { NextCh(); - for (;;) + for (; ; ) { if (m_currentInputCharacter == '*') { @@ -3940,14 +3341,18 @@ bool Comment0() } } else if (m_currentInputCharacter == CssBuffer.EOF) + { return false; + } else + { NextCh(); + } } } else { - m_scannerBuffer.Pos = pos0; + ScannerBuffer.Pos = pos0; NextCh(); m_lineNumberOfCurrentCharacter = line0; m_columnNumberOfCurrentCharacter = col0; @@ -3956,127 +3361,148 @@ bool Comment0() return false; } - - void CheckLiteral() + private void CheckLiteral() { - switch (m_currentToken.m_tokenValue) + switch (m_currentToken.TokenValue) { case "url": - m_currentToken.m_tokenKind = 9; + m_currentToken.TokenKind = 9; break; + case "all": - m_currentToken.m_tokenKind = 12; + m_currentToken.TokenKind = 12; break; + case "aural": - m_currentToken.m_tokenKind = 13; + m_currentToken.TokenKind = 13; break; + case "braille": - m_currentToken.m_tokenKind = 14; + m_currentToken.TokenKind = 14; break; + case "embossed": - m_currentToken.m_tokenKind = 15; + m_currentToken.TokenKind = 15; break; + case "handheld": - m_currentToken.m_tokenKind = 16; + m_currentToken.TokenKind = 16; break; + case "print": - m_currentToken.m_tokenKind = 17; + m_currentToken.TokenKind = 17; break; + case "projection": - m_currentToken.m_tokenKind = 18; + m_currentToken.TokenKind = 18; break; + case "screen": - m_currentToken.m_tokenKind = 19; + m_currentToken.TokenKind = 19; break; + case "tty": - m_currentToken.m_tokenKind = 20; + m_currentToken.TokenKind = 20; break; + case "tv": - m_currentToken.m_tokenKind = 21; + m_currentToken.TokenKind = 21; break; + case "n": - m_currentToken.m_tokenKind = 22; + m_currentToken.TokenKind = 22; break; + case "important": - m_currentToken.m_tokenKind = 45; + m_currentToken.TokenKind = 45; break; + default: break; } } - CssToken NextToken() + private CssToken NextToken() { while (m_currentInputCharacter == ' ' || m_currentInputCharacter == 10 || m_currentInputCharacter == 13) + { NextCh(); + } + if (m_currentInputCharacter == '/' && Comment0()) + { return NextToken(); - int recKind = c_noSym; - int recEnd = m_currentCharacterBytePosition; - m_currentToken = new CssToken(); - m_currentToken.m_tokenPositionInBytes = m_currentCharacterBytePosition; - m_currentToken.m_tokenColumn = m_columnNumberOfCurrentCharacter; - m_currentToken.m_tokenLine = m_lineNumberOfCurrentCharacter; - m_currentToken.m_tokenPositionInCharacters = m_unicodeCharacterPosition; + } + + var recKind = c_noSym; + var recEnd = m_currentCharacterBytePosition; + m_currentToken = new CssToken + { + TokenPositionInBytes = m_currentCharacterBytePosition, + TokenColumn = m_columnNumberOfCurrentCharacter, + TokenLine = m_lineNumberOfCurrentCharacter, + TokenPositionInCharacters = m_unicodeCharacterPosition + }; int state; if (s_start.ContainsKey(m_currentInputCharacter)) { state = (int)s_start[m_currentInputCharacter]; } - else { + else + { state = 0; } m_lengthOfCurrentToken = 0; AddCh(); +#pragma warning disable S907 // "goto" statement should not be used, but switch case is a boarder case and not worth refactoring it switch (state) { - case -1: { - m_currentToken.m_tokenKind = c_eof; - break; - } + case -1: + { + m_currentToken.TokenKind = c_eof; + break; + } case 0: { if (recKind != c_noSym) { - m_lengthOfCurrentToken = recEnd - m_currentToken.m_tokenPositionInBytes; + m_lengthOfCurrentToken = recEnd - m_currentToken.TokenPositionInBytes; SetScannerBehindT(); } - m_currentToken.m_tokenKind = recKind; + m_currentToken.TokenKind = recKind; break; } case 1: - recEnd = m_currentCharacterBytePosition; recKind = 1; - if (m_currentInputCharacter == '-' || m_currentInputCharacter >= '0' && m_currentInputCharacter <= '9' || m_currentInputCharacter >= 'A' && m_currentInputCharacter <= 'Z' || m_currentInputCharacter == '_' || m_currentInputCharacter >= 'a' && m_currentInputCharacter <= 'z') + while (m_currentInputCharacter == '-' || m_currentInputCharacter >= '0' && m_currentInputCharacter <= '9' || m_currentInputCharacter >= 'A' && m_currentInputCharacter <= 'Z' || m_currentInputCharacter == '_' || m_currentInputCharacter >= 'a' && m_currentInputCharacter <= 'z') { AddCh(); - goto case 1; } - else { - m_currentToken.m_tokenKind = 1; m_currentToken.m_tokenValue = new String(m_textOfCurrentToken, 0, m_lengthOfCurrentToken); + m_currentToken.TokenKind = 1; m_currentToken.TokenValue = new string(m_textOfCurrentToken, 0, m_lengthOfCurrentToken); CheckLiteral(); return m_currentToken; } case 2: { - m_currentToken.m_tokenKind = 2; + m_currentToken.TokenKind = 2; break; } case 3: { - m_currentToken.m_tokenKind = 3; + m_currentToken.TokenKind = 3; break; } case 4: { - m_currentToken.m_tokenKind = 4; + m_currentToken.TokenKind = 4; break; } case 5: if (m_currentInputCharacter == '!') { AddCh(); + goto case 6; } else @@ -4089,7 +3515,8 @@ CssToken NextToken() AddCh(); goto case 7; } - else { + else + { goto case 0; } case 7: @@ -4104,7 +3531,7 @@ CssToken NextToken() } case 8: { - m_currentToken.m_tokenKind = 5; + m_currentToken.TokenKind = 5; break; } case 9: @@ -4119,87 +3546,87 @@ CssToken NextToken() } case 10: { - m_currentToken.m_tokenKind = 6; + m_currentToken.TokenKind = 6; break; } case 11: { - m_currentToken.m_tokenKind = 7; + m_currentToken.TokenKind = 7; break; } case 12: { - m_currentToken.m_tokenKind = 8; + m_currentToken.TokenKind = 8; break; } case 13: { - m_currentToken.m_tokenKind = 10; + m_currentToken.TokenKind = 10; break; } case 14: { - m_currentToken.m_tokenKind = 11; + m_currentToken.TokenKind = 11; break; } case 15: { - m_currentToken.m_tokenKind = 23; + m_currentToken.TokenKind = 23; break; } case 16: { - m_currentToken.m_tokenKind = 25; + m_currentToken.TokenKind = 25; break; } case 17: { - m_currentToken.m_tokenKind = 26; + m_currentToken.TokenKind = 26; break; } case 18: { - m_currentToken.m_tokenKind = 27; + m_currentToken.TokenKind = 27; break; } case 19: { - m_currentToken.m_tokenKind = 28; + m_currentToken.TokenKind = 28; break; } case 20: { - m_currentToken.m_tokenKind = 29; + m_currentToken.TokenKind = 29; break; } case 21: { - m_currentToken.m_tokenKind = 30; + m_currentToken.TokenKind = 30; break; } case 22: { - m_currentToken.m_tokenKind = 33; + m_currentToken.TokenKind = 33; break; } case 23: { - m_currentToken.m_tokenKind = 34; + m_currentToken.TokenKind = 34; break; } case 24: { - m_currentToken.m_tokenKind = 35; + m_currentToken.TokenKind = 35; break; } case 25: { - m_currentToken.m_tokenKind = 36; + m_currentToken.TokenKind = 36; break; } case 26: { - m_currentToken.m_tokenKind = 37; + m_currentToken.TokenKind = 37; break; } case 27: @@ -4208,12 +3635,13 @@ CssToken NextToken() AddCh(); goto case 28; } - else { + else + { goto case 0; } case 28: { - m_currentToken.m_tokenKind = 38; + m_currentToken.TokenKind = 38; break; } case 29: @@ -4222,12 +3650,13 @@ CssToken NextToken() AddCh(); goto case 30; } - else { + else + { goto case 0; } case 30: { - m_currentToken.m_tokenKind = 39; + m_currentToken.TokenKind = 39; break; } case 31: @@ -4242,42 +3671,42 @@ CssToken NextToken() } case 32: { - m_currentToken.m_tokenKind = 40; + m_currentToken.TokenKind = 40; break; } case 33: { - m_currentToken.m_tokenKind = 41; + m_currentToken.TokenKind = 41; break; } case 34: { - m_currentToken.m_tokenKind = 42; + m_currentToken.TokenKind = 42; break; } case 35: { - m_currentToken.m_tokenKind = 43; + m_currentToken.TokenKind = 43; break; } case 36: { - m_currentToken.m_tokenKind = 44; + m_currentToken.TokenKind = 44; break; } case 37: { - m_currentToken.m_tokenKind = 46; + m_currentToken.TokenKind = 46; break; } case 38: { - m_currentToken.m_tokenKind = 47; + m_currentToken.TokenKind = 47; break; } case 39: { - m_currentToken.m_tokenKind = 48; + m_currentToken.TokenKind = 48; break; } case 40: @@ -4290,12 +3719,10 @@ CssToken NextToken() } else { - m_currentToken.m_tokenKind = 24; + m_currentToken.TokenKind = 24; break; } case 41: - recEnd = m_currentCharacterBytePosition; - recKind = 31; if (m_currentInputCharacter == '=') { AddCh(); @@ -4303,12 +3730,10 @@ CssToken NextToken() } else { - m_currentToken.m_tokenKind = 31; + m_currentToken.TokenKind = 31; break; } case 42: - recEnd = m_currentCharacterBytePosition; - recKind = 32; if (m_currentInputCharacter == '=') { AddCh(); @@ -4316,11 +3741,10 @@ CssToken NextToken() } else { - m_currentToken.m_tokenKind = 32; + m_currentToken.TokenKind = 32; break; } case 43: - recEnd = m_currentCharacterBytePosition; recKind = 1; if (m_currentInputCharacter == '-' || m_currentInputCharacter >= '0' && m_currentInputCharacter <= '9' || m_currentInputCharacter >= 'A' && m_currentInputCharacter <= 'Z' || m_currentInputCharacter == '_' || m_currentInputCharacter >= 'a' && m_currentInputCharacter <= 'z') { AddCh(); @@ -4333,34 +3757,38 @@ CssToken NextToken() } else { - m_currentToken.m_tokenKind = 1; - m_currentToken.m_tokenValue = new String(m_textOfCurrentToken, 0, m_lengthOfCurrentToken); + m_currentToken.TokenKind = 1; + m_currentToken.TokenValue = new string(m_textOfCurrentToken, 0, m_lengthOfCurrentToken); CheckLiteral(); return m_currentToken; } - } - m_currentToken.m_tokenValue = new String(m_textOfCurrentToken, 0, m_lengthOfCurrentToken); +#pragma warning restore S907 // "goto" statement should not be used, but switch case is a boarder case and not worth refactoring it + + m_currentToken.TokenValue = new string(m_textOfCurrentToken, 0, m_lengthOfCurrentToken); return m_currentToken; } private void SetScannerBehindT() { - m_scannerBuffer.Pos = m_currentToken.m_tokenPositionInBytes; + ScannerBuffer.Pos = m_currentToken.TokenPositionInBytes; NextCh(); - m_lineNumberOfCurrentCharacter = m_currentToken.m_tokenLine; m_columnNumberOfCurrentCharacter = m_currentToken.m_tokenColumn; m_unicodeCharacterPosition = m_currentToken.m_tokenPositionInCharacters; - for (int i = 0; i < m_lengthOfCurrentToken; i++) NextCh(); + m_lineNumberOfCurrentCharacter = m_currentToken.TokenLine; m_columnNumberOfCurrentCharacter = m_currentToken.TokenColumn; m_unicodeCharacterPosition = m_currentToken.TokenPositionInCharacters; + for (var i = 0; i < m_lengthOfCurrentToken; i++) + { + NextCh(); + } } public CssToken Scan() { - if (m_tokensAlreadyPeeked.m_nextToken == null) + if (m_tokensAlreadyPeeked.NextToken == null) { return NextToken(); } else { - m_currentPeekToken = m_tokensAlreadyPeeked = m_tokensAlreadyPeeked.m_nextToken; + m_currentPeekToken = m_tokensAlreadyPeeked = m_tokensAlreadyPeeked.NextToken; return m_tokensAlreadyPeeked; } } @@ -4369,12 +3797,12 @@ public CssToken Peek() { do { - if (m_currentPeekToken.m_nextToken == null) + if (m_currentPeekToken.NextToken == null) { - m_currentPeekToken.m_nextToken = NextToken(); + m_currentPeekToken.NextToken = NextToken(); } - m_currentPeekToken = m_currentPeekToken.m_nextToken; - } while (m_currentPeekToken.m_tokenKind > c_maxT); + m_currentPeekToken = m_currentPeekToken.NextToken; + } while (m_currentPeekToken.TokenKind > c_maxT); return m_currentPeekToken; } @@ -4384,5 +3812,4 @@ public void ResetPeek() m_currentPeekToken = m_tokensAlreadyPeeked; } } - -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/ListItemRetriever.cs b/OpenXmlPowerTools/ListItemRetriever.cs index 75ee0311..38638821 100644 --- a/OpenXmlPowerTools/ListItemRetriever.cs +++ b/OpenXmlPowerTools/ListItemRetriever.cs @@ -1,27 +1,25 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class ListItemRetrieverSettings { - public static Dictionary> DefaultListItemTextImplementations = - new Dictionary>() + public static Dictionary> DefaultListItemTextImplementations = + new Dictionary>() { {"fr-FR", ListItemTextGetter_fr_FR.GetListItemText}, {"tr-TR", ListItemTextGetter_tr_TR.GetListItemText}, - {"ru-RU", ListItemTextGetter_ru_RU.GetListItemText}, + {"ru-RU", ListItemTextGetterRuRU.GetListItemText}, {"sv-SE", ListItemTextGetter_sv_SE.GetListItemText}, {"zh-CN", ListItemTextGetter_zh_CN.GetListItemText}, }; - public Dictionary> ListItemTextImplementations; + + public Dictionary> ListItemTextImplementations; + public ListItemRetrieverSettings() { ListItemTextImplementations = DefaultListItemTextImplementations; @@ -32,12 +30,27 @@ public class ListItemRetriever { public class ListItemSourceSet { - public int NumId; // numId from the paragraph or style - public XElement Num; // num element from the numbering part - public int AbstractNumId; // abstract numId - public XElement AbstractNum; // abstractNum element - - public ListItemSourceSet(XDocument numXDoc, XDocument styleXDoc, int numId) + /// + /// numId from the paragraph or style + /// + public int NumId { get; set; } + + /// + /// num element from the numbering part + /// + public XElement Num { get; set; } + + /// + /// abstract numId + /// + public int AbstractNumId { get; set; } + + /// + /// abstractNum element + /// + public XElement AbstractNum { get; set; } + + public ListItemSourceSet(XDocument numXDoc, int numId) { NumId = numId; @@ -54,8 +67,7 @@ public ListItemSourceSet(XDocument numXDoc, XDocument styleXDoc, int numId) AbstractNum = numXDoc .Root .Elements(W.abstractNum) - .Where(e => (int)e.Attribute(W.abstractNumId) == AbstractNumId) - .FirstOrDefault(); +.FirstOrDefault(e => (int)e.Attribute(W.abstractNumId) == AbstractNumId); } public int? StartOverride(int ilvl) @@ -64,20 +76,26 @@ public ListItemSourceSet(XDocument numXDoc, XDocument styleXDoc, int numId) .Elements(W.lvlOverride) .FirstOrDefault(nlo => (int)nlo.Attribute(W.ilvl) == ilvl); if (lvlOverride != null) + { return (int?)lvlOverride .Elements(W.startOverride) .Attributes(W.val) .FirstOrDefault(); + } + return null; } - public XElement OverrideLvl(int ilvl) + public XElement? OverrideLvl(int ilvl) { var lvlOverride = Num .Elements(W.lvlOverride) .FirstOrDefault(nlo => (int)nlo.Attribute(W.ilvl) == ilvl); if (lvlOverride != null) + { return lvlOverride.Element(W.lvl); + } + return null; } @@ -92,24 +110,27 @@ public XElement Lvl(int ilvl) { var overrideLvl = OverrideLvl(ilvl); if (overrideLvl != null) + { return overrideLvl; + } + return AbstractLvl(ilvl); } } public class ListItemSource { - public ListItemSourceSet Main; - public string NumStyleLinkName; - public ListItemSourceSet NumStyleLink; - public int Style_ilvl; + public ListItemSourceSet Main { get; set; } + public string NumStyleLinkName { get; set; } + public ListItemSourceSet NumStyleLink { get; set; } + public int Style_ilvl { get; set; } // for list item sources that use numStyleLink, there are two abstractId values. // The abstractId that is use is in num->abstractNum->numStyleLink->style->num->abstractNum public ListItemSource(XDocument numXDoc, XDocument stylesXDoc, int numId) { - Main = new ListItemSourceSet(numXDoc, stylesXDoc, numId); + Main = new ListItemSourceSet(numXDoc, numId); NumStyleLinkName = (string)Main .AbstractNum @@ -130,34 +151,43 @@ public ListItemSource(XDocument numXDoc, XDocument stylesXDoc, int numId) .FirstOrDefault(); if (numStyleLinkNumId != null) - NumStyleLink = new ListItemSourceSet(numXDoc, stylesXDoc, (int)numStyleLinkNumId); + { + NumStyleLink = new ListItemSourceSet(numXDoc, (int)numStyleLinkNumId); + } } } - public XElement Lvl(int ilvl) + public XElement? Lvl(int ilvl) { var lvl2 = Main.Lvl(ilvl); if (lvl2 == null) { - for (int i = ilvl - 1; i >= 0; i--) + for (var i = ilvl - 1; i >= 0; i--) { lvl2 = Main.Lvl(i); if (lvl2 != null) + { break; + } } } if (lvl2 != null) + { return lvl2; + } + if (NumStyleLink != null) { var lvl = NumStyleLink.Lvl(ilvl); if (lvl == null) { - for (int i = ilvl - 1; i >= 0; i--) + for (var i = ilvl - 1; i >= 0; i--) { lvl = NumStyleLink.Lvl(i); if (lvl != null) + { break; + } } } return lvl; @@ -171,7 +201,9 @@ public XElement Lvl(int ilvl) { var startOverride = NumStyleLink.StartOverride(ilvl); if (startOverride != null) + { return startOverride; + } } return Main.StartOverride(ilvl); } @@ -181,28 +213,25 @@ public int Start(int ilvl) var lvl = Lvl(ilvl); var start = (int?)lvl.Elements(W.start).Attributes(W.val).FirstOrDefault(); if (start != null) + { return (int)start; + } + return 0; } - public int AbstractNumId - { - get - { - return Main.AbstractNumId; - } - } + public int AbstractNumId => Main.AbstractNumId; } public class ListItemInfo { - public bool IsListItem; - public bool IsZeroNumId; + public bool IsListItem { get; set; } + public bool IsZeroNumId { get; set; } - public ListItemSource FromStyle; - public ListItemSource FromParagraph; + public ListItemSource FromStyle { get; set; } + public ListItemSource? FromParagraph { get; set; } - private int? mAbstractNumId = null; + private int? mAbstractNumId { get; set; } = null; public int? AbstractNumId { @@ -213,27 +242,37 @@ public int? AbstractNumId // however, it is easy enough to change if necessary if (mAbstractNumId != null) + { return mAbstractNumId; + } + if (FromParagraph != null) + { mAbstractNumId = FromParagraph.AbstractNumId; + } else if (FromStyle != null) + { mAbstractNumId = FromStyle.AbstractNumId; + } + return mAbstractNumId; } } - public XElement Lvl(int ilvl) + public XElement? Lvl(int ilvl) { if (FromParagraph != null) { var lvl = FromParagraph.Lvl(ilvl); if (lvl == null) { - for (int i = ilvl - 1; i >= 0; i--) + for (var i = ilvl - 1; i >= 0; i--) { lvl = FromParagraph.Lvl(i); if (lvl != null) + { break; + } } } return lvl; @@ -241,11 +280,13 @@ public XElement Lvl(int ilvl) var lvl2 = FromStyle.Lvl(ilvl); if (lvl2 == null) { - for (int i = ilvl - 1; i >= 0; i--) + for (var i = ilvl - 1; i >= 0; i--) { lvl2 = FromParagraph.Lvl(i); if (lvl2 != null) + { break; + } } } return lvl2; @@ -254,7 +295,10 @@ public XElement Lvl(int ilvl) public int Start(int ilvl) { if (FromParagraph != null) + { return FromParagraph.Start(ilvl); + } + return FromStyle.Start(ilvl); } @@ -274,7 +318,7 @@ public int Start(int ilvl, bool takeOverride, out bool isOverride) isOverride = false; return FromParagraph.Start(ilvl); } - else if (this.FromStyle != null) + else if (FromStyle != null) { if (takeOverride) { @@ -298,14 +342,20 @@ public int Start(int ilvl, bool takeOverride, out bool isOverride) { var startOverride = FromParagraph.StartOverride(ilvl); if (startOverride != null) + { return (int)startOverride; + } + return null; } - else if (this.FromStyle != null) + else if (FromStyle != null) { var startOverride = FromStyle.StartOverride(ilvl); if (startOverride != null) + { return (int)startOverride; + } + return null; } return null; @@ -318,16 +368,26 @@ public int NumId get { if (mNumId != null) + { return (int)mNumId; + } + if (FromParagraph != null) + { mNumId = FromParagraph.Main.NumId; + } else if (FromStyle != null) + { mNumId = FromStyle.Main.NumId; + } + return (int)mNumId; } } - public ListItemInfo() { } + public ListItemInfo() + { + } public ListItemInfo(bool isListItem, bool isZeroNumId) { @@ -335,7 +395,7 @@ public ListItemInfo(bool isListItem, bool isZeroNumId) IsZeroNumId = isZeroNumId; } } - + public static void SetParagraphLevel(XElement paragraph, int ilvl) { var pi = paragraph.Annotation(); @@ -355,22 +415,27 @@ public static int GetParagraphLevel(XElement paragraph) { var pi = paragraph.Annotation(); if (pi != null) + { return pi.Ilvl; + } + throw new OpenXmlPowerToolsException("Internal error - should never ask for ilvl without it first being set."); } - public static ListItemInfo GetListItemInfo(XDocument numXDoc, XDocument stylesXDoc, XElement paragraph) + public static ListItemInfo GetListItemInfo(XElement paragraph) { // The following is an optimization - only determine ListItemInfo once for a // paragraph. - ListItemInfo listItemInfo = paragraph.Annotation(); + var listItemInfo = paragraph.Annotation(); if (listItemInfo != null) + { return listItemInfo; + } + throw new OpenXmlPowerToolsException("Attempting to retrieve ListItemInfo before initialization"); } - private static ListItemInfo NotAListItem = new ListItemInfo(false, true); - private static ListItemInfo ZeroNumId = new ListItemInfo(false, false); + private static readonly ListItemInfo NotAListItem = new ListItemInfo(false, true); public static void InitListItemInfo(XDocument numXDoc, XDocument stylesXDoc, XElement paragraph) { @@ -382,7 +447,7 @@ public static void InitListItemInfo(XDocument numXDoc, XDocument stylesXDoc, XEl int? paragraphNumId = null; - XElement paragraphNumberingProperties = paragraph + var paragraphNumberingProperties = paragraph .Elements(W.pPr) .Elements(W.numPr) .FirstOrDefault(); @@ -403,7 +468,7 @@ public static void InitListItemInfo(XDocument numXDoc, XDocument stylesXDoc, XEl } } - string paragraphStyleName = GetParagraphStyleName(stylesXDoc, paragraph); + var paragraphStyleName = GetParagraphStyleName(stylesXDoc, paragraph); var listItemInfo = GetListItemInfoFromCache(numXDoc, paragraphStyleName, paragraphNumId); if (listItemInfo != null) @@ -418,22 +483,28 @@ public static void InitListItemInfo(XDocument numXDoc, XDocument stylesXDoc, XEl .FirstOrDefault(); if (para_ilvl == null) + { para_ilvl = 0; + } var abstractNum = listItemInfo.FromParagraph.Main.AbstractNum; var multiLevelType = (string)abstractNum.Elements(W.multiLevelType).Attributes(W.val).FirstOrDefault(); if (multiLevelType == "singleLevel") + { para_ilvl = 0; + } SetParagraphLevel(paragraph, (int)para_ilvl); } else if (listItemInfo.FromStyle != null) { - int this_ilvl = listItemInfo.FromStyle.Style_ilvl; + var this_ilvl = listItemInfo.FromStyle.Style_ilvl; var abstractNum = listItemInfo.FromStyle.Main.AbstractNum; var multiLevelType = (string)abstractNum.Elements(W.multiLevelType).Attributes(W.val).FirstOrDefault(); if (multiLevelType == "singleLevel") + { this_ilvl = 0; + } SetParagraphLevel(paragraph, this_ilvl); } @@ -447,8 +518,7 @@ public static void InitListItemInfo(XDocument numXDoc, XDocument stylesXDoc, XEl if (paragraphStyleName != null) { - listItemInfo.FromStyle = InitializeStyleListItemSource(numXDoc, stylesXDoc, paragraph, paragraphStyleName, - out style_ilvl, out styleZeroNumId); + listItemInfo.FromStyle = InitializeStyleListItemSource(numXDoc, stylesXDoc, paragraph, out style_ilvl, out styleZeroNumId); } int? paragraph_ilvl = null; @@ -459,33 +529,40 @@ public static void InitListItemInfo(XDocument numXDoc, XDocument stylesXDoc, XEl listItemInfo.FromParagraph = InitializeParagraphListItemSource(numXDoc, stylesXDoc, paragraph, paragraphNumberingProperties, out paragraph_ilvl, out paragraphZeroNumId); } - if (styleZeroNumId == true && paragraphZeroNumId == null || - paragraphZeroNumId == true) + if (styleZeroNumId == true && paragraphZeroNumId == null || paragraphZeroNumId == true) { paragraph.AddAnnotation(NotAListItem); AddListItemInfoIntoCache(numXDoc, paragraphStyleName, paragraphNumId, NotAListItem); return; } - int ilvlToSet = 0; + var ilvlToSet = 0; if (paragraph_ilvl != null) + { ilvlToSet = (int)paragraph_ilvl; + } else if (style_ilvl != null) + { ilvlToSet = (int)style_ilvl; + } if (listItemInfo.FromParagraph != null) { var abstractNum = listItemInfo.FromParagraph.Main.AbstractNum; var multiLevelType = (string)abstractNum.Elements(W.multiLevelType).Attributes(W.val).FirstOrDefault(); if (multiLevelType == "singleLevel") + { ilvlToSet = 0; + } } else if (listItemInfo.FromStyle != null) { var abstractNum = listItemInfo.FromStyle.Main.AbstractNum; var multiLevelType = (string)abstractNum.Elements(W.multiLevelType).Attributes(W.val).FirstOrDefault(); if (multiLevelType == "singleLevel") + { ilvlToSet = 0; + } } SetParagraphLevel(paragraph, ilvlToSet); @@ -504,7 +581,9 @@ private static string GetParagraphStyleName(XDocument stylesXDoc, XElement parag .FirstOrDefault(); if (paragraphStyleName == null) + { paragraphStyleName = GetDefaultParagraphStyleName(stylesXDoc); + } return paragraphStyleName; } @@ -513,8 +592,7 @@ private static bool FirstRunIsEmptySectionBreak(XElement paragraph) { var firstRun = paragraph .DescendantsTrimmed(W.txbxContent) - .Where(d => d.Name == W.r) - .FirstOrDefault(); +.FirstOrDefault(d => d.Name == W.r); var hasTextElement = paragraph .DescendantsTrimmed(W.txbxContent) @@ -522,23 +600,22 @@ private static bool FirstRunIsEmptySectionBreak(XElement paragraph) .Elements(W.t) .Any(); - if (firstRun == null || !hasTextElement) - { - if (paragraph + if ((firstRun == null || !hasTextElement) && paragraph .Elements(W.pPr) .Elements(W.sectPr) .Any()) - return true; + { + return true; } return false; } - private static ListItemSource InitializeParagraphListItemSource(XDocument numXDoc, XDocument stylesXDoc, XElement paragraph, XElement paragraphNumberingProperties, out int? ilvl, out bool? zeroNumId) + private static ListItemSource? InitializeParagraphListItemSource(XDocument numXDoc, XDocument stylesXDoc, XElement paragraph, XElement paragraphNumberingProperties, out int? ilvl, out bool? zeroNumId) { zeroNumId = null; // Paragraph numbering properties must contain a numId. - int? numId = (int?)paragraphNumberingProperties + var numId = (int?)paragraphNumberingProperties .Elements(W.numId) .Attributes(W.val) .FirstOrDefault(); @@ -567,27 +644,28 @@ private static ListItemSource InitializeParagraphListItemSource(XDocument numXDo zeroNumId = false; if (ilvl == null) + { ilvl = 0; + } - ListItemSource listItemSource = new ListItemSource(numXDoc, stylesXDoc, (int)numId); + var listItemSource = new ListItemSource(numXDoc, stylesXDoc, (int)numId); return listItemSource; } - private static ListItemSource InitializeStyleListItemSource(XDocument numXDoc, XDocument stylesXDoc, XElement paragraph, string paragraphStyleName, - out int? ilvl, out bool? zeroNumId) + private static ListItemSource? InitializeStyleListItemSource(XDocument numXDoc, XDocument stylesXDoc, XElement paragraph, out int? ilvl, out bool? zeroNumId) { zeroNumId = null; - XElement pPr = FormattingAssembler.ParagraphStyleRollup(paragraph, stylesXDoc, GetDefaultParagraphStyleName(stylesXDoc)); + var pPr = FormattingAssembler.ParagraphStyleRollup(paragraph, stylesXDoc, GetDefaultParagraphStyleName(stylesXDoc)); if (pPr != null) { - XElement styleNumberingProperties = pPr + var styleNumberingProperties = pPr .Elements(W.numPr) .FirstOrDefault(); if (styleNumberingProperties != null && styleNumberingProperties.Element(W.numId) != null) { - int numId = (int)styleNumberingProperties + var numId = (int)styleNumberingProperties .Elements(W.numId) .Attributes(W.val) .FirstOrDefault(); @@ -598,7 +676,9 @@ private static ListItemSource InitializeStyleListItemSource(XDocument numXDoc, X .FirstOrDefault(); if (ilvl == null) + { ilvl = 0; + } if (numId == 0) { @@ -607,11 +687,10 @@ private static ListItemSource InitializeStyleListItemSource(XDocument numXDoc, X } // make sure that the numId is valid - XElement num = numXDoc + var num = numXDoc .Root .Elements(W.num) - .Where(e => (int)e.Attribute(W.numId) == numId) - .FirstOrDefault(); +.FirstOrDefault(e => (int)e.Attribute(W.numId) == numId); if (num == null) { @@ -619,8 +698,10 @@ private static ListItemSource InitializeStyleListItemSource(XDocument numXDoc, X return null; } - ListItemSource listItemSource = new ListItemSource(numXDoc, stylesXDoc, numId); - listItemSource.Style_ilvl = (int)ilvl; + var listItemSource = new ListItemSource(numXDoc, stylesXDoc, numId) + { + Style_ilvl = (int)ilvl + }; zeroNumId = false; return listItemSource; @@ -633,12 +714,14 @@ private static ListItemSource InitializeStyleListItemSource(XDocument numXDoc, X private static string GetDefaultParagraphStyleName(XDocument stylesXDoc) { XElement defaultParagraphStyle; - string defaultParagraphStyleName = null; + string? defaultParagraphStyleName = null; - StylesInfo stylesInfo = stylesXDoc.Annotation(); + var stylesInfo = stylesXDoc.Annotation(); if (stylesInfo != null) + { defaultParagraphStyleName = stylesInfo.DefaultParagraphStyleName; + } else { defaultParagraphStyle = stylesXDoc @@ -647,17 +730,25 @@ private static string GetDefaultParagraphStyleName(XDocument stylesXDoc) .FirstOrDefault(s => { if ((string)s.Attribute(W.type) != "paragraph") + { return false; + } + var defaultAttribute = s.Attribute(W._default); - var isDefault = false; - if (defaultAttribute != null && - (bool)s.Attribute(W._default).ToBoolean()) - isDefault = true; - return isDefault; + + if (defaultAttribute != null && s.Attribute(W._default).ToBoolean().HasValue) + { + return s.Attribute(W._default)?.ToBoolean() ?? false; + } + + return false; }); defaultParagraphStyleName = null; if (defaultParagraphStyle != null) + { defaultParagraphStyleName = (string)defaultParagraphStyle.Attribute(W.styleId); + } + stylesInfo = new StylesInfo() { DefaultParagraphStyleName = defaultParagraphStyleName, @@ -667,15 +758,12 @@ private static string GetDefaultParagraphStyleName(XDocument stylesXDoc) return defaultParagraphStyleName; } - private static ListItemInfo GetListItemInfoFromCache(XDocument numXDoc, string styleName, int? numId) + private static ListItemInfo? GetListItemInfoFromCache(XDocument numXDoc, string styleName, int? numId) { - string key = - (styleName == null ? "" : styleName) + - "|" + - (numId == null ? "" : numId.ToString()); + var key = (styleName ?? "") + "|" + (numId == null ? "" : numId.ToString()); var numXDocRoot = numXDoc.Root; - Dictionary listItemInfoCache = + var listItemInfoCache = numXDocRoot.Annotation>(); if (listItemInfoCache == null) { @@ -683,19 +771,22 @@ private static ListItemInfo GetListItemInfoFromCache(XDocument numXDoc, string s numXDocRoot.AddAnnotation(listItemInfoCache); } if (listItemInfoCache.ContainsKey(key)) + { return listItemInfoCache[key]; + } + return null; } private static void AddListItemInfoIntoCache(XDocument numXDoc, string styleName, int? numId, ListItemInfo listItemInfo) { - string key = + var key = (styleName == null ? "" : styleName) + "|" + (numId == null ? "" : numId.ToString()); var numXDocRoot = numXDoc.Root; - Dictionary listItemInfoCache = + var listItemInfoCache = numXDocRoot.Annotation>(); if (listItemInfoCache == null) { @@ -703,79 +794,94 @@ private static void AddListItemInfoIntoCache(XDocument numXDoc, string styleName numXDocRoot.AddAnnotation(listItemInfoCache); } if (!listItemInfoCache.ContainsKey(key)) + { listItemInfoCache.Add(key, listItemInfo); + } } public class LevelNumbers { - public int[] LevelNumbersArray; + public int[] LevelNumbersArray { get; set; } } private class StylesInfo { - public string DefaultParagraphStyleName; + public string? DefaultParagraphStyleName { get; set; } } private class ParagraphInfo { - public int Ilvl; + public int Ilvl { get; set; } } private class ReverseAxis { - public XElement PreviousParagraph; + public XElement? PreviousParagraph { get; set; } } - public static string RetrieveListItem(WordprocessingDocument wordDoc, XElement paragraph) + public static string? RetrieveListItem(WordprocessingDocument wordDoc, XElement paragraph) { return RetrieveListItem(wordDoc, paragraph, null); } - public static string RetrieveListItem(WordprocessingDocument wordDoc, XElement paragraph, ListItemRetrieverSettings settings) + public static string? RetrieveListItem(WordprocessingDocument wordDoc, XElement paragraph, ListItemRetrieverSettings? settings) { if (wordDoc.MainDocumentPart.NumberingDefinitionsPart == null) + { return null; + } var listItemInfo = paragraph.Annotation(); if (listItemInfo == null) - InitializeListItemRetriever(wordDoc, settings); + { + InitializeListItemRetriever(wordDoc); + } listItemInfo = paragraph.Annotation(); if (!listItemInfo.IsListItem) + { return null; + } var numberingDefinitionsPart = wordDoc .MainDocumentPart .NumberingDefinitionsPart; if (numberingDefinitionsPart == null) + { return null; + } - StyleDefinitionsPart styleDefinitionsPart = wordDoc + var styleDefinitionsPart = wordDoc .MainDocumentPart .StyleDefinitionsPart; if (styleDefinitionsPart == null) + { return null; + } - var numXDoc = numberingDefinitionsPart.GetXDocument(); var stylesXDoc = styleDefinitionsPart.GetXDocument(); var paragraphLevel = GetParagraphLevel(paragraph); var lvl = listItemInfo.Lvl(paragraphLevel); - string lvlText = (string)lvl.Elements(W.lvlText).Attributes(W.val).FirstOrDefault(); + var lvlText = (string)lvl.Elements(W.lvlText).Attributes(W.val).FirstOrDefault(); if (lvlText == null) + { return null; + } var levelNumbersAnnotation = paragraph.Annotation(); if (levelNumbersAnnotation == null) + { throw new OpenXmlPowerToolsException("Internal error"); + } - int[] levelNumbers = levelNumbersAnnotation.LevelNumbersArray; - string languageIdentifier = GetLanguageIdentifier(paragraph, stylesXDoc); - string listItem = FormatListItem(listItemInfo, levelNumbers, GetParagraphLevel(paragraph), - lvlText, stylesXDoc, languageIdentifier, settings); + var levelNumbers = levelNumbersAnnotation.LevelNumbersArray; + var languageIdentifier = GetLanguageIdentifier(paragraph, stylesXDoc); + var listItem = FormatListItem(listItemInfo, levelNumbers, GetParagraphLevel(paragraph), + lvlText, languageIdentifier, settings); return listItem; } @@ -787,7 +893,7 @@ private static string GetLanguageIdentifier(XElement paragraph, XDocument styles .Attributes(PtOpenXml.LanguageType) .FirstOrDefault(); - string languageIdentifier = null; + string? languageIdentifier = null; if (languageType == null || languageType == "western") { @@ -799,6 +905,7 @@ private static string GetLanguageIdentifier(XElement paragraph, XDocument styles .FirstOrDefault(); if (languageIdentifier == null) + { languageIdentifier = (string)stylesXDoc .Root .Elements(W.docDefaults) @@ -807,6 +914,7 @@ private static string GetLanguageIdentifier(XElement paragraph, XDocument styles .Elements(W.lang) .Attributes(W.val) .FirstOrDefault(); + } } else if (languageType == "eastAsia") { @@ -818,6 +926,7 @@ private static string GetLanguageIdentifier(XElement paragraph, XDocument styles .FirstOrDefault(); if (languageIdentifier == null) + { languageIdentifier = (string)stylesXDoc .Root .Elements(W.docDefaults) @@ -826,6 +935,7 @@ private static string GetLanguageIdentifier(XElement paragraph, XDocument styles .Elements(W.lang) .Attributes(W.eastAsia) .FirstOrDefault(); + } } else if (languageType == "bidi") { @@ -837,6 +947,7 @@ private static string GetLanguageIdentifier(XElement paragraph, XDocument styles .FirstOrDefault(); if (languageIdentifier == null) + { languageIdentifier = (string)stylesXDoc .Root .Elements(W.docDefaults) @@ -845,45 +956,43 @@ private static string GetLanguageIdentifier(XElement paragraph, XDocument styles .Elements(W.lang) .Attributes(W.bidi) .FirstOrDefault(); + } } - if (languageIdentifier == null) + { languageIdentifier = "en-US"; + } + return languageIdentifier; } - private static void InitializeListItemRetriever(WordprocessingDocument wordDoc, ListItemRetrieverSettings settings) + private static void InitializeListItemRetriever(WordprocessingDocument wordDoc) { foreach (var part in wordDoc.ContentParts()) - InitializeListItemRetrieverForPart(wordDoc, part, settings); - -#if false - foreach (var part in wordDoc.ContentParts()) - { - var xDoc = part.GetXDocument(); - var paras = xDoc - .Descendants(W.p) - .Where(p => - p.Annotation() == null); - if (paras.Any()) - Console.WriteLine("Error"); - } -#endif + { + InitializeListItemRetrieverForPart(wordDoc, part); + } } - private static void InitializeListItemRetrieverForPart(WordprocessingDocument wordDoc, OpenXmlPart part, ListItemRetrieverSettings settings) + private static void InitializeListItemRetrieverForPart(WordprocessingDocument wordDoc, OpenXmlPart part) { var mainXDoc = part.GetXDocument(); - + var numPart = wordDoc.MainDocumentPart.NumberingDefinitionsPart; if (numPart == null) + { return; + } + var numXDoc = numPart.GetXDocument(); var stylesPart = wordDoc.MainDocumentPart.StyleDefinitionsPart; if (stylesPart == null) + { return; + } + var stylesXDoc = stylesPart.GetXDocument(); var rootNode = mainXDoc.Root; @@ -895,7 +1004,9 @@ private static void InitializeListItemRetrieverForPart(WordprocessingDocument wo .Descendants(W.txbxContent); foreach (var textBox in textBoxes) + { InitializeListItemRetrieverForStory(numXDoc, stylesXDoc, textBox); + } } private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocument stylesXDoc, XElement rootNode) @@ -905,14 +1016,19 @@ private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocu .Where(p => p.Name == W.p); foreach (var paragraph in paragraphs) + { InitListItemInfo(numXDoc, stylesXDoc, paragraph); + } var abstractNumIds = paragraphs .Select(paragraph => { - ListItemInfo listItemInfo = paragraph.Annotation(); + var listItemInfo = paragraph.Annotation(); if (!listItemInfo.IsListItem) - return (int?)null; + { + return null; + } + return listItemInfo.AbstractNumId; }) .Where(a => a != null) @@ -936,16 +1052,19 @@ private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocu { var listItemInfo = paragraph.Annotation(); if (!listItemInfo.IsListItem) + { return false; + } + return listItemInfo.AbstractNumId == abstractNumId; }) .ToList(); // annotate paragraphs with previous paragraphs so that we can look backwards with good perf - XElement prevParagraph = null; + XElement? prevParagraph = null; foreach (var paragraph in listItems) { - ReverseAxis reverse = new ReverseAxis() + var reverse = new ReverseAxis() { PreviousParagraph = prevParagraph, }; @@ -954,8 +1073,8 @@ private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocu } var startOverrideAlreadyUsed = new List(); - List previous = null; - ListItemInfo[] listItemInfoInEffectForStartOverride = new ListItemInfo[] { + List? previous = null; + var listItemInfoInEffectForStartOverride = new ListItemInfo[] { null, null, null, @@ -972,18 +1091,23 @@ private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocu var listItemInfo = paragraph.Annotation(); var ilvl = GetParagraphLevel(paragraph); listItemInfoInEffectForStartOverride[ilvl] = listItemInfo; - ListItemInfo listItemInfoInEffect = null; + ListItemInfo? listItemInfoInEffect = null; if (ilvl > 0) + { listItemInfoInEffect = listItemInfoInEffectForStartOverride[ilvl - 1]; + } + var levelNumbers = new List(); - for (int level = 0; level <= ilvl; level++) + for (var level = 0; level <= ilvl; level++) { var numId = listItemInfo.NumId; var startOverride = listItemInfo.StartOverride(level); int? inEffectStartOverride = null; if (listItemInfoInEffect != null) + { inEffectStartOverride = listItemInfoInEffect.StartOverride(level); + } if (level == ilvl) { @@ -998,15 +1122,15 @@ private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocu return plvl == ilvl; }); if (previousPara != null) + { previous = previousPara.Annotation().LevelNumbersArray.ToList(); + } } } - if (previous == null || - level >= previous.Count() || - (level == ilvl && startOverride != null && !startOverrideAlreadyUsed.Contains(numId))) + if (previous == null || level >= previous.Count || level == ilvl && startOverride != null && !startOverrideAlreadyUsed.Contains(numId)) { - if (previous == null || level >= previous.Count()) + if (previous == null || level >= previous.Count) { var start = listItemInfo.Start(level); // only look at startOverride if the level that we're examining is same as the paragraph's level. @@ -1020,9 +1144,14 @@ private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocu else { if (startOverride != null) + { start = (int)startOverride; + } + if (inEffectStartOverride != null && inEffectStartOverride > start) + { start = (int)inEffectStartOverride; + } } } levelNumbers.Add(start); @@ -1031,16 +1160,10 @@ private static void InitializeListItemRetrieverForStory(XDocument numXDoc, XDocu { var start = listItemInfo.Start(level); // only look at startOverride if the level that we're examining is same as the paragraph's level. - if (level == ilvl) + if (level == ilvl && startOverride != null && !startOverrideAlreadyUsed.Contains(numId)) { - if (startOverride != null) - { - if (!startOverrideAlreadyUsed.Contains(numId)) - { - startOverrideAlreadyUsed.Add(numId); - start = (int)startOverride; - } - } + startOverrideAlreadyUsed.Add(numId); + start = (int)startOverride; } levelNumbers.Add(start); } @@ -1088,56 +1211,73 @@ private static IEnumerable PreviousParagraphsForLvlRestart(XElement pa { var ra = current.Annotation(); if (ra == null || ra.PreviousParagraph == null) + { yield break; + } + var raLvl = GetParagraphLevel(ra.PreviousParagraph); if (raLvl < ilvl) + { yield break; + } + yield return ra.PreviousParagraph; current = ra.PreviousParagraph; } } - private static string FormatListItem(ListItemInfo lii, int[] levelNumbers, int ilvl, - string lvlText, XDocument styles, string languageCultureName, ListItemRetrieverSettings settings) + private static string FormatListItem(ListItemInfo lii, int[] levelNumbers, int ilvl, string lvlText, string languageCultureName, ListItemRetrieverSettings settings) { - string[] formatTokens = GetFormatTokens(lvlText).ToArray(); - XElement lvl = lii.Lvl(ilvl); - bool isLgl = lvl.Elements(W.isLgl).Any(); - string listItem = formatTokens.Select((t, l) => + var formatTokens = GetFormatTokens(lvlText).ToArray(); + var lvl = lii.Lvl(ilvl); + var isLgl = lvl.Elements(W.isLgl).Any(); + var listItem = formatTokens.Select((t, l) => { if (t.Substring(0, 1) != "%") + { return t; - int indentationLevel; - if (!Int32.TryParse(t.Substring(1), out indentationLevel)) + } + + if (!int.TryParse(t.Substring(1), out var indentationLevel)) + { return t; + } + indentationLevel -= 1; if (indentationLevel >= levelNumbers.Length) + { indentationLevel = levelNumbers.Length - 1; - int levelNumber = levelNumbers[indentationLevel]; - string levelText = null; - XElement rlvl = lii.Lvl(indentationLevel); - string numFmtForLevel = (string)rlvl.Elements(W.numFmt).Attributes(W.val).FirstOrDefault(); + } + + var levelNumber = levelNumbers[indentationLevel]; + string? levelText = null; + var rlvl = lii.Lvl(indentationLevel); + var numFmtForLevel = (string)rlvl.Elements(W.numFmt).Attributes(W.val).FirstOrDefault(); if (numFmtForLevel == null) { var numFmtElement = rlvl.Elements(MC.AlternateContent).Elements(MC.Choice).Elements(W.numFmt).FirstOrDefault(); if (numFmtElement != null && (string)numFmtElement.Attribute(W.val) == "custom") + { numFmtForLevel = (string)numFmtElement.Attribute(W.format); + } } if (numFmtForLevel != "none") { if (isLgl && numFmtForLevel != "decimalZero") + { numFmtForLevel = "decimal"; + } } - if (languageCultureName != null && settings != null) + if (languageCultureName != null && settings != null && settings.ListItemTextImplementations.ContainsKey(languageCultureName)) { - if (settings.ListItemTextImplementations.ContainsKey(languageCultureName)) - { - var impl = settings.ListItemTextImplementations[languageCultureName]; - levelText = impl(languageCultureName, levelNumber, numFmtForLevel); - } + var impl = settings.ListItemTextImplementations[languageCultureName]; + levelText = impl(levelNumber, numFmtForLevel); } if (levelText == null) - levelText = ListItemTextGetter_Default.GetListItemText(languageCultureName, levelNumber, numFmtForLevel); + { + levelText = ListItemTextGetter_Default.GetListItemText(levelNumber, numFmtForLevel); + } + return levelText; }).StringConcatenate(); return listItem; @@ -1145,18 +1285,21 @@ private static string FormatListItem(ListItemInfo lii, int[] levelNumbers, int i private static IEnumerable GetFormatTokens(string lvlText) { - int i = 0; + var i = 0; while (true) { if (i >= lvlText.Length) + { yield break; + } + if (lvlText[i] == '%' && i <= lvlText.Length - 2) { yield return lvlText.Substring(i, 2); i += 2; continue; } - int percentIndex = lvlText.IndexOf('%', i); + var percentIndex = lvlText.IndexOf('%', i); if (percentIndex == -1 || percentIndex > lvlText.Length - 2) { yield return lvlText.Substring(i); @@ -1167,6 +1310,5 @@ private static IEnumerable GetFormatTokens(string lvlText) i = percentIndex + 2; } } - } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/GetListItemText_Default.cs b/OpenXmlPowerTools/ListItemTextGetter_Default.cs similarity index 67% rename from OpenXmlPowerTools/GetListItemText_Default.cs rename to OpenXmlPowerTools/ListItemTextGetter_Default.cs index ae4f9cd0..7e1d0522 100644 --- a/OpenXmlPowerTools/GetListItemText_Default.cs +++ b/OpenXmlPowerTools/ListItemTextGetter_Default.cs @@ -1,60 +1,52 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { - class ListItemTextGetter_Default + internal class ListItemTextGetter_Default { - private static string[] RomanOnes = + private static readonly string[] RomanOnes = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; - private static string[] RomanTens = + private static readonly string[] RomanTens = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; - private static string[] RomanHundreds = + private static readonly string[] RomanHundreds = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "M" }; - private static string[] RomanThousands = + private static readonly string[] RomanThousands = { "", "M", "MM", "MMM", "MMMM", "MMMMM", "MMMMMM", "MMMMMMM", "MMMMMMMM", "MMMMMMMMM", "MMMMMMMMMM" }; - private static string[] OneThroughNineteen = { + private static readonly string[] OneThroughNineteen = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; - private static string[] Tens = { + private static readonly string[] Tens = { "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }; - private static string[] OrdinalOneThroughNineteen = { + private static readonly string[] OrdinalOneThroughNineteen = { "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth" }; - private static string[] OrdinalTenths = { + private static readonly string[] OrdinalTenths = { "tenth", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth" }; - public static string GetListItemText(string languageCultureName, int levelNumber, string numFmt) + public static string GetListItemText(int levelNumber, string numFmt) { if (numFmt == "none") { @@ -67,48 +59,58 @@ public static string GetListItemText(string languageCultureName, int levelNumber if (numFmt == "decimalZero") { if (levelNumber <= 9) + { return "0" + levelNumber.ToString(); + } else + { return levelNumber.ToString(); + } } if (numFmt == "upperRoman") { - int ones = levelNumber % 10; - int tens = (levelNumber % 100) / 10; - int hundreds = (levelNumber % 1000) / 100; - int thousands = levelNumber / 1000; + var ones = levelNumber % 10; + var tens = levelNumber % 100 / 10; + var hundreds = levelNumber % 1000 / 100; + var thousands = levelNumber / 1000; return RomanThousands[thousands] + RomanHundreds[hundreds] + RomanTens[tens] + RomanOnes[ones]; } if (numFmt == "lowerRoman") { - int ones = levelNumber % 10; - int tens = (levelNumber % 100) / 10; - int hundreds = (levelNumber % 1000) / 100; - int thousands = levelNumber / 1000; + var ones = levelNumber % 10; + var tens = levelNumber % 100 / 10; + var hundreds = levelNumber % 1000 / 100; + var thousands = levelNumber / 1000; return (RomanThousands[thousands] + RomanHundreds[hundreds] + RomanTens[tens] + RomanOnes[ones]).ToLower(); } if (numFmt == "upperLetter") { - int levelNumber2 = levelNumber % 780; + var levelNumber2 = levelNumber % 780; if (levelNumber2 == 0) + { levelNumber2 = 780; - string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - int c = (levelNumber2 - 1) / 26; - int n = (levelNumber2 - 1) % 26; - char x = a[n]; + } + + var a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + var c = (levelNumber2 - 1) / 26; + var n = (levelNumber2 - 1) % 26; + var x = a[n]; return "".PadRight(c + 1, x); } if (numFmt == "lowerLetter") { - int levelNumber3 = levelNumber % 780; + var levelNumber3 = levelNumber % 780; if (levelNumber3 == 0) + { levelNumber3 = 780; - string a = "abcdefghijklmnopqrstuvwxyz"; - int c = (levelNumber3 - 1) / 26; - int n = (levelNumber3 - 1) % 26; - char x = a[n]; + } + + var a = "abcdefghijklmnopqrstuvwxyz"; + var c = (levelNumber3 - 1) / 26; + var n = (levelNumber3 - 1) % 26; + var x = a[n]; return "".PadRight(c + 1, x); } if (numFmt == "ordinal") @@ -116,59 +118,95 @@ public static string GetListItemText(string languageCultureName, int levelNumber string suffix; if (levelNumber % 100 == 11 || levelNumber % 100 == 12 || levelNumber % 100 == 13) + { suffix = "th"; + } else if (levelNumber % 10 == 1) + { suffix = "st"; + } else if (levelNumber % 10 == 2) + { suffix = "nd"; + } else if (levelNumber % 10 == 3) + { suffix = "rd"; + } else + { suffix = "th"; + } + return levelNumber.ToString() + suffix; } if (numFmt == "cardinalText") { - string result = ""; - int t1 = levelNumber / 1000; - int t2 = levelNumber % 1000; + var result = ""; + var t1 = levelNumber / 1000; + var t2 = levelNumber % 1000; if (t1 >= 1) + { result += OneThroughNineteen[t1 - 1] + " thousand"; + } + if (t1 >= 1 && t2 == 0) + { return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + if (t1 >= 1) + { result += " "; - int h1 = (levelNumber % 1000) / 100; - int h2 = levelNumber % 100; + } + + var h1 = levelNumber % 1000 / 100; + var h2 = levelNumber % 100; if (h1 >= 1) + { result += OneThroughNineteen[h1 - 1] + " hundred"; + } + if (h1 >= 1 && h2 == 0) + { return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + if (h1 >= 1) + { result += " "; - int z = levelNumber % 100; + } + + var z = levelNumber % 100; if (z <= 19) + { result += OneThroughNineteen[z - 1]; + } else { - int x = z / 10; - int r = z % 10; + var x = z / 10; + var r = z % 10; result += Tens[x - 1]; if (r >= 1) + { result += "-" + OneThroughNineteen[r - 1]; + } } return result.Substring(0, 1).ToUpper() + result.Substring(1); } if (numFmt == "ordinalText") { - string result = ""; - int t1 = levelNumber / 1000; - int t2 = levelNumber % 1000; + var result = ""; + var t1 = levelNumber / 1000; + var t2 = levelNumber % 1000; if (t1 >= 1 && t2 != 0) + { result += OneThroughNineteen[t1 - 1] + " thousand"; + } + if (t1 >= 1 && t2 == 0) { result += OneThroughNineteen[t1 - 1] + " thousandth"; @@ -176,11 +214,17 @@ public static string GetListItemText(string languageCultureName, int levelNumber result.Substring(1); } if (t1 >= 1) + { result += " "; - int h1 = (levelNumber % 1000) / 100; - int h2 = levelNumber % 100; + } + + var h1 = levelNumber % 1000 / 100; + var h2 = levelNumber % 100; if (h1 >= 1 && h2 != 0) + { result += OneThroughNineteen[h1 - 1] + " hundred"; + } + if (h1 >= 1 && h2 == 0) { result += OneThroughNineteen[h1 - 1] + " hundredth"; @@ -188,20 +232,32 @@ public static string GetListItemText(string languageCultureName, int levelNumber result.Substring(1); } if (h1 >= 1) + { result += " "; - int z = levelNumber % 100; + } + + var z = levelNumber % 100; if (z <= 19) + { result += OrdinalOneThroughNineteen[z - 1]; + } else { - int x = z / 10; - int r = z % 10; + var x = z / 10; + var r = z % 10; if (r == 0) + { result += OrdinalTenths[x - 1]; + } else + { result += Tens[x - 1]; + } + if (r >= 1) + { result += "-" + OrdinalOneThroughNineteen[r - 1]; + } } return result.Substring(0, 1).ToUpper() + result.Substring(1); @@ -223,7 +279,10 @@ public static string GetListItemText(string languageCultureName, int levelNumber return string.Format("{0:00000}", levelNumber); } if (numFmt == "bullet") + { return ""; + } + if (numFmt == "decimalEnclosedCircle") { if (levelNumber >= 1 && levelNumber <= 20) @@ -237,4 +296,4 @@ public static string GetListItemText(string languageCultureName, int levelNumber return levelNumber.ToString(); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/MarkupSimplifier.cs b/OpenXmlPowerTools/MarkupSimplifier.cs index 564f129a..1240e78b 100644 --- a/OpenXmlPowerTools/MarkupSimplifier.cs +++ b/OpenXmlPowerTools/MarkupSimplifier.cs @@ -1,15 +1,12 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Xml.Linq; using System.Xml.Schema; -using DocumentFormat.OpenXml.Packaging; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public partial class WmlDocument { @@ -21,24 +18,24 @@ public WmlDocument SimplifyMarkup(SimplifyMarkupSettings settings) public class SimplifyMarkupSettings { - public bool AcceptRevisions; - public bool NormalizeXml; - public bool RemoveBookmarks; - public bool RemoveComments; - public bool RemoveContentControls; - public bool RemoveEndAndFootNotes; - public bool RemoveFieldCodes; - public bool RemoveGoBackBookmark; - public bool RemoveHyperlinks; - public bool RemoveLastRenderedPageBreak; - public bool RemoveMarkupForDocumentComparison; - public bool RemovePermissions; - public bool RemoveProof; - public bool RemoveRsidInfo; - public bool RemoveSmartTags; - public bool RemoveSoftHyphens; - public bool RemoveWebHidden; - public bool ReplaceTabsWithSpaces; + public bool AcceptRevisions { get; set; } + public bool NormalizeXml { get; set; } + public bool RemoveBookmarks { get; set; } + public bool RemoveComments { get; set; } + public bool RemoveContentControls { get; set; } + public bool RemoveEndAndFootNotes { get; set; } + public bool RemoveFieldCodes { get; set; } + public bool RemoveGoBackBookmark { get; set; } + public bool RemoveHyperlinks { get; set; } + public bool RemoveLastRenderedPageBreak { get; set; } + public bool RemoveMarkupForDocumentComparison { get; set; } + public bool RemovePermissions { get; set; } + public bool RemoveProof { get; set; } + public bool RemoveRsidInfo { get; set; } + public bool RemoveSmartTags { get; set; } + public bool RemoveSoftHyphens { get; set; } + public bool RemoveWebHidden { get; set; } + public bool ReplaceTabsWithSpaces { get; set; } } [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -46,12 +43,13 @@ public static class MarkupSimplifier { public static WmlDocument SimplifyMarkup(WmlDocument doc, SimplifyMarkupSettings settings) { - using (var streamDoc = new OpenXmlMemoryStreamDocument(doc)) + using var streamDoc = new OpenXmlMemoryStreamDocument(doc); + using (var document = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument()) - SimplifyMarkup(document, settings); - return streamDoc.GetModifiedWmlDocument(); + SimplifyMarkup(document, settings); } + + return streamDoc.GetModifiedWmlDocument(); } public static void SimplifyMarkup(WordprocessingDocument doc, SimplifyMarkupSettings settings) @@ -62,43 +60,65 @@ public static void SimplifyMarkup(WordprocessingDocument doc, SimplifyMarkupSett RemoveElementsForDocumentComparison(doc); } if (settings.RemoveRsidInfo) + { RemoveRsidInfoInSettings(doc); + } + if (settings.AcceptRevisions) + { RevisionAccepter.AcceptRevisions(doc); - foreach (OpenXmlPart part in doc.ContentParts()) + } + + foreach (var part in doc.ContentParts()) + { SimplifyMarkupForPart(part, settings); + } if (doc.MainDocumentPart.StyleDefinitionsPart != null) + { SimplifyMarkupForPart(doc.MainDocumentPart.StyleDefinitionsPart, settings); + } + if (doc.MainDocumentPart.StylesWithEffectsPart != null) + { SimplifyMarkupForPart(doc.MainDocumentPart.StylesWithEffectsPart, settings); + } if (settings.RemoveComments) { - WordprocessingCommentsPart commentsPart = doc.MainDocumentPart.WordprocessingCommentsPart; - if (commentsPart != null) doc.MainDocumentPart.DeletePart(commentsPart); + var commentsPart = doc.MainDocumentPart.WordprocessingCommentsPart; + if (commentsPart != null) + { + doc.MainDocumentPart.DeletePart(commentsPart); + } - WordprocessingCommentsExPart commentsExPart = doc.MainDocumentPart.WordprocessingCommentsExPart; - if (commentsExPart != null) doc.MainDocumentPart.DeletePart(commentsExPart); + var commentsExPart = doc.MainDocumentPart.WordprocessingCommentsExPart; + if (commentsExPart != null) + { + doc.MainDocumentPart.DeletePart(commentsExPart); + } } } private static void RemoveRsidInfoInSettings(WordprocessingDocument doc) { - DocumentSettingsPart part = doc.MainDocumentPart.DocumentSettingsPart; - if (part == null) return; + var part = doc.MainDocumentPart.DocumentSettingsPart; + if (part == null) + { + return; + } - XDocument settingsXDoc = part.GetXDocument(); + var settingsXDoc = part.GetXDocument(); settingsXDoc.Descendants(W.rsids).Remove(); part.PutXDocument(); } private static void RemoveElementsForDocumentComparison(WordprocessingDocument doc) { - OpenXmlPart part = doc.ExtendedFilePropertiesPart; + OpenXmlPart? part = doc.ExtendedFilePropertiesPart; if (part != null) { - XDocument appPropsXDoc = part.GetXDocument(); + var appPropsXDoc = part.GetXDocument(); appPropsXDoc.Descendants(EP.TotalTime).Remove(); part.PutXDocument(); } @@ -106,23 +126,23 @@ private static void RemoveElementsForDocumentComparison(WordprocessingDocument d part = doc.CoreFilePropertiesPart; if (part != null) { - XDocument corePropsXDoc = part.GetXDocument(); + var corePropsXDoc = part.GetXDocument(); corePropsXDoc.Descendants(CP.revision).Remove(); corePropsXDoc.Descendants(DCTERMS.created).Remove(); corePropsXDoc.Descendants(DCTERMS.modified).Remove(); part.PutXDocument(); } - XDocument mainXDoc = doc.MainDocumentPart.GetXDocument(); - List bookmarkStart = mainXDoc + var mainXDoc = doc.MainDocumentPart.GetXDocument(); + var bookmarkStart = mainXDoc .Descendants(W.bookmarkStart) - .Where(b => (string) b.Attribute(W.name) == "_GoBack") + .Where(b => (string)b.Attribute(W.name) == "_GoBack") .ToList(); - foreach (XElement item in bookmarkStart) + foreach (var item in bookmarkStart) { - IEnumerable bookmarkEnd = mainXDoc + var bookmarkEnd = mainXDoc .Descendants(W.bookmarkEnd) - .Where(be => (int) be.Attribute(W.id) == (int) item.Attribute(W.id)); + .Where(be => (int)be.Attribute(W.id) == (int)item.Attribute(W.id)); bookmarkEnd.Remove(); } @@ -132,21 +152,21 @@ private static void RemoveElementsForDocumentComparison(WordprocessingDocument d public static XElement MergeAdjacentSuperfluousRuns(XElement element) { - return (XElement) MergeAdjacentRunsTransform(element); + return (XElement)MergeAdjacentRunsTransform(element); } public static XElement TransformElementToSingleCharacterRuns(XElement element) { - return (XElement) SingleCharacterRunTransform(element); + return (XElement)SingleCharacterRunTransform(element); } public static void TransformPartToSingleCharacterRuns(OpenXmlPart part) { // After transforming to single character runs, Rsid info will be invalid, so // remove from the part. - XDocument xDoc = part.GetXDocument(); - var newRoot = (XElement) RemoveRsidTransform(xDoc.Root); - newRoot = (XElement) SingleCharacterRunTransform(newRoot); + var xDoc = part.GetXDocument(); + var newRoot = RemoveRsidTransform(xDoc.Root) as XElement; + newRoot = (XElement)SingleCharacterRunTransform(newRoot); xDoc.Elements().First().ReplaceWith(newRoot); part.PutXDocument(); } @@ -154,36 +174,43 @@ public static void TransformPartToSingleCharacterRuns(OpenXmlPart part) public static void TransformToSingleCharacterRuns(WordprocessingDocument doc) { if (RevisionAccepter.HasTrackedRevisions(doc)) + { throw new OpenXmlPowerToolsException( "Transforming a document to single character runs is not supported for " + "a document with tracked revisions."); + } - foreach (OpenXmlPart part in doc.ContentParts()) + foreach (var part in doc.ContentParts()) + { TransformPartToSingleCharacterRuns(part); + } } private static object RemoveCustomXmlAndContentControlsTransform( XNode node, SimplifyMarkupSettings simplifyMarkupSettings) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (simplifyMarkupSettings.RemoveSmartTags && element.Name == W.smartTag) + { return element .Elements() .Select(e => RemoveCustomXmlAndContentControlsTransform(e, simplifyMarkupSettings)); + } if (simplifyMarkupSettings.RemoveContentControls && element.Name == W.sdt) + { return element .Elements(W.sdtContent) .Elements() .Select(e => RemoveCustomXmlAndContentControlsTransform(e, simplifyMarkupSettings)); + } return new XElement(element.Name, element.Attributes(), @@ -193,13 +220,17 @@ private static object RemoveCustomXmlAndContentControlsTransform( return node; } - private static object RemoveRsidTransform(XNode node) + private static object? RemoveRsidTransform(XNode node) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (element.Name == W.rsid) + { return null; + } return new XElement(element.Name, element @@ -217,26 +248,31 @@ private static object RemoveRsidTransform(XNode node) private static object MergeAdjacentRunsTransform(XNode node) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (element.Name == W.p) + { return WordprocessingMLUtil.CoalesceAdjacentRunsWithIdenticalFormatting(element); + } return new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => MergeAdjacentRunsTransform(n))); } - private static object RemoveEmptyRunsAndRunPropertiesTransform( + private static object? RemoveEmptyRunsAndRunPropertiesTransform( XNode node) { - var element = node as XElement; - if (element != null) + if (node is XElement element) { if (((element.Name == W.r) || (element.Name == W.rPr) || (element.Name == W.pPr)) && !element.Elements().Any()) + { return null; + } return new XElement(element.Name, element.Attributes(), @@ -249,25 +285,28 @@ private static object RemoveEmptyRunsAndRunPropertiesTransform( private static object MergeAdjacentInstrText( XNode node) { - var element = node as XElement; - if (element != null) + if (node is XElement element) { if ((element.Name == W.r) && element.Elements(W.instrText).Any()) { - IEnumerable> grouped = + var grouped = element.Elements().GroupAdjacent(e => e.Name == W.instrText); return new XElement(W.r, grouped.Select(g => { - if (g.Key == false) - return (object) g; + if (!g.Key) + { + return (object)g; + } // If .doc files are converted to .docx by the Binary to Open XML Translator, // the w:instrText elements might be empty, in which case newInstrText would // be an empty string. - string newInstrText = g.Select(i => (string) i).StringConcatenate(); + var newInstrText = g.Select(i => (string)i).StringConcatenate(); if (string.IsNullOrEmpty(newInstrText)) + { return new XElement(W.instrText); + } return new XElement(W.instrText, (newInstrText[0] == ' ') || (newInstrText[newInstrText.Length - 1] == ' ') @@ -294,82 +333,112 @@ private static object MergeAdjacentInstrText( // - collapse fldSimple // - remove fldSimple, fldData, fldChar, instrText. - private static object SimplifyMarkupTransform( + private static object? SimplifyMarkupTransform( XNode node, SimplifyMarkupSettings settings, SimplifyMarkupParameters parameters) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (settings.RemovePermissions && ((element.Name == W.permEnd) || (element.Name == W.permStart))) + { return null; + } if (settings.RemoveProof && ((element.Name == W.proofErr) || (element.Name == W.noProof))) + { return null; + } if (settings.RemoveSoftHyphens && (element.Name == W.softHyphen)) + { return null; + } if (settings.RemoveLastRenderedPageBreak && (element.Name == W.lastRenderedPageBreak)) + { return null; + } if (settings.RemoveBookmarks && ((element.Name == W.bookmarkStart) || (element.Name == W.bookmarkEnd))) + { return null; + } if (settings.RemoveGoBackBookmark && - (((element.Name == W.bookmarkStart) && ((int) element.Attribute(W.id) == parameters.GoBackId)) || - ((element.Name == W.bookmarkEnd) && ((int) element.Attribute(W.id) == parameters.GoBackId)))) + (((element.Name == W.bookmarkStart) && ((int)element.Attribute(W.id) == parameters.GoBackId)) || + ((element.Name == W.bookmarkEnd) && ((int)element.Attribute(W.id) == parameters.GoBackId)))) + { return null; + } if (settings.RemoveWebHidden && (element.Name == W.webHidden)) + { return null; + } if (settings.ReplaceTabsWithSpaces && - (element.Name == W.tab) && + (element.Name == W.tab) && (element.Parent != null && element.Parent.Name == W.r)) + { return new XElement(W.t, new XAttribute(XNamespace.Xml + "space", "preserve"), " "); + } if (settings.RemoveComments && ((element.Name == W.commentRangeStart) || (element.Name == W.commentRangeEnd) || (element.Name == W.commentReference) || (element.Name == W.annotationRef))) + { return null; + } if (settings.RemoveComments && (element.Name == W.rStyle) && (element.Attribute(W.val).Value == "CommentReference")) + { return null; + } if (settings.RemoveEndAndFootNotes && ((element.Name == W.endnoteReference) || (element.Name == W.footnoteReference))) + { return null; + } if (settings.RemoveFieldCodes) { if (element.Name == W.fldSimple) + { return element.Elements().Select(e => SimplifyMarkupTransform(e, settings, parameters)); + } if ((element.Name == W.fldData) || (element.Name == W.fldChar) || (element.Name == W.instrText)) + { return null; + } } if (settings.RemoveHyperlinks && (element.Name == W.hyperlink)) + { return element.Elements(); + } return new XElement(element.Name, element.Attributes(), @@ -394,21 +463,14 @@ private static XDocument Normalize(XDocument source, XmlSchemaSet schema) // children of XDocument. Only white space text nodes are allowed as // children of a document, so we can remove all text nodes. if (n is XComment || n is XProcessingInstruction || n is XText) + { return null; + } - var e = n as XElement; - return e != null ? NormalizeElement(e, havePsvi) : n; + return n is XElement e ? NormalizeElement(e, havePsvi) : n; })); } - // TODO: Check whether this can be removed. - //private static bool DeepEqualsWithNormalization(XDocument doc1, XDocument doc2, XmlSchemaSet schemaSet) - //{ - // XDocument d1 = Normalize(doc1, schemaSet); - // XDocument d2 = Normalize(doc2, schemaSet); - // return XNode.DeepEquals(d1, d2); - //} - private static IEnumerable NormalizeAttributes(XElement element, bool havePsvi) { return element.Attributes() @@ -421,26 +483,31 @@ private static IEnumerable NormalizeAttributes(XElement element, boo { if (havePsvi) { - IXmlSchemaInfo schemaInfo = a.GetSchemaInfo(); - XmlSchemaType schemaType = schemaInfo != null ? schemaInfo.SchemaType : null; - XmlTypeCode? typeCode = schemaType != null ? schemaType.TypeCode : (XmlTypeCode?) null; + var schemaInfo = a.GetSchemaInfo(); + var schemaType = schemaInfo?.SchemaType; + var typeCode = schemaType != null ? schemaType.TypeCode : (XmlTypeCode?)null; switch (typeCode) { case XmlTypeCode.Boolean: - return new XAttribute(a.Name, (bool) a); + return new XAttribute(a.Name, (bool)a); + case XmlTypeCode.DateTime: - return new XAttribute(a.Name, (DateTime) a); + return new XAttribute(a.Name, (DateTime)a); + case XmlTypeCode.Decimal: - return new XAttribute(a.Name, (decimal) a); + return new XAttribute(a.Name, (decimal)a); + case XmlTypeCode.Double: - return new XAttribute(a.Name, (double) a); + return new XAttribute(a.Name, (double)a); + case XmlTypeCode.Float: - return new XAttribute(a.Name, (float) a); + return new XAttribute(a.Name, (float)a); + case XmlTypeCode.HexBinary: case XmlTypeCode.Language: return new XAttribute(a.Name, - ((string) a).ToLower()); + ((string)a).ToLower()); } } @@ -448,15 +515,18 @@ private static IEnumerable NormalizeAttributes(XElement element, boo }); } - private static XNode NormalizeNode(XNode node, bool havePsvi) + private static XNode? NormalizeNode(XNode node, bool havePsvi) { // trim comments and processing instructions from normalized tree if (node is XComment || node is XProcessingInstruction) + { return null; + } - var e = node as XElement; - if (e != null) + if (node is XElement e) + { return NormalizeElement(e, havePsvi); + } // Only thing left is XCData and XText, so clone them return node; @@ -466,37 +536,43 @@ private static XElement NormalizeElement(XElement element, bool havePsvi) { if (havePsvi) { - IXmlSchemaInfo schemaInfo = element.GetSchemaInfo(); - XmlSchemaType schemaType = schemaInfo != null ? schemaInfo.SchemaType : null; - XmlTypeCode? typeCode = schemaType != null ? schemaType.TypeCode : (XmlTypeCode?) null; + var schemaInfo = element.GetSchemaInfo(); + var schemaType = schemaInfo?.SchemaType; + var typeCode = schemaType != null ? schemaType.TypeCode : (XmlTypeCode?)null; switch (typeCode) { case XmlTypeCode.Boolean: return new XElement(element.Name, NormalizeAttributes(element, true), - (bool) element); + (bool)element); + case XmlTypeCode.DateTime: return new XElement(element.Name, NormalizeAttributes(element, true), - (DateTime) element); + (DateTime)element); + case XmlTypeCode.Decimal: return new XElement(element.Name, NormalizeAttributes(element, true), - (decimal) element); + (decimal)element); + case XmlTypeCode.Double: return new XElement(element.Name, NormalizeAttributes(element, true), - (double) element); + (double)element); + case XmlTypeCode.Float: return new XElement(element.Name, NormalizeAttributes(element, true), - (float) element); + (float)element); + case XmlTypeCode.HexBinary: case XmlTypeCode.Language: return new XElement(element.Name, NormalizeAttributes(element, true), - ((string) element).ToLower()); + ((string)element).ToLower()); + default: return new XElement(element.Name, NormalizeAttributes(element, true), @@ -514,29 +590,35 @@ private static void SimplifyMarkupForPart(OpenXmlPart part, SimplifyMarkupSettin var parameters = new SimplifyMarkupParameters(); if (part.ContentType == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml") { - var doc = (WordprocessingDocument) part.OpenXmlPackage; + var doc = (WordprocessingDocument)part.OpenXmlPackage; if (settings.RemoveGoBackBookmark) { - XElement goBackBookmark = doc + var goBackBookmark = doc .MainDocumentPart .GetXDocument() .Descendants(W.bookmarkStart) - .FirstOrDefault(bm => (string) bm.Attribute(W.name) == "_GoBack"); + .FirstOrDefault(bm => (string)bm.Attribute(W.name) == "_GoBack"); if (goBackBookmark != null) - parameters.GoBackId = (int) goBackBookmark.Attribute(W.id); + { + parameters.GoBackId = (int)goBackBookmark.Attribute(W.id); + } } } - XDocument xdoc = part.GetXDocument(); - XElement newRoot = xdoc.Root; + var xdoc = part.GetXDocument(); + var newRoot = xdoc.Root; // Need to do this first to enable simplifying hyperlinks. if (settings.RemoveContentControls || settings.RemoveSmartTags) - newRoot = (XElement) RemoveCustomXmlAndContentControlsTransform(newRoot, settings); + { + newRoot = (XElement)RemoveCustomXmlAndContentControlsTransform(newRoot, settings); + } // This may touch many elements, so needs to be its own transform. if (settings.RemoveRsidInfo) - newRoot = (XElement) RemoveRsidTransform(newRoot); + { + newRoot = RemoveRsidTransform(newRoot) as XElement; + } var prevNewRoot = new XDocument(newRoot); while (true) @@ -551,22 +633,26 @@ private static void SimplifyMarkupForPart(OpenXmlPart part, SimplifyMarkupSettin settings.RemoveWebHidden || settings.RemoveGoBackBookmark || settings.RemoveHyperlinks) - newRoot = (XElement) SimplifyMarkupTransform(newRoot, settings, parameters); + { + newRoot = SimplifyMarkupTransform(newRoot, settings, parameters) as XElement; + } // Remove runs and run properties that have become empty due to previous transforms. - newRoot = (XElement) RemoveEmptyRunsAndRunPropertiesTransform(newRoot); + newRoot = RemoveEmptyRunsAndRunPropertiesTransform(newRoot) as XElement; // Merge adjacent runs that have identical run properties. - newRoot = (XElement) MergeAdjacentRunsTransform(newRoot); + newRoot = (XElement)MergeAdjacentRunsTransform(newRoot); // Merge adjacent instrText elements. - newRoot = (XElement) MergeAdjacentInstrText(newRoot); + newRoot = (XElement)MergeAdjacentInstrText(newRoot); // Separate run children into separate runs - newRoot = (XElement) SeparateRunChildrenIntoSeparateRuns(newRoot); + newRoot = (XElement)SeparateRunChildrenIntoSeparateRuns(newRoot); if (XNode.DeepEquals(prevNewRoot.Root, newRoot)) + { break; + } prevNewRoot = new XDocument(newRoot); } @@ -595,12 +681,18 @@ private static void SimplifyMarkupForPart(OpenXmlPart part, SimplifyMarkupSettin new XAttribute(MC.Ignorable, "w14 wp14 w15 w16se"), }; - XDocument newXDoc = Normalize(new XDocument(newRoot), null); + var newXDoc = Normalize(new XDocument(newRoot), null); newRoot = newXDoc.Root; if (newRoot != null) - foreach (XAttribute nsAttr in nsAttrs) + { + foreach (var nsAttr in nsAttrs) + { if (newRoot.Attribute(nsAttr.Name) == null) + { newRoot.Add(nsAttr); + } + } + } part.PutXDocument(newXDoc); } @@ -612,13 +704,15 @@ private static void SimplifyMarkupForPart(OpenXmlPart part, SimplifyMarkupSettin private static object SeparateRunChildrenIntoSeparateRuns(XNode node) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (element.Name == W.r) { - IEnumerable runChildren = element.Elements().Where(e => e.Name != W.rPr); - XElement rPr = element.Element(W.rPr); + var runChildren = element.Elements().Where(e => e.Name != W.rPr); + var rPr = element.Element(W.rPr); return runChildren.Select(rc => new XElement(W.r, rPr, rc)); } @@ -627,12 +721,15 @@ private static object SeparateRunChildrenIntoSeparateRuns(XNode node) element.Nodes().Select(n => SeparateRunChildrenIntoSeparateRuns(n))); } - private static object SingleCharacterRunTransform(XNode node) + private static object SingleCharacterRunTransform(XNode? node) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (element.Name == W.r) + { return element.Elements() .Where(e => e.Name != W.rPr) .GroupAdjacent(sr => sr.Name == W.t) @@ -640,7 +737,7 @@ private static object SingleCharacterRunTransform(XNode node) { if (g.Key) { - string s = g.Select(t => (string) t).StringConcatenate(); + var s = g.Select(t => (string)t).StringConcatenate(); return s.Select(c => new XElement(W.r, element.Elements(W.rPr), @@ -656,6 +753,7 @@ private static object SingleCharacterRunTransform(XNode node) sr.Attributes(), sr.Nodes().Select(n => SingleCharacterRunTransform(n))))); }); + } return new XElement(element.Name, element.Attributes(), @@ -675,6 +773,14 @@ public class InternalException : Exception public InternalException(string message) : base(message) { } + + public InternalException() + { + } + + public InternalException(string message, Exception innerException) : base(message, innerException) + { + } } public class InvalidSettingsException : Exception @@ -682,6 +788,14 @@ public class InvalidSettingsException : Exception public InvalidSettingsException(string message) : base(message) { } + + public InvalidSettingsException() + { + } + + public InvalidSettingsException(string message, Exception innerException) : base(message, innerException) + { + } } private class SimplifyMarkupParameters @@ -689,4 +803,4 @@ private class SimplifyMarkupParameters public int? GoBackId { get; set; } } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/MetricsGetter.cs b/OpenXmlPowerTools/MetricsGetter.cs index 86445ef9..f6a40086 100644 --- a/OpenXmlPowerTools/MetricsGetter.cs +++ b/OpenXmlPowerTools/MetricsGetter.cs @@ -1,54 +1,48 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Validation; using System; using System.Collections.Generic; -using System.Drawing; +using System.Globalization; using System.IO; -using System.IO.Packaging; using System.Linq; using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using System.Globalization; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class MetricsGetterSettings { - public bool IncludeTextInContentControls; - public bool IncludeXlsxTableCellData; - public bool RetrieveNamespaceList; - public bool RetrieveContentTypeList; + public bool IncludeTextInContentControls { get; set; } + public bool IncludeXlsxTableCellData { get; set; } + public bool RetrieveNamespaceList { get; set; } + public bool RetrieveContentTypeList { get; set; } } public class MetricsGetter { - private static Lazy Graphics { get; } = new Lazy(() => - { - Image image = new Bitmap(1, 1); - return System.Drawing.Graphics.FromImage(image); - }); + private const string UriString = "http://broken-link/"; - public static XElement GetMetrics(string fileName, MetricsGetterSettings settings) + public static XElement? GetMetrics(string fileName, MetricsGetterSettings settings) { - FileInfo fi = new FileInfo(fileName); + var fi = new FileInfo(fileName); if (!fi.Exists) + { throw new FileNotFoundException("{0} does not exist.", fi.FullName); + } + if (Util.IsWordprocessingML(fi.Extension)) { - WmlDocument wmlDoc = new WmlDocument(fi.FullName, true); + var wmlDoc = new WmlDocument(fi.FullName, true); return GetDocxMetrics(wmlDoc, settings); } if (Util.IsSpreadsheetML(fi.Extension)) { - SmlDocument smlDoc = new SmlDocument(fi.FullName, true); + var smlDoc = new SmlDocument(fi.FullName, true); return GetXlsxMetrics(smlDoc, settings); } if (Util.IsPresentationML(fi.Extension)) { - PmlDocument pmlDoc = new PmlDocument(fi.FullName, true); + var pmlDoc = new PmlDocument(fi.FullName, true); return GetPptxMetrics(pmlDoc, settings); } return null; @@ -58,46 +52,50 @@ public static XElement GetDocxMetrics(WmlDocument wmlDoc, MetricsGetterSettings { try { - using (MemoryStream ms = new MemoryStream()) + using var ms = new MemoryStream(); + ms.Write(wmlDoc.DocumentByteArray, 0, wmlDoc.DocumentByteArray.Length); + using var document = WordprocessingDocument.Open(ms, true); + var hasTrackedRevisions = RevisionAccepter.HasTrackedRevisions(document); + if (hasTrackedRevisions) { - ms.Write(wmlDoc.DocumentByteArray, 0, wmlDoc.DocumentByteArray.Length); - using (WordprocessingDocument document = WordprocessingDocument.Open(ms, true)) - { - bool hasTrackedRevisions = RevisionAccepter.HasTrackedRevisions(document); - if (hasTrackedRevisions) - RevisionAccepter.AcceptRevisions(document); - XElement metrics1 = GetWmlMetrics(wmlDoc.FileName, false, document, settings); - if (hasTrackedRevisions) - metrics1.Add(new XElement(H.RevisionTracking, new XAttribute(H.Val, true))); - return metrics1; - } + RevisionAccepter.AcceptRevisions(document); + } + + var metrics1 = GetWmlMetrics(wmlDoc.FileName, false, document, settings); + if (hasTrackedRevisions) + { + metrics1.Add(new XElement(H.RevisionTracking, new XAttribute(H.Val, true))); } + + return metrics1; } catch (OpenXmlPowerToolsException e) { if (e.ToString().Contains("Invalid Hyperlink")) { - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { ms.Write(wmlDoc.DocumentByteArray, 0, wmlDoc.DocumentByteArray.Length); -#if !NET35 UriFixer.FixInvalidUri(ms, brokenUri => FixUri(brokenUri)); -#endif wmlDoc = new WmlDocument("dummy.docx", ms.ToArray()); } - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { ms.Write(wmlDoc.DocumentByteArray, 0, wmlDoc.DocumentByteArray.Length); - using (WordprocessingDocument document = WordprocessingDocument.Open(ms, true)) + using var document = WordprocessingDocument.Open(ms, true); + var hasTrackedRevisions = RevisionAccepter.HasTrackedRevisions(document); + if (hasTrackedRevisions) { - bool hasTrackedRevisions = RevisionAccepter.HasTrackedRevisions(document); - if (hasTrackedRevisions) - RevisionAccepter.AcceptRevisions(document); - XElement metrics2 = GetWmlMetrics(wmlDoc.FileName, true, document, settings); - if (hasTrackedRevisions) - metrics2.Add(new XElement(H.RevisionTracking, new XAttribute(H.Val, true))); - return metrics2; + RevisionAccepter.AcceptRevisions(document); } + + var metrics2 = GetWmlMetrics(wmlDoc.FileName, true, document, settings); + if (hasTrackedRevisions) + { + metrics2.Add(new XElement(H.RevisionTracking, new XAttribute(H.Val, true))); + } + + return metrics2; } } } @@ -108,16 +106,15 @@ public static XElement GetDocxMetrics(WmlDocument wmlDoc, MetricsGetterSettings return metrics; } - private static int _getTextWidth(FontFamily ff, FontStyle fs, decimal sz, string text) + private static int _getTextWidth(SixLabors.Fonts.FontFamily ff, SixLabors.Fonts.FontStyle fs, decimal sz, string text) { try { - using (var f = new Font(ff, (float)sz / 2f, fs)) - { - var proposedSize = new Size(int.MaxValue, int.MaxValue); - var sf = Graphics.Value.MeasureString(text, f, proposedSize); - return (int) sf.Width; - } + var font = new SixLabors.Fonts.Font(ff, (float)sz / 2f, fs); + var textOptions = new SixLabors.Fonts.TextOptions(font); + var size = SixLabors.Fonts.TextMeasurer.MeasureSize(text, textOptions); + + return (int)size.Width; } catch { @@ -125,7 +122,7 @@ private static int _getTextWidth(FontFamily ff, FontStyle fs, decimal sz, string } } - public static int GetTextWidth(FontFamily ff, FontStyle fs, decimal sz, string text) + public static int GetTextWidth(SixLabors.Fonts.FontFamily ff, SixLabors.Fonts.FontStyle fs, decimal sz, string text) { try { @@ -135,21 +132,20 @@ public static int GetTextWidth(FontFamily ff, FontStyle fs, decimal sz, string t { try { - const FontStyle fs2 = FontStyle.Regular; + const SixLabors.Fonts.FontStyle fs2 = SixLabors.Fonts.FontStyle.Regular; return _getTextWidth(ff, fs2, sz, text); } catch (ArgumentException) { - const FontStyle fs2 = FontStyle.Bold; + const SixLabors.Fonts.FontStyle fs2 = SixLabors.Fonts.FontStyle.Bold; try { return _getTextWidth(ff, fs2, sz, text); } catch (ArgumentException) { - // if both regular and bold fail, then get metrics for Times New Roman - // use the original FontStyle (in fs) - var ff2 = new FontFamily("Times New Roman"); + // if both regular and bold fail, then get metrics for Times New Roman the original FontStyle (in fs) + var ff2 = SixLabors.Fonts.SystemFonts.Families.Single(font => font.Name == "Times New Roman"); return _getTextWidth(ff2, fs, sz, text); } } @@ -163,7 +159,7 @@ public static int GetTextWidth(FontFamily ff, FontStyle fs, decimal sz, string t private static Uri FixUri(string brokenUri) { - return new Uri("http://broken-link/"); + return new Uri(UriString); } private static XElement GetWmlMetrics(string fileName, bool invalidHyperlink, WordprocessingDocument wDoc, MetricsGetterSettings settings) @@ -174,7 +170,10 @@ private static XElement GetWmlMetrics(string fileName, bool invalidHyperlink, Wo return GetMetricsForWmlPart(part, settings); })); if (!parts.HasElements) + { parts = null; + } + var metrics = new XElement(H.Metrics, new XAttribute(H.FileName, fileName), new XAttribute(H.FileType, "WordprocessingML"), @@ -189,9 +188,7 @@ private static XElement GetWmlMetrics(string fileName, bool invalidHyperlink, Wo private static XElement RetrieveContentTypeList(OpenXmlPackage oxPkg) { - Package pkg = oxPkg.Package; - - var nonRelationshipParts = pkg.GetParts().Cast().Where(p => p.ContentType != "application/vnd.openxmlformats-package.relationships+xml"); + var nonRelationshipParts = oxPkg.GetAllParts().Where(p => p.ContentType != "application/vnd.openxmlformats-package.relationships+xml"); var contentTypes = nonRelationshipParts .Select(p => p.ContentType) .OrderBy(t => t) @@ -203,38 +200,35 @@ private static XElement RetrieveContentTypeList(OpenXmlPackage oxPkg) private static XElement RetrieveNamespaceList(OpenXmlPackage oxPkg) { - Package pkg = oxPkg.Package; - - var nonRelationshipParts = pkg.GetParts().Cast().Where(p => p.ContentType != "application/vnd.openxmlformats-package.relationships+xml"); + var nonRelationshipParts = oxPkg.GetAllParts().Where(p => p.ContentType != "application/vnd.openxmlformats-package.relationships+xml"); var xmlParts = nonRelationshipParts .Where(p => p.ContentType.ToLower().EndsWith("xml")); var uniqueNamespaces = new HashSet(); foreach (var xp in xmlParts) { - using (Stream st = xp.GetStream()) + using var st = xp.GetStream(); + try { - try - { - XDocument xdoc = XDocument.Load(st); - var namespaces = xdoc - .Descendants() - .Attributes() - .Where(a => a.IsNamespaceDeclaration) - .Select(a => string.Format("{0}|{1}", a.Name.LocalName, a.Value)) - .OrderBy(t => t) - .Distinct() - .ToList(); - foreach (var item in namespaces) - uniqueNamespaces.Add(item); - } - // if catch exception, forget about it. Just trying to get a most complete survey possible of all namespaces in all documents. - // if caught exception, chances are the document is bad anyway. - catch (Exception) + var xdoc = XDocument.Load(st); + var namespaces = xdoc + .Descendants() + .Attributes() + .Where(a => a.IsNamespaceDeclaration) + .Select(a => string.Format("{0}|{1}", a.Name.LocalName, a.Value)) + .OrderBy(t => t) + .Distinct() + .ToList(); + foreach (var item in namespaces) { - continue; + uniqueNamespaces.Add(item); } } + catch (Exception) + { + // if catch exception, forget about it. Just trying to get a most complete survey possible of all namespaces in all documents. If caught exception, chances are the document is bad anyway. + continue; + } } var xe = new XElement(H.Namespaces, uniqueNamespaces.OrderBy(t => t).Select(n => @@ -249,78 +243,115 @@ private static XElement RetrieveNamespaceList(OpenXmlPackage oxPkg) private static List GetMiscWmlMetrics(WordprocessingDocument document, bool invalidHyperlink) { - List metrics = new List(); - List notes = new List(); - Dictionary elementCountDictionary = new Dictionary(); + var metrics = new List(); + var notes = new List(); + var elementCountDictionary = new Dictionary(); if (invalidHyperlink) + { metrics.Add(new XElement(H.InvalidHyperlink, new XAttribute(H.Val, invalidHyperlink))); + } - bool valid = ValidateWordprocessingDocument(document, metrics, notes, elementCountDictionary); + var valid = ValidateWordprocessingDocument(document, metrics, notes, elementCountDictionary); if (invalidHyperlink) + { valid = false; + } return metrics; } private static bool ValidateWordprocessingDocument(WordprocessingDocument wDoc, List metrics, List notes, Dictionary metricCountDictionary) { - bool valid = ValidateAgainstSpecificVersion(wDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2007, H.SdkValidationError2007); + var valid = ValidateAgainstSpecificVersion(wDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2007, H.SdkValidationError2007); valid |= ValidateAgainstSpecificVersion(wDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2010, H.SdkValidationError2010); -#if !NET35 valid |= ValidateAgainstSpecificVersion(wDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2013, H.SdkValidationError2013); -#endif - int elementCount = 0; - int paragraphCount = 0; - int textCount = 0; + var elementCount = 0; + var paragraphCount = 0; + var textCount = 0; foreach (var part in wDoc.ContentParts()) { - XDocument xDoc = part.GetXDocument(); + var xDoc = part.GetXDocument(); foreach (var e in xDoc.Descendants()) { if (e.Name == W.txbxContent) + { IncrementMetric(metricCountDictionary, H.TextBox); + } else if (e.Name == W.sdt) + { IncrementMetric(metricCountDictionary, H.ContentControl); + } else if (e.Name == W.customXml) + { IncrementMetric(metricCountDictionary, H.CustomXmlMarkup); + } else if (e.Name == W.fldChar) + { IncrementMetric(metricCountDictionary, H.ComplexField); + } else if (e.Name == W.fldSimple) + { IncrementMetric(metricCountDictionary, H.SimpleField); + } else if (e.Name == W.altChunk) + { IncrementMetric(metricCountDictionary, H.AltChunk); + } else if (e.Name == W.tbl) + { IncrementMetric(metricCountDictionary, H.Table); + } else if (e.Name == W.hyperlink) + { IncrementMetric(metricCountDictionary, H.Hyperlink); + } else if (e.Name == W.framePr) + { IncrementMetric(metricCountDictionary, H.LegacyFrame); + } else if (e.Name == W.control) + { IncrementMetric(metricCountDictionary, H.ActiveX); + } else if (e.Name == W.subDoc) + { IncrementMetric(metricCountDictionary, H.SubDocument); + } else if (e.Name == VML.imagedata || e.Name == VML.fill || e.Name == VML.stroke || e.Name == A.blip) { var relId = (string)e.Attribute(R.embed); if (relId != null) + { ValidateImageExists(part, relId, metricCountDictionary); + } + relId = (string)e.Attribute(R.pict); if (relId != null) + { ValidateImageExists(part, relId, metricCountDictionary); + } + relId = (string)e.Attribute(R.id); if (relId != null) + { ValidateImageExists(part, relId, metricCountDictionary); + } } if (part.Uri == wDoc.MainDocumentPart.Uri) { elementCount++; if (e.Name == W.p) + { paragraphCount++; + } + if (e.Name == W.t) + { textCount += ((string)e).Length; + } } } } @@ -332,33 +363,42 @@ private static bool ValidateWordprocessingDocument(WordprocessingDocument wDoc, } metrics.Add(new XElement(H.ElementCount, new XAttribute(H.Val, elementCount))); - metrics.Add(new XElement(H.AverageParagraphLength, new XAttribute(H.Val, (int)((double)textCount / (double)paragraphCount)))); + metrics.Add(new XElement(H.AverageParagraphLength, new XAttribute(H.Val, (int)(textCount / (double)paragraphCount)))); if (wDoc.GetAllParts().Any(part => part.ContentType == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) + { metrics.Add(new XElement(H.EmbeddedXlsx, new XAttribute(H.Val, true))); + } NumberingFormatListAssembly(wDoc, metrics); - XDocument wxDoc = wDoc.MainDocumentPart.GetXDocument(); + var wxDoc = wDoc.MainDocumentPart.GetXDocument(); foreach (var d in wxDoc.Descendants()) { if (d.Name == W.saveThroughXslt) { - string rid = (string)d.Attribute(R.id); + var rid = (string)d.Attribute(R.id); var tempExternalRelationship = wDoc .MainDocumentPart .DocumentSettingsPart .ExternalRelationships .FirstOrDefault(h => h.Id == rid); if (tempExternalRelationship == null) + { metrics.Add(new XElement(H.InvalidSaveThroughXslt, new XAttribute(H.Val, true))); + } + valid = false; } else if (d.Name == W.trackRevisions) + { metrics.Add(new XElement(H.TrackRevisionsEnabled, new XAttribute(H.Val, true))); + } else if (d.Name == W.documentProtection) + { metrics.Add(new XElement(H.DocumentProtection, new XAttribute(H.Val, true))); + } } FontAndCharSetAnalysis(wDoc, metrics, notes); @@ -368,21 +408,29 @@ private static bool ValidateWordprocessingDocument(WordprocessingDocument wDoc, private static bool ValidateAgainstSpecificVersion(WordprocessingDocument wDoc, List metrics, DocumentFormat.OpenXml.FileFormatVersions versionToValidateAgainst, XName versionSpecificMetricName) { - OpenXmlValidator validator = new OpenXmlValidator(versionToValidateAgainst); + var validator = new OpenXmlValidator(versionToValidateAgainst); var errors = validator.Validate(wDoc); - bool valid = errors.Count() == 0; + var valid = errors.Count() == 0; if (!valid) { if (!metrics.Any(e => e.Name == H.SdkValidationError)) + { metrics.Add(new XElement(H.SdkValidationError, new XAttribute(H.Val, true))); + } + metrics.Add(new XElement(versionSpecificMetricName, new XAttribute(H.Val, true), errors.Take(3).Select(err => { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); if (err.Description.Length > 300) + { sb.Append(PtUtils.MakeValidXml(err.Description.Substring(0, 300) + " ... elided ...") + Environment.NewLine); + } else + { sb.Append(PtUtils.MakeValidXml(err.Description) + Environment.NewLine); + } + sb.Append(" in part " + PtUtils.MakeValidXml(err.Part.Uri.ToString()) + Environment.NewLine); sb.Append(" at " + PtUtils.MakeValidXml(err.Path.XPath) + Environment.NewLine); return sb.ToString(); @@ -393,21 +441,29 @@ private static bool ValidateAgainstSpecificVersion(WordprocessingDocument wDoc, private static bool ValidateAgainstSpecificVersion(SpreadsheetDocument sDoc, List metrics, DocumentFormat.OpenXml.FileFormatVersions versionToValidateAgainst, XName versionSpecificMetricName) { - OpenXmlValidator validator = new OpenXmlValidator(versionToValidateAgainst); + var validator = new OpenXmlValidator(versionToValidateAgainst); var errors = validator.Validate(sDoc); - bool valid = errors.Count() == 0; + var valid = errors.Count() == 0; if (!valid) { if (!metrics.Any(e => e.Name == H.SdkValidationError)) + { metrics.Add(new XElement(H.SdkValidationError, new XAttribute(H.Val, true))); + } + metrics.Add(new XElement(versionSpecificMetricName, new XAttribute(H.Val, true), errors.Take(3).Select(err => { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); if (err.Description.Length > 300) + { sb.Append(PtUtils.MakeValidXml(err.Description.Substring(0, 300) + " ... elided ...") + Environment.NewLine); + } else + { sb.Append(PtUtils.MakeValidXml(err.Description) + Environment.NewLine); + } + sb.Append(" in part " + PtUtils.MakeValidXml(err.Part.Uri.ToString()) + Environment.NewLine); sb.Append(" at " + PtUtils.MakeValidXml(err.Path.XPath) + Environment.NewLine); return sb.ToString(); @@ -418,21 +474,29 @@ private static bool ValidateAgainstSpecificVersion(SpreadsheetDocument sDoc, Lis private static bool ValidateAgainstSpecificVersion(PresentationDocument pDoc, List metrics, DocumentFormat.OpenXml.FileFormatVersions versionToValidateAgainst, XName versionSpecificMetricName) { - OpenXmlValidator validator = new OpenXmlValidator(versionToValidateAgainst); + var validator = new OpenXmlValidator(versionToValidateAgainst); var errors = validator.Validate(pDoc); - bool valid = errors.Count() == 0; + var valid = errors.Count() == 0; if (!valid) { if (!metrics.Any(e => e.Name == H.SdkValidationError)) + { metrics.Add(new XElement(H.SdkValidationError, new XAttribute(H.Val, true))); + } + metrics.Add(new XElement(versionSpecificMetricName, new XAttribute(H.Val, true), errors.Take(3).Select(err => { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); if (err.Description.Length > 300) + { sb.Append(PtUtils.MakeValidXml(err.Description.Substring(0, 300) + " ... elided ...") + Environment.NewLine); + } else + { sb.Append(PtUtils.MakeValidXml(err.Description) + Environment.NewLine); + } + sb.Append(" in part " + PtUtils.MakeValidXml(err.Part.Uri.ToString()) + Environment.NewLine); sb.Append(" at " + PtUtils.MakeValidXml(err.Path.XPath) + Environment.NewLine); return sb.ToString(); @@ -444,22 +508,27 @@ private static bool ValidateAgainstSpecificVersion(PresentationDocument pDoc, Li private static void IncrementMetric(Dictionary metricCountDictionary, XName xName) { if (metricCountDictionary.ContainsKey(xName)) + { metricCountDictionary[xName] = metricCountDictionary[xName] + 1; + } else + { metricCountDictionary.Add(xName, 1); + } } private static void ValidateImageExists(OpenXmlPart part, string relId, Dictionary metrics) { var imagePart = part.Parts.FirstOrDefault(ipp => ipp.RelationshipId == relId); if (imagePart == null) + { IncrementMetric(metrics, H.ReferenceToNullImage); + } } - private static void NumberingFormatListAssembly(WordprocessingDocument wDoc, List metrics) { - List numFmtList = new List(); + var numFmtList = new List(); foreach (var part in wDoc.ContentParts()) { var xDoc = part.GetXDocument(); @@ -468,15 +537,17 @@ private static void NumberingFormatListAssembly(WordprocessingDocument wDoc, Lis .Select(p => { ListItemRetriever.RetrieveListItem(wDoc, p, null); - ListItemRetriever.ListItemInfo lif = p.Annotation(); + var lif = p.Annotation(); if (lif != null && lif.IsListItem && lif.Lvl(ListItemRetriever.GetParagraphLevel(p)) != null) { - string numFmtForLevel = (string)lif.Lvl(ListItemRetriever.GetParagraphLevel(p)).Elements(W.numFmt).Attributes(W.val).FirstOrDefault(); + var numFmtForLevel = (string)lif.Lvl(ListItemRetriever.GetParagraphLevel(p)).Elements(W.numFmt).Attributes(W.val).FirstOrDefault(); if (numFmtForLevel == null) { var numFmtElement = lif.Lvl(ListItemRetriever.GetParagraphLevel(p)).Elements(MC.AlternateContent).Elements(MC.Choice).Elements(W.numFmt).FirstOrDefault(); if (numFmtElement != null && (string)numFmtElement.Attribute(W.val) == "custom") + { numFmtForLevel = (string)numFmtElement.Attribute(W.format); + } } return numFmtForLevel; } @@ -493,7 +564,7 @@ private static void NumberingFormatListAssembly(WordprocessingDocument wDoc, Lis } } - class FormattingMetrics + private class FormattingMetrics { public int RunCount; public int RunWithoutRprCount; @@ -520,7 +591,7 @@ public FormattingMetrics() private static void FontAndCharSetAnalysis(WordprocessingDocument wDoc, List metrics, List notes) { - FormattingAssemblerSettings settings = new FormattingAssemblerSettings + var settings = new FormattingAssemblerSettings { RemoveStyleNamesFromParagraphAndRunProperties = false, ClearStyles = true, @@ -542,27 +613,59 @@ private static void FontAndCharSetAnalysis(WordprocessingDocument wDoc, List 0) + { metrics.Add(new XElement(H.RunWithoutRprCount, new XAttribute(H.Val, formattingMetrics.RunWithoutRprCount))); + } + if (formattingMetrics.ZeroLengthText > 0) + { metrics.Add(new XElement(H.ZeroLengthText, new XAttribute(H.Val, formattingMetrics.ZeroLengthText))); + } + if (formattingMetrics.MultiFontRun > 0) + { metrics.Add(new XElement(H.MultiFontRun, new XAttribute(H.Val, formattingMetrics.MultiFontRun))); + } + if (formattingMetrics.AsciiCharCount > 0) + { metrics.Add(new XElement(H.AsciiCharCount, new XAttribute(H.Val, formattingMetrics.AsciiCharCount))); + } + if (formattingMetrics.CSCharCount > 0) + { metrics.Add(new XElement(H.CSCharCount, new XAttribute(H.Val, formattingMetrics.CSCharCount))); + } + if (formattingMetrics.EastAsiaCharCount > 0) + { metrics.Add(new XElement(H.EastAsiaCharCount, new XAttribute(H.Val, formattingMetrics.EastAsiaCharCount))); + } + if (formattingMetrics.HAnsiCharCount > 0) + { metrics.Add(new XElement(H.HAnsiCharCount, new XAttribute(H.Val, formattingMetrics.HAnsiCharCount))); + } + if (formattingMetrics.AsciiRunCount > 0) + { metrics.Add(new XElement(H.AsciiRunCount, new XAttribute(H.Val, formattingMetrics.AsciiRunCount))); + } + if (formattingMetrics.CSRunCount > 0) + { metrics.Add(new XElement(H.CSRunCount, new XAttribute(H.Val, formattingMetrics.CSRunCount))); + } + if (formattingMetrics.EastAsiaRunCount > 0) + { metrics.Add(new XElement(H.EastAsiaRunCount, new XAttribute(H.Val, formattingMetrics.EastAsiaRunCount))); + } + if (formattingMetrics.HAnsiRunCount > 0) + { metrics.Add(new XElement(H.HAnsiRunCount, new XAttribute(H.Val, formattingMetrics.HAnsiRunCount))); + } if (formattingMetrics.Languages.Any()) { @@ -589,7 +692,7 @@ private static void AnalyzeRun(XElement run, List attList, List FormattingAssembler.DetermineFontTypeFromCharacter(ch, csa)) .ToArray(); @@ -606,24 +709,38 @@ private static void AnalyzeRun(XElement run, List attList, List { if (ft == FormattingAssembler.FontType.Ascii) + { return csa.LatinLang; + } + if (ft == FormattingAssembler.FontType.CS) + { return csa.BidiLang; + } + if (ft == FormattingAssembler.FontType.EastAsia) + { return csa.EastAsiaLang; + } //if (ft == FormattingAssembler.FontType.HAnsi) return csa.LatinLang; }) .Select(l => { if (l == "" || l == null) + { return /* "Dflt:" + */ CultureInfo.CurrentCulture.Name; + } + return l; }) //.Where(l => l != null && l != "") .Distinct(); if (languages.Any(l => !formattingMetrics.Languages.Contains(l))) + { formattingMetrics.Languages = formattingMetrics.Languages.Concat(languages).Distinct().ToList(); + } + var multiFontRun = distinctFonts.Count() > 1; if (multiFontRun) { @@ -642,14 +759,17 @@ private static void AnalyzeRun(XElement run, List attList, List attList, List metrics = new List(); - - bool valid = ValidateAgainstSpecificVersion(sDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2007, H.SdkValidationError2007); - valid |= ValidateAgainstSpecificVersion(sDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2010, H.SdkValidationError2010); -#if !NET35 - valid |= ValidateAgainstSpecificVersion(sDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2013, H.SdkValidationError2013); -#endif - - return new XElement(H.Metrics, - new XAttribute(H.FileName, smlDoc.FileName), - new XAttribute(H.FileType, "SpreadsheetML"), - metrics, - GetTableInfoForWorkbook(sDoc, settings), - settings.RetrieveNamespaceList ? RetrieveNamespaceList(sDoc) : null, - settings.RetrieveContentTypeList ? RetrieveContentTypeList(sDoc) : null); - } - } + using var streamDoc = new OpenXmlMemoryStreamDocument(smlDoc); + using var sDoc = streamDoc.GetSpreadsheetDocument(); + var metrics = new List(); + + var valid = ValidateAgainstSpecificVersion(sDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2007, H.SdkValidationError2007); + valid |= ValidateAgainstSpecificVersion(sDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2010, H.SdkValidationError2010); + valid |= ValidateAgainstSpecificVersion(sDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2013, H.SdkValidationError2013); + + return new XElement(H.Metrics, + new XAttribute(H.FileName, smlDoc.FileName), + new XAttribute(H.FileType, "SpreadsheetML"), + metrics, + GetTableInfoForWorkbook(sDoc, settings), + settings.RetrieveNamespaceList ? RetrieveNamespaceList(sDoc) : null, + settings.RetrieveContentTypeList ? RetrieveContentTypeList(sDoc) : null); } private static XElement GetTableInfoForWorkbook(SpreadsheetDocument spreadsheet, MetricsGetterSettings settings) @@ -713,25 +831,25 @@ private static XElement GetTableInfoForWorkbook(SpreadsheetDocument spreadsheet, { var rid = (string)sh.Attribute(R.id); var sheetName = (string)sh.Attribute("name"); - WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(rid); + var worksheetPart = (WorksheetPart)workbookPart.GetPartById(rid); return GetTableInfoForSheet(spreadsheet, worksheetPart, sheetName, settings); })); return partInformation; } - public static XElement GetTableInfoForSheet(SpreadsheetDocument spreadsheetDocument, WorksheetPart sheetPart, string sheetName, + public static XElement? GetTableInfoForSheet(SpreadsheetDocument spreadsheetDocument, WorksheetPart sheetPart, string sheetName, MetricsGetterSettings settings) { var xd = sheetPart.GetXDocument(); - XElement sheetInformation = new XElement(H.Sheet, + var sheetInformation = new XElement(H.Sheet, new XAttribute(H.Name, sheetName), xd.Root.Elements(S.tableParts).Elements(S.tablePart).Select(tp => { - string rId = (string)tp.Attribute(R.id); - TableDefinitionPart tablePart = (TableDefinitionPart)sheetPart.GetPartById(rId); + var rId = (string)tp.Attribute(R.id); + var tablePart = (TableDefinitionPart)sheetPart.GetPartById(rId); var txd = tablePart.GetXDocument(); var tableName = (string)txd.Root.Attribute("displayName"); - XElement tableCellData = null; + XElement? tableCellData = null; if (settings.IncludeXlsxTableCellData) { var xlsxTable = spreadsheetDocument.Table(tableName); @@ -763,38 +881,38 @@ public static XElement GetTableInfoForSheet(SpreadsheetDocument spreadsheetDocum }) ); if (!sheetInformation.HasElements) + { return null; + } + return sheetInformation; } public static XElement GetPptxMetrics(PmlDocument pmlDoc, MetricsGetterSettings settings) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(pmlDoc)) - { - using (PresentationDocument pDoc = streamDoc.GetPresentationDocument()) - { - List metrics = new List(); - - bool valid = ValidateAgainstSpecificVersion(pDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2007, H.SdkValidationError2007); - valid |= ValidateAgainstSpecificVersion(pDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2010, H.SdkValidationError2010); -#if !NET35 - valid |= ValidateAgainstSpecificVersion(pDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2013, H.SdkValidationError2013); -#endif - return new XElement(H.Metrics, - new XAttribute(H.FileName, pmlDoc.FileName), - new XAttribute(H.FileType, "PresentationML"), - metrics, - settings.RetrieveNamespaceList ? RetrieveNamespaceList(pDoc) : null, - settings.RetrieveContentTypeList ? RetrieveContentTypeList(pDoc) : null); - } - } + using var streamDoc = new OpenXmlMemoryStreamDocument(pmlDoc); + using var pDoc = streamDoc.GetPresentationDocument(); + var metrics = new List(); + + var valid = ValidateAgainstSpecificVersion(pDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2007, H.SdkValidationError2007); + valid |= ValidateAgainstSpecificVersion(pDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2010, H.SdkValidationError2010); + valid |= ValidateAgainstSpecificVersion(pDoc, metrics, DocumentFormat.OpenXml.FileFormatVersions.Office2013, H.SdkValidationError2013); + return new XElement(H.Metrics, + new XAttribute(H.FileName, pmlDoc.FileName), + new XAttribute(H.FileType, "PresentationML"), + metrics, + settings.RetrieveNamespaceList ? RetrieveNamespaceList(pDoc) : null, + settings.RetrieveContentTypeList ? RetrieveContentTypeList(pDoc) : null); } - private static object GetStyleHierarchy(WordprocessingDocument document) + private static object? GetStyleHierarchy(WordprocessingDocument document) { var stylePart = document.MainDocumentPart.StyleDefinitionsPart; if (stylePart == null) + { return null; + } + var xd = stylePart.GetXDocument(); var stylesWithPath = xd.Root .Elements(W.style) @@ -806,23 +924,31 @@ private static object GetStyleHierarchy(WordprocessingDocument document) { var baseStyle = (string)thisStyle.Elements(W.basedOn).Attributes(W.val).FirstOrDefault(); if (baseStyle == null) + { break; + } + styleString = baseStyle + "/" + styleString; thisStyle = xd.Root.Elements(W.style).FirstOrDefault(ts => ts.Attribute(W.styleId).Value == baseStyle); if (thisStyle == null) + { break; + } } return styleString; }) .OrderBy(n => n) .ToList(); - XElement styleHierarchy = new XElement(H.StyleHierarchy); + var styleHierarchy = new XElement(H.StyleHierarchy); foreach (var item in stylesWithPath) { var styleChain = item.Split('/'); - XElement elementToAddTo = styleHierarchy; + var elementToAddTo = styleHierarchy; foreach (var inChain in styleChain.PtSkipLast(1)) + { elementToAddTo = elementToAddTo.Elements(H.Style).FirstOrDefault(z => z.Attribute(H.Id).Value == inChain); + } + var styleToAdd = styleChain.Last(); elementToAddTo.Add( new XElement(H.Style, @@ -832,9 +958,9 @@ private static object GetStyleHierarchy(WordprocessingDocument document) return styleHierarchy; } - private static XElement GetMetricsForWmlPart(OpenXmlPart part, MetricsGetterSettings settings) + private static XElement? GetMetricsForWmlPart(OpenXmlPart part, MetricsGetterSettings settings) { - XElement contentControls = null; + XElement? contentControls = null; if (part is MainDocumentPart || part is HeaderPart || part is FooterPart || @@ -842,35 +968,41 @@ part is FootnotesPart || part is EndnotesPart) { var xd = part.GetXDocument(); - contentControls = (XElement)GetContentControlsTransform(xd.Root, settings); + contentControls = GetContentControlsTransform(xd.Root, settings) as XElement; if (!contentControls.HasElements) + { contentControls = null; + } } var partMetrics = new XElement(H.Part, new XAttribute(H.ContentType, part.ContentType), new XAttribute(H.Uri, part.Uri.ToString()), contentControls); if (partMetrics.HasElements) + { return partMetrics; + } + return null; } - private static object GetContentControlsTransform(XNode node, MetricsGetterSettings settings) + private static object? GetContentControlsTransform(XNode node, MetricsGetterSettings settings) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { if (element == element.Document.Root) + { return new XElement(H.ContentControls, element.Nodes().Select(n => GetContentControlsTransform(n, settings))); + } if (element.Name == W.sdt) { var tag = (string)element.Elements(W.sdtPr).Elements(W.tag).Attributes(W.val).FirstOrDefault(); - XAttribute tagAttr = tag != null ? new XAttribute(H.Tag, tag) : null; + var tagAttr = tag != null ? new XAttribute(H.Tag, tag) : null; var alias = (string)element.Elements(W.sdtPr).Elements(W.alias).Attributes(W.val).FirstOrDefault(); - XAttribute aliasAttr = alias != null ? new XAttribute(H.Alias, alias) : null; + var aliasAttr = alias != null ? new XAttribute(H.Alias, alias) : null; var xPathAttr = new XAttribute(H.XPath, element.GetXPath()); @@ -886,30 +1018,78 @@ private static object GetContentControlsTransform(XNode node, MetricsGetterSetti var isGroup = element.Elements(W.sdtPr).Elements(W.group).Any(); var isPicture = element.Elements(W.sdtPr).Elements(W.picture).Any(); var isRichText = element.Elements(W.sdtPr).Elements(W.richText).Any() || - (! isText && - ! isBibliography && - ! isCitation && - ! isComboBox && - ! isDate && - ! isDocPartList && - ! isDocPartObj && - ! isDropDownList && - ! isEquation && - ! isGroup && - ! isPicture); - string type = null; - if (isText ) type = "Text"; - if (isBibliography) type = "Bibliography"; - if (isCitation ) type = "Citation"; - if (isComboBox ) type = "ComboBox"; - if (isDate ) type = "Date"; - if (isDocPartList ) type = "DocPartList"; - if (isDocPartObj ) type = "DocPartObj"; - if (isDropDownList) type = "DropDownList"; - if (isEquation ) type = "Equation"; - if (isGroup ) type = "Group"; - if (isPicture ) type = "Picture"; - if (isRichText ) type = "RichText"; + !isText && + !isBibliography && + !isCitation && + !isComboBox && + !isDate && + !isDocPartList && + !isDocPartObj && + !isDropDownList && + !isEquation && + !isGroup && + !isPicture; + string? type = null; + if (isText) + { + type = "Text"; + } + + if (isBibliography) + { + type = "Bibliography"; + } + + if (isCitation) + { + type = "Citation"; + } + + if (isComboBox) + { + type = "ComboBox"; + } + + if (isDate) + { + type = "Date"; + } + + if (isDocPartList) + { + type = "DocPartList"; + } + + if (isDocPartObj) + { + type = "DocPartObj"; + } + + if (isDropDownList) + { + type = "DropDownList"; + } + + if (isEquation) + { + type = "Equation"; + } + + if (isGroup) + { + type = "Group"; + } + + if (isPicture) + { + type = "Picture"; + } + + if (isRichText) + { + type = "RichText"; + } + var typeAttr = new XAttribute(H.Type, type); return new XElement(H.ContentControl, @@ -923,149 +1103,152 @@ private static object GetContentControlsTransform(XNode node, MetricsGetterSetti return element.Nodes().Select(n => GetContentControlsTransform(n, settings)); } if (settings.IncludeTextInContentControls) + { return node; + } + return null; } } public static class H { - public static XName ActiveX = "ActiveX"; - public static XName Alias = "Alias"; - public static XName AltChunk = "AltChunk"; - public static XName Arguments = "Arguments"; - public static XName AsciiCharCount = "AsciiCharCount"; - public static XName AsciiRunCount = "AsciiRunCount"; - public static XName AverageParagraphLength = "AverageParagraphLength"; - public static XName BaselineReport = "BaselineReport"; - public static XName Batch = "Batch"; - public static XName BatchName = "BatchName"; - public static XName BatchSelector = "BatchSelector"; - public static XName CSCharCount = "CSCharCount"; - public static XName CSRunCount = "CSRunCount"; - public static XName Catalog = "Catalog"; - public static XName CatalogList = "CatalogList"; - public static XName CatalogListFile = "CatalogListFile"; - public static XName CaughtException = "CaughtException"; - public static XName Cell = "Cell"; - public static XName Column = "Column"; - public static XName Columns = "Columns"; - public static XName ComplexField = "ComplexField"; - public static XName Computer = "Computer"; - public static XName Computers = "Computers"; - public static XName ContentControl = "ContentControl"; - public static XName ContentControls = "ContentControls"; - public static XName ContentType = "ContentType"; - public static XName ContentTypes = "ContentTypes"; - public static XName CustomXmlMarkup = "CustomXmlMarkup"; - public static XName DLL = "DLL"; - public static XName DefaultDialogValuesFile = "DefaultDialogValuesFile"; - public static XName DefaultValues = "DefaultValues"; - public static XName Dependencies = "Dependencies"; - public static XName DestinationDir = "DestinationDir"; - public static XName Directory = "Directory"; - public static XName DirectoryPattern = "DirectoryPattern"; - public static XName DisplayName = "DisplayName"; - public static XName DoJobQueueName = "DoJobQueueName"; - public static XName Document = "Document"; - public static XName DocumentProtection = "DocumentProtection"; - public static XName DocumentSelector = "DocumentSelector"; - public static XName DocumentType = "DocumentType"; - public static XName Documents = "Documents"; - public static XName EastAsiaCharCount = "EastAsiaCharCount"; - public static XName EastAsiaRunCount = "EastAsiaRunCount"; - public static XName ElementCount = "ElementCount"; - public static XName EmbeddedXlsx = "EmbeddedXlsx"; - public static XName Error = "Error"; - public static XName Exception = "Exception"; - public static XName Exe = "Exe"; - public static XName ExeRoot = "ExeRoot"; - public static XName Extension = "Extension"; - public static XName File = "File"; - public static XName FileLength = "FileLength"; - public static XName FileName = "FileName"; - public static XName FilePattern = "FilePattern"; - public static XName FileType = "FileType"; - public static XName Guid = "Guid"; - public static XName HAnsiCharCount = "HAnsiCharCount"; - public static XName HAnsiRunCount = "HAnsiRunCount"; - public static XName RevisionTracking = "RevisionTracking"; - public static XName Hyperlink = "Hyperlink"; - public static XName IPAddress = "IPAddress"; - public static XName Id = "Id"; - public static XName Invalid = "Invalid"; - public static XName InvalidHyperlink = "InvalidHyperlink"; - public static XName InvalidHyperlinkException = "InvalidHyperlinkException"; - public static XName InvalidSaveThroughXslt = "InvalidSaveThroughXslt"; - public static XName JobComplete = "JobComplete"; - public static XName JobExe = "JobExe"; - public static XName JobName = "JobName"; - public static XName JobSpec = "JobSpec"; - public static XName Languages = "Languages"; - public static XName LegacyFrame = "LegacyFrame"; - public static XName LocalDoJobQueue = "LocalDoJobQueue"; - public static XName MachineName = "MachineName"; - public static XName MaxConcurrentJobs = "MaxConcurrentJobs"; - public static XName MaxDocumentsInJob = "MaxDocumentsInJob"; - public static XName MaxParagraphLength = "MaxParagraphLength"; - public static XName Message = "Message"; - public static XName Metrics = "Metrics"; - public static XName MultiDirectory = "MultiDirectory"; - public static XName MultiFontRun = "MultiFontRun"; - public static XName MultiServerQueue = "MultiServerQueue"; - public static XName Name = "Name"; - public static XName Namespaces = "Namespaces"; - public static XName Namespace = "Namespace"; - public static XName NamespaceName = "NamespaceName"; - public static XName NamespacePrefix = "NamespacePrefix"; - public static XName Note = "Note"; - public static XName NumberingFormatList = "NumberingFormatList"; - public static XName ObjectDisposedException = "ObjectDisposedException"; - public static XName ParagraphCount = "ParagraphCount"; - public static XName Part = "Part"; - public static XName Parts = "Parts"; - public static XName PassedDocuments = "PassedDocuments"; - public static XName Path = "Path"; - public static XName ProduceCatalog = "ProduceCatalog"; - public static XName ReferenceToNullImage = "ReferenceToNullImage"; - public static XName Report = "Report"; - public static XName Root = "Root"; - public static XName RootDirectory = "RootDirectory"; - public static XName Row = "Row"; - public static XName RunCount = "RunCount"; - public static XName RunWithoutRprCount = "RunWithoutRprCount"; - public static XName SdkValidationError = "SdkValidationError"; - public static XName SdkValidationError2007 = "SdkValidationError2007"; - public static XName SdkValidationError2010 = "SdkValidationError2010"; - public static XName SdkValidationError2013 = "SdkValidationError2013"; - public static XName Sheet = "Sheet"; - public static XName Sheets = "Sheets"; - public static XName SimpleField = "SimpleField"; - public static XName Skip = "Skip"; - public static XName SmartTag = "SmartTag"; - public static XName SourceRootDir = "SourceRootDir"; - public static XName SpawnerJobExeLocation = "SpawnerJobExeLocation"; - public static XName SpawnerReady = "SpawnerReady"; - public static XName Style = "Style"; - public static XName StyleHierarchy = "StyleHierarchy"; - public static XName SubDocument = "SubDocument"; - public static XName Table = "Table"; - public static XName TableData = "TableData"; - public static XName Tag = "Tag"; - public static XName Take = "Take"; - public static XName TextBox = "TextBox"; - public static XName TrackRevisionsEnabled = "TrackRevisionsEnabled"; - public static XName Type = "Type"; - public static XName Uri = "Uri"; - public static XName Val = "Val"; - public static XName Valid = "Valid"; - public static XName WindowStyle = "WindowStyle"; - public static XName XPath = "XPath"; - public static XName ZeroLengthText = "ZeroLengthText"; - public static XName custDataLst = "custDataLst"; - public static XName custShowLst = "custShowLst"; - public static XName kinsoku = "kinsoku"; - public static XName modifyVerifier = "modifyVerifier"; - public static XName photoAlbum = "photoAlbum"; + public static readonly XName ActiveX = "ActiveX"; + public static readonly XName Alias = "Alias"; + public static readonly XName AltChunk = "AltChunk"; + public static readonly XName Arguments = "Arguments"; + public static readonly XName AsciiCharCount = "AsciiCharCount"; + public static readonly XName AsciiRunCount = "AsciiRunCount"; + public static readonly XName AverageParagraphLength = "AverageParagraphLength"; + public static readonly XName BaselineReport = "BaselineReport"; + public static readonly XName Batch = "Batch"; + public static readonly XName BatchName = "BatchName"; + public static readonly XName BatchSelector = "BatchSelector"; + public static readonly XName CSCharCount = "CSCharCount"; + public static readonly XName CSRunCount = "CSRunCount"; + public static readonly XName Catalog = "Catalog"; + public static readonly XName CatalogList = "CatalogList"; + public static readonly XName CatalogListFile = "CatalogListFile"; + public static readonly XName CaughtException = "CaughtException"; + public static readonly XName Cell = "Cell"; + public static readonly XName Column = "Column"; + public static readonly XName Columns = "Columns"; + public static readonly XName ComplexField = "ComplexField"; + public static readonly XName Computer = "Computer"; + public static readonly XName Computers = "Computers"; + public static readonly XName ContentControl = "ContentControl"; + public static readonly XName ContentControls = "ContentControls"; + public static readonly XName ContentType = "ContentType"; + public static readonly XName ContentTypes = "ContentTypes"; + public static readonly XName CustomXmlMarkup = "CustomXmlMarkup"; + public static readonly XName DLL = "DLL"; + public static readonly XName DefaultDialogValuesFile = "DefaultDialogValuesFile"; + public static readonly XName DefaultValues = "DefaultValues"; + public static readonly XName Dependencies = "Dependencies"; + public static readonly XName DestinationDir = "DestinationDir"; + public static readonly XName Directory = "Directory"; + public static readonly XName DirectoryPattern = "DirectoryPattern"; + public static readonly XName DisplayName = "DisplayName"; + public static readonly XName DoJobQueueName = "DoJobQueueName"; + public static readonly XName Document = "Document"; + public static readonly XName DocumentProtection = "DocumentProtection"; + public static readonly XName DocumentSelector = "DocumentSelector"; + public static readonly XName DocumentType = "DocumentType"; + public static readonly XName Documents = "Documents"; + public static readonly XName EastAsiaCharCount = "EastAsiaCharCount"; + public static readonly XName EastAsiaRunCount = "EastAsiaRunCount"; + public static readonly XName ElementCount = "ElementCount"; + public static readonly XName EmbeddedXlsx = "EmbeddedXlsx"; + public static readonly XName Error = "Error"; + public static readonly XName Exception = "Exception"; + public static readonly XName Exe = "Exe"; + public static readonly XName ExeRoot = "ExeRoot"; + public static readonly XName Extension = "Extension"; + public static readonly XName File = "File"; + public static readonly XName FileLength = "FileLength"; + public static readonly XName FileName = "FileName"; + public static readonly XName FilePattern = "FilePattern"; + public static readonly XName FileType = "FileType"; + public static readonly XName Guid = "Guid"; + public static readonly XName HAnsiCharCount = "HAnsiCharCount"; + public static readonly XName HAnsiRunCount = "HAnsiRunCount"; + public static readonly XName RevisionTracking = "RevisionTracking"; + public static readonly XName Hyperlink = "Hyperlink"; + public static readonly XName IPAddress = "IPAddress"; + public static readonly XName Id = "Id"; + public static readonly XName Invalid = "Invalid"; + public static readonly XName InvalidHyperlink = "InvalidHyperlink"; + public static readonly XName InvalidHyperlinkException = "InvalidHyperlinkException"; + public static readonly XName InvalidSaveThroughXslt = "InvalidSaveThroughXslt"; + public static readonly XName JobComplete = "JobComplete"; + public static readonly XName JobExe = "JobExe"; + public static readonly XName JobName = "JobName"; + public static readonly XName JobSpec = "JobSpec"; + public static readonly XName Languages = "Languages"; + public static readonly XName LegacyFrame = "LegacyFrame"; + public static readonly XName LocalDoJobQueue = "LocalDoJobQueue"; + public static readonly XName MachineName = "MachineName"; + public static readonly XName MaxConcurrentJobs = "MaxConcurrentJobs"; + public static readonly XName MaxDocumentsInJob = "MaxDocumentsInJob"; + public static readonly XName MaxParagraphLength = "MaxParagraphLength"; + public static readonly XName Message = "Message"; + public static readonly XName Metrics = "Metrics"; + public static readonly XName MultiDirectory = "MultiDirectory"; + public static readonly XName MultiFontRun = "MultiFontRun"; + public static readonly XName MultiServerQueue = "MultiServerQueue"; + public static readonly XName Name = "Name"; + public static readonly XName Namespaces = "Namespaces"; + public static readonly XName Namespace = "Namespace"; + public static readonly XName NamespaceName = "NamespaceName"; + public static readonly XName NamespacePrefix = "NamespacePrefix"; + public static readonly XName Note = "Note"; + public static readonly XName NumberingFormatList = "NumberingFormatList"; + public static readonly XName ObjectDisposedException = "ObjectDisposedException"; + public static readonly XName ParagraphCount = "ParagraphCount"; + public static readonly XName Part = "Part"; + public static readonly XName Parts = "Parts"; + public static readonly XName PassedDocuments = "PassedDocuments"; + public static readonly XName Path = "Path"; + public static readonly XName ProduceCatalog = "ProduceCatalog"; + public static readonly XName ReferenceToNullImage = "ReferenceToNullImage"; + public static readonly XName Report = "Report"; + public static readonly XName Root = "Root"; + public static readonly XName RootDirectory = "RootDirectory"; + public static readonly XName Row = "Row"; + public static readonly XName RunCount = "RunCount"; + public static readonly XName RunWithoutRprCount = "RunWithoutRprCount"; + public static readonly XName SdkValidationError = "SdkValidationError"; + public static readonly XName SdkValidationError2007 = "SdkValidationError2007"; + public static readonly XName SdkValidationError2010 = "SdkValidationError2010"; + public static readonly XName SdkValidationError2013 = "SdkValidationError2013"; + public static readonly XName Sheet = "Sheet"; + public static readonly XName Sheets = "Sheets"; + public static readonly XName SimpleField = "SimpleField"; + public static readonly XName Skip = "Skip"; + public static readonly XName SmartTag = "SmartTag"; + public static readonly XName SourceRootDir = "SourceRootDir"; + public static readonly XName SpawnerJobExeLocation = "SpawnerJobExeLocation"; + public static readonly XName SpawnerReady = "SpawnerReady"; + public static readonly XName Style = "Style"; + public static readonly XName StyleHierarchy = "StyleHierarchy"; + public static readonly XName SubDocument = "SubDocument"; + public static readonly XName Table = "Table"; + public static readonly XName TableData = "TableData"; + public static readonly XName Tag = "Tag"; + public static readonly XName Take = "Take"; + public static readonly XName TextBox = "TextBox"; + public static readonly XName TrackRevisionsEnabled = "TrackRevisionsEnabled"; + public static readonly XName Type = "Type"; + public static readonly XName Uri = "Uri"; + public static readonly XName Val = "Val"; + public static readonly XName Valid = "Valid"; + public static readonly XName WindowStyle = "WindowStyle"; + public static readonly XName XPath = "XPath"; + public static readonly XName ZeroLengthText = "ZeroLengthText"; + public static readonly XName custDataLst = "custDataLst"; + public static readonly XName custShowLst = "custShowLst"; + public static readonly XName kinsoku = "kinsoku"; + public static readonly XName modifyVerifier = "modifyVerifier"; + public static readonly XName photoAlbum = "photoAlbum"; } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/NugetIcon.png b/OpenXmlPowerTools/NugetIcon.png new file mode 100644 index 00000000..5e0f86b2 Binary files /dev/null and b/OpenXmlPowerTools/NugetIcon.png differ diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/BreakHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/BreakHandler.cs new file mode 100644 index 00000000..d8dd2cd6 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/BreakHandler.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + /// + /// Default handler that transforms OpenXml breaks into some HTML specific equivalent + /// + public class BreakHandler : IBreakHandler + { + /// + /// Default handler that transforms breaks into some HTML specific equivalent + /// + /// + /// + public IEnumerable TransformBreak(XElement element) + { + XElement span = default!; + var tabWidth = (decimal?)element.Attribute(PtOpenXml.TabWidth); + if (tabWidth != null) + { + span = new XElement(Xhtml.span); + span.AddAnnotation(new Dictionary + { + { "margin", string.Format(NumberFormatInfo.InvariantInfo, "0 0 0 {0:0.00}in", tabWidth) }, + { "padding", "0 0 0 0" } + }); + } + + var paragraph = element.Ancestors(W.p).FirstOrDefault(); + + var isBidi = paragraph != null && paragraph.Elements(W.pPr).Elements(W.bidi).Any(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true); + + var zeroWidthChar = isBidi ? new XEntity("#x200f") : new XEntity("#x200e"); + return new XNode[] { new XElement(Xhtml.br), zeroWidthChar, span }; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/FontHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/FontHandler.cs new file mode 100644 index 00000000..7c89ba19 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/FontHandler.cs @@ -0,0 +1,19 @@ +using System.Linq; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + public class FontHandler : IFontHandler + { + public string TranslateParagraphStyleFont(XElement paragraph) + { + return (string)paragraph.Attributes(PtOpenXml.FontName).FirstOrDefault(); + } + + public string TranslateRunStyleFont(XElement run) + { + var sym = run.Element(W.sym); + return sym != null ? (string)sym.Attributes(W.font).FirstOrDefault() : (string)run.Attributes(PtOpenXml.FontName).FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/HtmlConverterExtensions.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/HtmlConverterExtensions.cs new file mode 100644 index 00000000..6a226202 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/HtmlConverterExtensions.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + internal static class HtmlConverterExtensions + { + public static void AddIfMissing(this Dictionary style, string propName, string value) + { + if (style.ContainsKey(propName)) + { + return; + } + + style.Add(propName, value); + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IBreakHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IBreakHandler.cs new file mode 100644 index 00000000..0f9938fa --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IBreakHandler.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + public interface IBreakHandler + { + IEnumerable TransformBreak(XElement element); + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IFontHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IFontHandler.cs new file mode 100644 index 00000000..58e09650 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IFontHandler.cs @@ -0,0 +1,10 @@ +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + public interface IFontHandler + { + public string TranslateRunStyleFont(XElement run); + public string TranslateParagraphStyleFont(XElement paragraph); + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IImageHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IImageHandler.cs new file mode 100644 index 00000000..f1b394d6 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/IImageHandler.cs @@ -0,0 +1,17 @@ +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + /// + /// Implement an imageHandler to get image support in HTML + /// + public interface IImageHandler + { + /// + /// Transforms OpenXml Images to HTML embeddable images + /// + /// + /// + public XElement TransformImage(ImageInfo imageInfo); + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ISymbolHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ISymbolHandler.cs new file mode 100644 index 00000000..64d39bef --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ISymbolHandler.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ /// + /// Handler that transforms every symbol from w:sym + /// + public interface ISymbolHandler + { + /// + /// Returns some kind of changed symbol, that will be used instead of the original in a w:sym element + /// + /// + /// fontFamilily of current run + /// transformed symbol + public XElement TransformSymbol(XElement element, Dictionary fontFamily); + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ITextHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ITextHandler.cs new file mode 100644 index 00000000..d726e564 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ITextHandler.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + /// + /// Handler that transforms every text in w:t + /// + public interface ITextHandler + { + /// + /// Returns some kind of changed text, that will be used instead of the original in w:t elements + /// + /// + /// fontFamilily of current run + /// transformed text + public string TransformText(string text, Dictionary fontFamily); + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ImageHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ImageHandler.cs new file mode 100644 index 00000000..f42c9a93 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ImageHandler.cs @@ -0,0 +1,44 @@ +using SkiaSharp; +using System; +using System.IO; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + /// + /// Default image handler + /// + public class ImageHandler : IImageHandler + { + /// + /// Transforms OpenXml Images to HTML embeddable images + /// + /// + /// + public XElement TransformImage(ImageInfo imageInfo) + { + using var imageStream = new MemoryStream(); + imageInfo.Image.CopyTo(imageStream); + var data = imageStream.ToArray(); + + using var codec = SKCodec.Create(new SKMemoryStream(data)); + var mimeType = GetMimeType(codec.EncodedFormat); + var base64 = Convert.ToBase64String(data); + var imageSource = $"data:{mimeType};base64,{base64}"; + + return new XElement(Xhtml.img, new XAttribute(NoNamespace.src, imageSource), imageInfo.ImgStyleAttribute, imageInfo.AltText != null ? new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); + } + + private static string GetMimeType(SKEncodedImageFormat format) => format switch + { + SKEncodedImageFormat.Bmp => "image/bmp", + SKEncodedImageFormat.Gif => "image/gif", + SKEncodedImageFormat.Ico => "image/x-icon", + SKEncodedImageFormat.Jpeg => "image/jpeg", + SKEncodedImageFormat.Png => "image/png", + SKEncodedImageFormat.Wbmp => "image/vnd.wap.wbmp", + SKEncodedImageFormat.Webp => "image/webp", + _ => "application/octet-stream", + }; + } +} diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ImageInfo.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ImageInfo.cs new file mode 100644 index 00000000..a1a57831 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/ImageInfo.cs @@ -0,0 +1,16 @@ +using System.IO; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + public class ImageInfo + { + public Stream? Image { get; set; } + public XAttribute? ImgStyleAttribute { get; set; } + public string? ContentType { get; set; } + public XElement? DrawingElement { get; set; } + public string? AltText { get; set; } + public static int EmusPerCm => 360000; + public static int EmusPerInch => 914400; + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/SymbolHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/SymbolHandler.cs new file mode 100644 index 00000000..83394f42 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/SymbolHandler.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + /// + /// Default handler that transforms every symbol into some html encoded font specific char + /// + public class SymbolHandler : ISymbolHandler + { + /// + /// Default handler that transforms every symbol into some html encoded font specific char + /// + /// + /// + /// + public XElement TransformSymbol(XElement element, Dictionary fontFamily) + { + var cs = (string)element.Attribute(W._char); + var c = Convert.ToInt32(cs, 16); + return new XElement(Xhtml.span, new XEntity(string.Format("#{0}", c))); + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/TextDummyHandler.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/TextDummyHandler.cs new file mode 100644 index 00000000..eb245ba2 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/TextDummyHandler.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + /// + /// Is a handler that does not temper with value in W.t elements + /// + public class TextDummyHandler : ITextHandler + { + /// + /// Is a handler that does not temper with values in W.t elements + /// + public string TransformText(string text, Dictionary fontFamily) + { + return text; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/WmlToHtmlConverter.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverter.cs similarity index 69% rename from OpenXmlPowerTools/WmlToHtmlConverter.cs rename to OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverter.cs index 3d1cb496..7a1bffa3 100644 --- a/OpenXmlPowerTools/WmlToHtmlConverter.cs +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverter.cs @@ -1,152 +1,45 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Drawing; using System.Globalization; using System.Linq; using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; - -// 200e lrm - LTR -// 200f rlm - RTL // todo need to set the HTTP "Content-Language" header, for instance: // Content-Language: en-US // Content-Language: fr-FR -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter { - public partial class WmlDocument - { - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public XElement ConvertToHtml(WmlToHtmlConverterSettings htmlConverterSettings) - { - return WmlToHtmlConverter.ConvertToHtml(this, htmlConverterSettings); - } - - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public XElement ConvertToHtml(HtmlConverterSettings htmlConverterSettings) - { - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings(htmlConverterSettings); - return WmlToHtmlConverter.ConvertToHtml(this, settings); - } - } - - [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")] - public class WmlToHtmlConverterSettings - { - public string PageTitle; - public string CssClassPrefix; - public bool FabricateCssClasses; - public string GeneralCss; - public string AdditionalCss; - public bool RestrictToSupportedLanguages; - public bool RestrictToSupportedNumberingFormats; - public Dictionary> ListItemImplementations; - public Func ImageHandler; - - public WmlToHtmlConverterSettings() - { - PageTitle = ""; - CssClassPrefix = "pt-"; - FabricateCssClasses = true; - GeneralCss = "span { white-space: pre-wrap; }"; - AdditionalCss = ""; - RestrictToSupportedLanguages = false; - RestrictToSupportedNumberingFormats = false; - ListItemImplementations = ListItemRetrieverSettings.DefaultListItemTextImplementations; - } - - public WmlToHtmlConverterSettings(HtmlConverterSettings htmlConverterSettings) - { - PageTitle = htmlConverterSettings.PageTitle; - CssClassPrefix = htmlConverterSettings.CssClassPrefix; - FabricateCssClasses = htmlConverterSettings.FabricateCssClasses; - GeneralCss = htmlConverterSettings.GeneralCss; - AdditionalCss = htmlConverterSettings.AdditionalCss; - RestrictToSupportedLanguages = htmlConverterSettings.RestrictToSupportedLanguages; - RestrictToSupportedNumberingFormats = htmlConverterSettings.RestrictToSupportedNumberingFormats; - ListItemImplementations = htmlConverterSettings.ListItemImplementations; - ImageHandler = htmlConverterSettings.ImageHandler; - } - } - - [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")] - public class HtmlConverterSettings - { - public string PageTitle; - public string CssClassPrefix; - public bool FabricateCssClasses; - public string GeneralCss; - public string AdditionalCss; - public bool RestrictToSupportedLanguages; - public bool RestrictToSupportedNumberingFormats; - public Dictionary> ListItemImplementations; - public Func ImageHandler; - - public HtmlConverterSettings() - { - PageTitle = ""; - CssClassPrefix = "pt-"; - FabricateCssClasses = true; - GeneralCss = "span { white-space: pre-wrap; }"; - AdditionalCss = ""; - RestrictToSupportedLanguages = false; - RestrictToSupportedNumberingFormats = false; - ListItemImplementations = ListItemRetrieverSettings.DefaultListItemTextImplementations; - } - } - - public static class HtmlConverter - { - public static XElement ConvertToHtml(WmlDocument wmlDoc, HtmlConverterSettings htmlConverterSettings) - { - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings(htmlConverterSettings); - return WmlToHtmlConverter.ConvertToHtml(wmlDoc, settings); - } - - public static XElement ConvertToHtml(WordprocessingDocument wDoc, HtmlConverterSettings htmlConverterSettings) - { - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings(htmlConverterSettings); - return WmlToHtmlConverter.ConvertToHtml(wDoc, settings); - } - } - - [SuppressMessage("ReSharper", "NotAccessedField.Global")] - [SuppressMessage("ReSharper", "UnusedMember.Global")] - public class ImageInfo - { - public Bitmap Bitmap; - public XAttribute ImgStyleAttribute; - public string ContentType; - public XElement DrawingElement; - public string AltText; - - public const int EmusPerInch = 914400; - public const int EmusPerCm = 360000; - } - + /// + /// Converts a wordDoc to a self contained HTML + /// public static class WmlToHtmlConverter { + /// + /// Converts a wordDoc to a self contained HTML + /// + /// + /// + /// public static XElement ConvertToHtml(WmlDocument doc, WmlToHtmlConverterSettings htmlConverterSettings) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(doc)) - { - using (WordprocessingDocument document = streamDoc.GetWordprocessingDocument()) - { - return ConvertToHtml(document, htmlConverterSettings); - } - } + using var streamDoc = new OpenXmlMemoryStreamDocument(doc); + using var document = streamDoc.GetWordprocessingDocument(); + return ConvertToHtml(document, htmlConverterSettings); } + /// + /// Converts a wordDoc to a self contained HTML + /// + /// + /// + /// public static XElement ConvertToHtml(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings htmlConverterSettings) { RevisionAccepter.AcceptRevisions(wordDoc); - SimplifyMarkupSettings simplifyMarkupSettings = new SimplifyMarkupSettings + var simplifyMarkupSettings = new SimplifyMarkupSettings { RemoveComments = true, RemoveContentControls = true, @@ -163,7 +56,7 @@ public static XElement ConvertToHtml(WordprocessingDocument wordDoc, WmlToHtmlCo }; MarkupSimplifier.SimplifyMarkup(wordDoc, simplifyMarkupSettings); - FormattingAssemblerSettings formattingAssemblerSettings = new FormattingAssemblerSettings + var formattingAssemblerSettings = new FormattingAssemblerSettings { RemoveStyleNamesFromParagraphAndRunProperties = false, ClearStyles = false, @@ -189,12 +82,11 @@ public static XElement ConvertToHtml(WordprocessingDocument wordDoc, WmlToHtmlCo CalculateSpanWidthForTabs(wordDoc); ReverseTableBordersForRtlTables(wordDoc); AdjustTableBorders(wordDoc); - XElement rootElement = wordDoc.MainDocumentPart.GetXDocument().Root; - FieldRetriever.AnnotateWithFieldInfo(wordDoc.MainDocumentPart); + var rootElement = wordDoc?.MainDocumentPart?.GetXDocument().Root; + FieldRetriever.AnnotateWithFieldInfo(wordDoc?.MainDocumentPart); AnnotateForSections(wordDoc); - XElement xhtml = (XElement)ConvertToHtmlTransform(wordDoc, htmlConverterSettings, - rootElement, false, 0m); + var xhtml = ConvertToHtmlTransform(wordDoc, htmlConverterSettings, rootElement, false, 0m) as XElement; ReifyStylesAndClasses(htmlConverterSettings, xhtml); @@ -211,23 +103,29 @@ public static XElement ConvertToHtml(WordprocessingDocument wordDoc, WmlToHtmlCo private static void ReverseTableBordersForRtlTables(WordprocessingDocument wordDoc) { - XDocument xd = wordDoc.MainDocumentPart.GetXDocument(); + var xd = wordDoc.MainDocumentPart.GetXDocument(); foreach (var tbl in xd.Descendants(W.tbl)) { var bidiVisual = tbl.Elements(W.tblPr).Elements(W.bidiVisual).FirstOrDefault(); if (bidiVisual == null) + { continue; + } var tblBorders = tbl.Elements(W.tblPr).Elements(W.tblBorders).FirstOrDefault(); if (tblBorders != null) { var left = tblBorders.Element(W.left); if (left != null) + { left = new XElement(W.right, left.Attributes()); + } var right = tblBorders.Element(W.right); if (right != null) + { right = new XElement(W.left, right.Attributes()); + } var newTblBorders = new XElement(W.tblBorders, tblBorders.Element(W.top), @@ -244,11 +142,15 @@ private static void ReverseTableBordersForRtlTables(WordprocessingDocument wordD { var left = tcBorders.Element(W.left); if (left != null) + { left = new XElement(W.right, left.Attributes()); + } var right = tcBorders.Element(W.right); if (right != null) + { right = new XElement(W.left, right.Attributes()); + } var newTcBorders = new XElement(W.tcBorders, tcBorders.Element(W.top), @@ -266,14 +168,13 @@ private static void ReifyStylesAndClasses(WmlToHtmlConverterSettings htmlConvert if (htmlConverterSettings.FabricateCssClasses) { var usedCssClassNames = new HashSet(); - var elementsThatNeedClasses = xhtml - .DescendantsAndSelf() - .Select(d => new - { - Element = d, - Styles = d.Annotation>(), - }) - .Where(z => z.Styles != null); + + var elementsThatNeedClasses = xhtml.DescendantsAndSelf().Select(d => new + { + Element = d, + Styles = d.Annotation>(), + }).Where(z => z.Styles != null); + var augmented = elementsThatNeedClasses .Select(p => new { @@ -283,9 +184,11 @@ private static void ReifyStylesAndClasses(WmlToHtmlConverterSettings htmlConvert }) .GroupBy(p => p.StylesString) .ToList(); - int classCounter = 1000000; + + var classCounter = 1000000; var sb = new StringBuilder(); sb.Append(Environment.NewLine); + foreach (var grp in augmented) { string classNameToUse; @@ -310,6 +213,7 @@ private static void ReifyStylesAndClasses(WmlToHtmlConverterSettings htmlConvert } usedCssClassNames.Add(classNameToUse); sb.Append(firstOne.Element.Name.LocalName + "." + classNameToUse + " {" + Environment.NewLine); + foreach (var st in firstOne.Styles.Where(s => s.Key != "PtStyleName")) { var s = " " + st.Key + ": " + st.Value + ";" + Environment.NewLine; @@ -317,35 +221,40 @@ private static void ReifyStylesAndClasses(WmlToHtmlConverterSettings htmlConvert } sb.Append("}" + Environment.NewLine); var classAtt = new XAttribute("class", classNameToUse); + foreach (var gc in grp) + { gc.Element.Add(classAtt); + } } + var styleValue = htmlConverterSettings.GeneralCss + sb + htmlConverterSettings.AdditionalCss; SetStyleElementValue(xhtml, styleValue); } else { - // Previously, the h:style element was not added at this point. However, - // at least the General CSS will contain important settings. + // Previously, the h:style element was not added at this point. However, at least the General CSS will contain important settings. SetStyleElementValue(xhtml, htmlConverterSettings.GeneralCss + htmlConverterSettings.AdditionalCss); foreach (var d in xhtml.DescendantsAndSelf()) { var style = d.Annotation>(); if (style == null) + { continue; - var styleValue = - style - .Where(p => p.Key != "PtStyleName") - .OrderBy(p => p.Key) - .Select(e => string.Format("{0}: {1};", e.Key, e.Value)) - .StringConcatenate(); - XAttribute st = new XAttribute("style", styleValue); + } + + var styleValue = style.Where(p => p.Key != "PtStyleName").OrderBy(p => p.Key).Select(e => $"{e.Key}: {e.Value};").StringConcatenate(); + var st = new XAttribute("style", styleValue); if (d.Attribute("style") != null) + { d.Attribute("style").Value += styleValue; + } else + { d.Add(st); + } } } } @@ -356,23 +265,163 @@ private static void SetStyleElementValue(XElement xhtml, string styleValue) .Descendants(Xhtml.style) .FirstOrDefault(); if (styleElement != null) + { styleElement.Value = styleValue; + } else { styleElement = new XElement(Xhtml.style, styleValue); var head = xhtml.Element(Xhtml.head); if (head != null) + { head.Add(styleElement); + } } } - private static object ConvertToHtmlTransform(WordprocessingDocument wordDoc, - WmlToHtmlConverterSettings settings, XNode node, - bool suppressTrailingWhiteSpace, - decimal currentMarginLeft) + private static object? ListAwareConvertToHtmlTransform(WordprocessingDocument wordDoc, + WmlToHtmlConverterSettings settings, + IEnumerable nodes, + decimal currentMarginLeft, + IList htmlNestedListTypes) + { + List styleOrNumParagraphs = nodes.OfType().ToList(); + var htmlNestedListItems = new List<(int Start, int CurrentNumber, bool Bidi, List ListItems)>(); + int previousLevels = 0; + int currentLevels = 0; + int currentNumber = 0; + int currentStart = 0; + + for (int paragraphIndex = 0; paragraphIndex < styleOrNumParagraphs.Count; paragraphIndex += 1) + { + XElement paragraph = styleOrNumParagraphs[paragraphIndex]; + var listType = (string?)paragraph.Attribute(PtOpenXml.HtmlStructure); + var isBidi = IsBidi(paragraph); + + var paragraphHtml = ConvertToHtmlTransform(wordDoc, settings, paragraph, paragraphIndex != styleOrNumParagraphs.Count - 1, currentMarginLeft); + if (!(paragraphHtml is XElement elementXml)) + { + continue; + } + + // Each paragraph, contains 1 or more children with data-pt-list-item-run attribute that indicates the nested numbering. + var paragraphChildren = elementXml.Elements().ToList(); + bool foundItemRun = false; + List? appendToList = null; + for (int childIndex = 0; childIndex < paragraphChildren.Count; childIndex += 1) + { + var child = paragraphChildren[childIndex]; + var itemRun = (string?)child.Attribute("data-pt-list-item-run"); + if (itemRun == null) + { + continue; + } + + if (!foundItemRun) + { + foundItemRun = true; + string[] levels = itemRun.Split('.'); + currentLevels = levels.Length; + for (int levelIndex = previousLevels - 1; levelIndex >= currentLevels && levelIndex > 0; levelIndex -= 1) + { + // We are now at a shallower nesting, add the deeper list to the shallower list as content. + // Note that this creates invalid HTML where a list is directly nested inside another list without a list item "li" tag, + // but Microsoft Word HTML export has the same behavior. + var (_, _, _, shallowList) = htmlNestedListItems[levelIndex - 1]; + var (deepStart, _, _, deepList) = htmlNestedListItems[levelIndex]; + var deepListType = htmlNestedListTypes[levelIndex]; + if (!string.IsNullOrEmpty(deepListType)) + { + var startAttribute = deepListType == "ol" ? new XAttribute("start", deepStart) : null; + var listHtml = new XElement(Xhtml.xhtml + deepListType, startAttribute, deepList); + shallowList.Add(listHtml); + var style = new Dictionary + { + ["list-style-type"] = "none", + ["padding"] = "0", + ["margin"] = "0", + }; + listHtml.AddAnnotation(style); + if (isBidi) + { + listHtml.Add(new XAttribute("dir", "rtl")); + } + } + + htmlNestedListItems.RemoveAt(levelIndex); + } + + for (int levelIndex = 0; levelIndex < currentLevels; levelIndex += 1) + { + // Create the level in htmlNestedListItems + int.TryParse(levels[levelIndex], out int start); + + // Current number will be set to the one from the deepest level + currentNumber = start; + if (levelIndex >= htmlNestedListItems.Count) + { + htmlNestedListItems.Add((start, start, isBidi, new List())); + } + + if (levelIndex >= htmlNestedListTypes.Count) + { + htmlNestedListTypes.Add(listType); + } + } + + (currentStart, _, _, appendToList) = htmlNestedListItems[currentLevels - 1]; + } + } + + if (appendToList != null) + { + appendToList.Add(new XElement(Xhtml.li, paragraphHtml)); + htmlNestedListTypes[currentLevels - 1] = listType; + htmlNestedListItems[currentLevels - 1] = (currentStart, currentNumber, isBidi, appendToList); + previousLevels = currentLevels; + } + } + + var rootList = new List(); + for (int levelIndex = htmlNestedListItems.Count - 1; levelIndex >= 0; levelIndex -= 1) + { + var list = levelIndex == 0 ? rootList : htmlNestedListItems[levelIndex - 1].ListItems; + var (deepStart, _, isBidi, deepList) = htmlNestedListItems[levelIndex]; + var deepListType = htmlNestedListTypes[levelIndex]; + if (!string.IsNullOrEmpty(deepListType)) + { + var startAttribute = deepListType == "ol" ? new XAttribute("start", deepStart) : null; + var listHtml = new XElement(Xhtml.xhtml + deepListType, startAttribute, deepList); + list.Add(listHtml); + var style = new Dictionary + { + ["list-style-type"] = "none", + ["padding"] = "0", + ["margin"] = "0", + }; + listHtml.AddAnnotation(style); + if (isBidi) + { + listHtml.Add(new XAttribute("dir", "rtl")); + } + } + else + { + list.AddRange(deepList); + } + + htmlNestedListItems.RemoveAt(levelIndex); + } + + return rootList.Count > 0 ? rootList[0] : null; + } + + private static object? ConvertToHtmlTransform(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XNode? node, bool suppressTrailingWhiteSpace, decimal currentMarginLeft, Dictionary styleContext = default!) { - var element = node as XElement; - if (element == null) return null; + if (!(node is XElement element)) + { + return null; + } // Transform the w:document element to the XHTML h:html element. // The h:head element is laid out based on the W3C's recommended layout, i.e., @@ -399,29 +448,35 @@ private static object ConvertToHtmlTransform(WordprocessingDocument wordDoc, return new XElement(Xhtml.body, CreateSectionDivs(wordDoc, settings, element)); } - // Transform the w:p element to the XHTML h:h1-h6 or h:p element (if the previous paragraph does not - // have a style separator). + // Transform the w:p element to the XHTML h:h1-h6 or h:p element (if the previous paragraph does not have a style separator). if (element.Name == W.p) { return ProcessParagraph(wordDoc, settings, element, suppressTrailingWhiteSpace, currentMarginLeft); } - // Transform hyperlinks to the XHTML h:a element. + // Transform hyper links to the XHTML h:a element. if (element.Name == W.hyperlink && element.Attribute(R.id) != null) { try { - var a = new XElement(Xhtml.a, - new XAttribute("href", - wordDoc.MainDocumentPart - .HyperlinkRelationships - .First(x => x.Id == (string)element.Attribute(R.id)) - .Uri - ), - element.Elements(W.r).Select(run => ConvertRun(wordDoc, settings, run)) - ); + var href = wordDoc.MainDocumentPart + ?.HyperlinkRelationships + .First(x => x.Id == (string?)element.Attribute(R.id)) + .Uri + .OriginalString + ?? string.Empty; + var anchor = element.Attribute(W.anchor); + if (anchor != null) + { + href += "#" + anchor.Value; + } + + var a = new XElement(Xhtml.a, new XAttribute("href", href), element.Elements(W.r).Select(run => ConvertRun(wordDoc, settings, run))); if (!a.Nodes().Any()) + { a.Add(new XText("")); + } + return a; } catch (UriFormatException) @@ -451,19 +506,13 @@ private static object ConvertToHtmlTransform(WordprocessingDocument wordDoc, // Transform every w:t element to a text node. if (element.Name == W.t) { - // We don't need to convert characters to entities in a UTF-8 document. - // Further, we don't need   entities for significant whitespace - // because we are wrapping the text nodes in elements within - // which all whitespace is significant. - return new XText(element.Value); + return settings.TextHandler.TransformText(element.Value, styleContext); } // Transform symbols to spans if (element.Name == W.sym) { - var cs = (string)element.Attribute(W._char); - var c = Convert.ToInt32(cs, 16); - return new XElement(Xhtml.span, new XEntity(string.Format("#{0}", c))); + return settings.SymbolHandler.TransformSymbol(element, styleContext); } // Transform tabs that have the pt:TabWidth attribute set @@ -475,7 +524,7 @@ private static object ConvertToHtmlTransform(WordprocessingDocument wordDoc, // Transform w:br to h:br. if (element.Name == W.br || element.Name == W.cr) { - return ProcessBreak(element); + return settings.BreakHandler.TransformBreak(element); } // Transform w:noBreakHyphen to '-' @@ -528,93 +577,110 @@ private static object ProcessHyperlinkToBookmark(WordprocessingDocument wordDoc, { var style = new Dictionary(); var a = new XElement(Xhtml.a, - new XAttribute("href", "#" + (string) element.Attribute(W.anchor)), + new XAttribute("href", "#" + (string)element.Attribute(W.anchor)), element.Elements(W.r).Select(run => ConvertRun(wordDoc, settings, run))); if (!a.Nodes().Any()) + { a.Add(new XText("")); + } + style.Add("text-decoration", "none"); a.AddAnnotation(style); return a; } - private static object ProcessBookmarkStart(XElement element) + private static object? ProcessBookmarkStart(XElement element) { - var name = (string) element.Attribute(W.name); - if (name == null) return null; + var name = (string)element.Attribute(W.name); + if (name == null) + { + return null; + } var style = new Dictionary(); var a = new XElement(Xhtml.a, new XAttribute("id", name), new XText("")); if (!a.Nodes().Any()) + { a.Add(new XText("")); + } + style.Add("text-decoration", "none"); a.AddAnnotation(style); return a; } - private static object ProcessTab(XElement element) + private static object? ProcessTab(XElement element) { var tabWidthAtt = element.Attribute(PtOpenXml.TabWidth); - if (tabWidthAtt == null) return null; + if (tabWidthAtt == null) + { + return null; + } - var leader = (string) element.Attribute(PtOpenXml.Leader); - var tabWidth = (decimal) tabWidthAtt; + var leader = (string)element.Attribute(PtOpenXml.Leader); + var tabWidth = (decimal)tabWidthAtt; var style = new Dictionary(); XElement span; if (leader != null) { var leaderChar = "."; if (leader == "hyphen") + { leaderChar = "-"; + } else if (leader == "dot") + { leaderChar = "."; + } else if (leader == "underscore") + { leaderChar = "_"; + } var runContainingTabToReplace = element.Ancestors(W.r).First(); - var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.pt + "FontName") ?? - runContainingTabToReplace.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName"); + var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.pt + "FontName") ?? runContainingTabToReplace.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName"); - var dummyRun = new XElement(W.r, - fontNameAtt, - runContainingTabToReplace.Elements(W.rPr), - new XElement(W.t, leaderChar)); + var dummyRun = new XElement(W.r, fontNameAtt, runContainingTabToReplace.Elements(W.rPr), new XElement(W.t, leaderChar)); var widthOfLeaderChar = CalcWidthOfRunInTwips(dummyRun); - bool forceArial = false; + var forceArial = false; + if (widthOfLeaderChar == 0) { - dummyRun = new XElement(W.r, - new XAttribute(PtOpenXml.FontName, "Arial"), - runContainingTabToReplace.Elements(W.rPr), - new XElement(W.t, leaderChar)); + dummyRun = new XElement(W.r, new XAttribute(PtOpenXml.FontName, "Arial"), runContainingTabToReplace.Elements(W.rPr), new XElement(W.t, leaderChar)); widthOfLeaderChar = CalcWidthOfRunInTwips(dummyRun); forceArial = true; } if (widthOfLeaderChar != 0) { - var numberOfLeaderChars = (int) (Math.Floor((tabWidth*1440)/widthOfLeaderChar)); + var numberOfLeaderChars = (int)Math.Floor(tabWidth * 1440 / widthOfLeaderChar); if (numberOfLeaderChars < 0) + { numberOfLeaderChars = 0; + } + span = new XElement(Xhtml.span, new XAttribute(XNamespace.Xml + "space", "preserve"), " " + "".PadRight(numberOfLeaderChars, leaderChar[0]) + " "); style.Add("margin", "0 0 0 0"); style.Add("padding", "0 0 0 0"); - style.Add("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", tabWidth)); + style.Add("margin-right", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", tabWidth)); style.Add("text-align", "center"); if (forceArial) + { style.Add("font-family", "Arial"); + } } else { span = new XElement(Xhtml.span, new XAttribute(XNamespace.Xml + "space", "preserve"), " "); style.Add("margin", "0 0 0 0"); style.Add("padding", "0 0 0 0"); - style.Add("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", tabWidth)); + style.Add("margin-right", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", tabWidth)); style.Add("text-align", "center"); if (leader == "underscore") { @@ -624,22 +690,7 @@ private static object ProcessTab(XElement element) } else { -#if false - var bidi = element - .Ancestors(W.p) - .Take(1) - .Elements(W.pPr) - .Elements(W.bidi) - .Where(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true) - .FirstOrDefault(); - var isBidi = bidi != null; - if (isBidi) - span = new XElement(Xhtml.span, new XEntity("#x200f")); // RLM - else - span = new XElement(Xhtml.span, new XEntity("#x200e")); // LRM -#else span = new XElement(Xhtml.span, new XEntity("#x00a0")); -#endif style.Add("margin", string.Format(NumberFormatInfo.InvariantInfo, "0 0 0 {0:0.00}in", tabWidth)); style.Add("padding", "0 0 0 0"); } @@ -647,34 +698,6 @@ private static object ProcessTab(XElement element) return span; } - private static object ProcessBreak(XElement element) - { - XElement span = null; - var tabWidth = (decimal?) element.Attribute(PtOpenXml.TabWidth); - if (tabWidth != null) - { - span = new XElement(Xhtml.span); - span.AddAnnotation(new Dictionary - { - { "margin", string.Format(NumberFormatInfo.InvariantInfo, "0 0 0 {0:0.00}in", tabWidth) }, - { "padding", "0 0 0 0" } - }); - } - - var paragraph = element.Ancestors(W.p).FirstOrDefault(); - var isBidi = paragraph != null && - paragraph.Elements(W.pPr).Elements(W.bidi).Any(b => b.Attribute(W.val) == null || - b.Attribute(W.val).ToBoolean() == true); - var zeroWidthChar = isBidi ? new XEntity("#x200f") : new XEntity("#x200e"); - - return new object[] - { - new XElement(Xhtml.br), - zeroWidthChar, - span, - }; - } - private static object ProcessContentControl(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element, decimal currentMarginLeft) { @@ -694,43 +717,53 @@ private static object ProcessContentControl(WordprocessingDocument wordDoc, WmlT // transformed to h:span elements rather than h:p elements and added to // the element (e.g., h:h2) created from the w:p element having the (first) // style separator (i.e., a w:specVanish element). - private static object ProcessParagraph(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, + private static object? ProcessParagraph(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element, bool suppressTrailingWhiteSpace, decimal currentMarginLeft) { // Ignore this paragraph if the previous paragraph has a style separator. // We have already transformed this one together with the previous one. var previousParagraph = element.ElementsBeforeSelf(W.p).LastOrDefault(); - if (HasStyleSeparator(previousParagraph)) return null; + if (HasStyleSeparator(previousParagraph)) + { + return null; + } - var elementName = GetParagraphElementName(element, wordDoc); + var (elementName, attributes) = GetParagraphElementNameAndAttributes(element, wordDoc); var isBidi = IsBidi(element); - var paragraph = (XElement) ConvertParagraph(wordDoc, settings, element, elementName, + var paragraph = ConvertParagraph(wordDoc, settings, element, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi); + paragraph.Add(attributes); - // The paragraph conversion might have created empty spans. - // These can and should be removed because empty spans are - // invalid in HTML5. + // The paragraph conversion might have created empty spans. These can and should be removed because empty spans are invalid in HTML5. paragraph.Elements(Xhtml.span).Where(e => e.IsEmpty).Remove(); foreach (var span in paragraph.Elements(Xhtml.span).ToList()) { var v = span.Value; if (v.Length > 0 && (char.IsWhiteSpace(v[0]) || char.IsWhiteSpace(v[v.Length - 1])) && span.Attribute(XNamespace.Xml + "space") == null) + { span.Add(new XAttribute(XNamespace.Xml + "space", "preserve")); + } } while (HasStyleSeparator(element)) { element = element.ElementsAfterSelf(W.p).FirstOrDefault(); - if (element == null) break; + if (element == null) + { + break; + } elementName = Xhtml.span; isBidi = IsBidi(element); - var span = (XElement)ConvertParagraph(wordDoc, settings, element, elementName, + var span = ConvertParagraph(wordDoc, settings, element, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi); var v = span.Value; if (v.Length > 0 && (char.IsWhiteSpace(v[0]) || char.IsWhiteSpace(v[v.Length - 1])) && span.Attribute(XNamespace.Xml + "space") == null) + { span.Add(new XAttribute(XNamespace.Xml + "space", "preserve")); + } + paragraph.Add(span); } @@ -750,25 +783,22 @@ private static object ProcessTable(WordprocessingDocument wordDoc, WmlToHtmlConv if (type != null && type == "pct") { var w = (int)tblW.Attribute(W._w); - style.AddIfMissing("width", (w / 50) + "%"); + style.AddIfMissing("width", w / 50 + "%"); } } var tblInd = element.Elements(W.tblPr).Elements(W.tblInd).FirstOrDefault(); if (tblInd != null) { var tblIndType = (string)tblInd.Attribute(W.type); - if (tblIndType != null) + if (tblIndType != null && tblIndType == "dxa") { - if (tblIndType == "dxa") + var width = (decimal?)tblInd.Attribute(W._w); + if (width != null) { - var width = (decimal?)tblInd.Attribute(W._w); - if (width != null) - { - style.AddIfMissing("margin-left", - width > 0m - ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width / 20m) - : "0"); - } + style.AddIfMissing("margin-left", + width > 0m + ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width / 20m) + : "0"); } } } @@ -783,17 +813,23 @@ private static object ProcessTable(WordprocessingDocument wordDoc, WmlToHtmlConv element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft))); table.AddAnnotation(style); var jc = (string)element.Elements(W.tblPr).Elements(W.jc).Attributes(W.val).FirstOrDefault() ?? "left"; - XAttribute dir = null; - XAttribute jcToUse = null; + XAttribute? dir = null; + XAttribute? jcToUse = null; if (bidiVisual != null) { dir = new XAttribute("dir", "rtl"); if (jc == "left") + { jcToUse = new XAttribute("align", "right"); + } else if (jc == "right") + { jcToUse = new XAttribute("align", "left"); + } else if (jc == "center") + { jcToUse = new XAttribute("align", "center"); + } } else { @@ -806,35 +842,46 @@ private static object ProcessTable(WordprocessingDocument wordDoc, WmlToHtmlConv return tableDiv; } - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - private static object ProcessTableCell(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element) + private static object? ProcessTableCell(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element) { var style = new Dictionary(); - XAttribute colSpan = null; - XAttribute rowSpan = null; + XAttribute? colSpan = null; + XAttribute? rowSpan = null; var tcPr = element.Element(W.tcPr); if (tcPr != null) { - if ((string) tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart") + if ((string)tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart") { var currentRow = element.Parent.ElementsBeforeSelf(W.tr).Count(); var currentCell = element.ElementsBeforeSelf(W.tc).Count(); var tbl = element.Parent.Parent; - int rowSpanCount = 1; + var rowSpanCount = 1; currentRow += 1; while (true) { var row = tbl.Elements(W.tr).Skip(currentRow).FirstOrDefault(); if (row == null) + { break; + } + var cell2 = row.Elements(W.tc).Skip(currentCell).FirstOrDefault(); if (cell2 == null) + { break; + } + if (cell2.Elements(W.tcPr).Elements(W.vMerge).FirstOrDefault() == null) + { break; - if ((string) cell2.Elements(W.tcPr).Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart") + } + + if ((string)cell2.Elements(W.tcPr).Elements(W.vMerge).Attributes(W.val).FirstOrDefault() == "restart") + { break; + } + currentRow += 1; rowSpanCount += 1; } @@ -842,32 +889,42 @@ private static object ProcessTableCell(WordprocessingDocument wordDoc, WmlToHtml } if (tcPr.Element(W.vMerge) != null && - (string) tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() != "restart") + (string)tcPr.Elements(W.vMerge).Attributes(W.val).FirstOrDefault() != "restart") + { return null; + } if (tcPr.Element(W.vAlign) != null) { - var vAlignVal = (string) tcPr.Elements(W.vAlign).Attributes(W.val).FirstOrDefault(); + var vAlignVal = (string)tcPr.Elements(W.vAlign).Attributes(W.val).FirstOrDefault(); if (vAlignVal == "top") + { style.AddIfMissing("vertical-align", "top"); + } else if (vAlignVal == "center") + { style.AddIfMissing("vertical-align", "middle"); + } else if (vAlignVal == "bottom") + { style.AddIfMissing("vertical-align", "bottom"); + } else + { style.AddIfMissing("vertical-align", "middle"); + } } style.AddIfMissing("vertical-align", "top"); - if ((string) tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "dxa") + if ((string)tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "dxa") { - decimal width = (int) tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault(); - style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width/20m)); + decimal width = (int)tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault(); + style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", width / 20m)); } - if ((string) tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "pct") + if ((string)tcPr.Elements(W.tcW).Attributes(W.type).FirstOrDefault() == "pct") { - decimal width = (int) tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault(); - style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}%", width/50m)); + decimal width = (int)tcPr.Elements(W.tcW).Attributes(W._w).FirstOrDefault(); + style.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}%", width / 50m)); } var tcBorders = tcPr.Element(W.tcBorders); @@ -878,9 +935,11 @@ private static object ProcessTableCell(WordprocessingDocument wordDoc, WmlToHtml CreateStyleFromShd(style, tcPr.Element(W.shd)); - var gridSpan = tcPr.Elements(W.gridSpan).Attributes(W.val).Select(a => (int?) a).FirstOrDefault(); + var gridSpan = tcPr.Elements(W.gridSpan).Attributes(W.val).Select(a => (int?)a).FirstOrDefault(); if (gridSpan != null) - colSpan = new XAttribute("colspan", (int) gridSpan); + { + colSpan = new XAttribute("colspan", (int)gridSpan); + } } style.AddIfMissing("padding-top", "0"); style.AddIfMissing("padding-bottom", "0"); @@ -897,14 +956,20 @@ private static object ProcessTableRow(WordprocessingDocument wordDoc, WmlToHtmlC decimal currentMarginLeft) { var style = new Dictionary(); - int? trHeight = (int?) element.Elements(W.trPr).Elements(W.trHeight).Attributes(W.val).FirstOrDefault(); + var trHeight = (int?)element.Elements(W.trPr).Elements(W.trHeight).Attributes(W.val).FirstOrDefault(); if (trHeight != null) + { style.AddIfMissing("height", - string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", (decimal) trHeight/1440m)); + string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", (decimal)trHeight / 1440m)); + } + var htmlRow = new XElement(Xhtml.tr, element.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft))); if (style.Any()) + { htmlRow.AddAnnotation(style); + } + return htmlRow; } @@ -921,35 +986,54 @@ private static bool IsBidi(XElement element) .Any(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true); } - private static XName GetParagraphElementName(XElement element, WordprocessingDocument wordDoc) + private static (XName ElementName, IEnumerable Attributes) GetParagraphElementNameAndAttributes(XElement element, WordprocessingDocument wordDoc) { var elementName = Xhtml.p; - var styleId = (string) element.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault(); - if (styleId == null) return elementName; + var styleId = (string?)element.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault(); + if (styleId == null) + { + return (elementName, Enumerable.Empty()); + } var style = GetStyle(styleId, wordDoc); - if (style == null) return elementName; + if (style == null) + { + return (elementName, Enumerable.Empty()); + } var outlineLevel = - (int?) style.Elements(W.pPr).Elements(W.outlineLvl).Attributes(W.val).FirstOrDefault(); + (int?)style.Elements(W.pPr).Elements(W.outlineLvl).Attributes(W.val).FirstOrDefault(); + if (outlineLevel != null && outlineLevel > 5 && outlineLevel < 9) + { + elementName = Xhtml.div; + var attributes = new XAttribute[] + { + new XAttribute("role", "heading"), + new XAttribute("aria-level", outlineLevel + 1), + }; + + return (elementName, attributes); + } + if (outlineLevel != null && outlineLevel <= 5) { elementName = Xhtml.xhtml + string.Format("h{0}", outlineLevel + 1); } - return elementName; + return (elementName, Enumerable.Empty()); } - private static XElement GetStyle(string styleId, WordprocessingDocument wordDoc) + private static XElement? GetStyle(string styleId, WordprocessingDocument wordDoc) { - var stylesPart = wordDoc.MainDocumentPart.StyleDefinitionsPart; - if (stylesPart == null) return null; + var stylesPart = wordDoc.MainDocumentPart?.StyleDefinitionsPart; + if (stylesPart == null) + { + return null; + } var styles = stylesPart.GetXDocument().Root; - return styles != null - ? styles.Elements(W.style).FirstOrDefault(s => (string) s.Attribute(W.styleId) == styleId) - : null; + return styles?.Elements(W.style).FirstOrDefault(s => (string)s.Attribute(W.styleId) == styleId); } private static object CreateSectionDivs(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement element) @@ -959,9 +1043,10 @@ private static object CreateSectionDivs(WordprocessingDocument wordDoc, WmlToHtm // for the non-paging transform. var groupedIntoDivs = element .Elements() - .GroupAdjacent(e => { + .GroupAdjacent(e => + { var sectAnnotation = e.Annotation(); - return sectAnnotation != null ? sectAnnotation.SectionElement.ToString() : ""; + return sectAnnotation != null ? sectAnnotation?.SectionElement?.ToString() : string.Empty; }); // note: when creating a paging html converter, need to pay attention to w:rtlGutter element. @@ -969,11 +1054,11 @@ private static object CreateSectionDivs(WordprocessingDocument wordDoc, WmlToHtm .Select(g => { var sectPr = g.First().Annotation(); - XElement bidi = null; + XElement? bidi = null; if (sectPr != null) { - bidi = sectPr - .SectionElement + bidi = sectPr? + .SectionElement? .Elements(W.bidi) .FirstOrDefault(b => b.Attribute(W.val) == null || b.Attribute(W.val).ToBoolean() == true); } @@ -1046,110 +1131,101 @@ private enum BorderType * - topLinePunct * - widowControl * - wordWrap - * */ - private static object ConvertParagraph(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, + private static XElement ConvertParagraph(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement paragraph, XName elementName, bool suppressTrailingWhiteSpace, decimal currentMarginLeft, bool isBidi) { - var style = DefineParagraphStyle(paragraph, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi); + var style = DefineParagraphStyle(paragraph, elementName, suppressTrailingWhiteSpace, currentMarginLeft, isBidi, settings.FontHandler); var rtl = isBidi ? new XAttribute("dir", "rtl") : new XAttribute("dir", "ltr"); var firstMark = isBidi ? new XEntity("#x200f") : null; - // Analyze initial runs to see whether we have a tab, in which case we will render - // a span with a defined width and ignore the tab rather than rendering the text - // preceding the tab and the tab as a span with a computed width. - var firstTabRun = paragraph - .Elements(W.r) - .FirstOrDefault(run => run.Elements(W.tab).Any()); - var elementsPrecedingTab = firstTabRun != null - ? paragraph.Elements(W.r).TakeWhile(e => e != firstTabRun) - .Where(e => e.Elements().Any(c => c.Attributes(PtOpenXml.TabWidth).Any())).ToList() - : Enumerable.Empty().ToList(); + // Analyze initial runs to see whether we have a tab, in which case we will render a span with a defined width and ignore the tab rather than rendering the text preceding the tab and the tab as a span with a computed width. + var firstTabRun = paragraph.Elements(W.r).FirstOrDefault(run => run.Elements(W.tab).Any()); + var elementsPrecedingTab = firstTabRun != null ? paragraph.Elements(W.r).TakeWhile(e => e != firstTabRun).Where(e => e.Elements().Any(c => c.Attributes(PtOpenXml.TabWidth).Any())).ToList() : Enumerable.Empty().ToList(); // TODO: Revisit // For the time being, if a hyperlink field precedes the tab, we'll render it as before. - var hyperlinkPrecedesTab = elementsPrecedingTab - .Elements(W.r) - .Elements(W.instrText) - .Select(e => e.Value) - .Any(value => value != null && value.TrimStart().ToUpper().StartsWith("HYPERLINK")); + var hyperlinkPrecedesTab = elementsPrecedingTab.Elements(W.r).Elements(W.instrText).Select(e => e.Value).Any(value => value != null && value.TrimStart().ToUpper().StartsWith("HYPERLINK")); + if (hyperlinkPrecedesTab) { - var paraElement1 = new XElement(elementName, - rtl, - firstMark, - ConvertContentThatCanContainFields(wordDoc, settings, paragraph.Elements())); + var paraElement1 = new XElement(elementName, rtl, firstMark, ConvertContentThatCanContainFields(wordDoc, settings, paragraph.Elements())); paraElement1.AddAnnotation(style); return paraElement1; } var txElementsPrecedingTab = TransformElementsPrecedingTab(wordDoc, settings, elementsPrecedingTab, firstTabRun); - var elementsSucceedingTab = firstTabRun != null - ? paragraph.Elements().SkipWhile(e => e != firstTabRun).Skip(1) - : paragraph.Elements(); - var paraElement = new XElement(elementName, - rtl, - firstMark, - txElementsPrecedingTab, - ConvertContentThatCanContainFields(wordDoc, settings, elementsSucceedingTab)); + var elementsSucceedingTab = TransformElementsSuceedingTab(paragraph, firstTabRun); + var paraElement = new XElement(elementName, rtl, firstMark, txElementsPrecedingTab, ConvertContentThatCanContainFields(wordDoc, settings, elementsSucceedingTab)); paraElement.AddAnnotation(style); return paraElement; } - private static List TransformElementsPrecedingTab(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, - List elementsPrecedingTab, XElement firstTabRun) + private static IEnumerable TransformElementsSuceedingTab(XElement paragraph, XElement? firstTabRun) { - var tabWidth = firstTabRun != null - ? (decimal?) firstTabRun.Elements(W.tab).Attributes(PtOpenXml.TabWidth).FirstOrDefault() ?? 0m - : 0m; - var precedingElementsWidth = elementsPrecedingTab - .Elements() - .Where(c => c.Attributes(PtOpenXml.TabWidth).Any()) - .Select(e => (decimal) e.Attribute(PtOpenXml.TabWidth)) - .Sum(); + var elementsSuceedingTab = firstTabRun != null ? paragraph.Elements().SkipWhile(e => e != firstTabRun).Skip(1) : paragraph.Elements(); + return elementsSuceedingTab; + } + + private static List TransformElementsPrecedingTab(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, List elementsPrecedingTab, XElement? firstTabRun) + { + var tabWidth = firstTabRun != null ? (decimal?)firstTabRun.Elements(W.tab).Attributes(PtOpenXml.TabWidth).FirstOrDefault() ?? 0m : 0m; + var precedingElementsWidth = elementsPrecedingTab.Elements().Where(c => c.Attributes(PtOpenXml.TabWidth).Any()).Select(e => (decimal)e.Attribute(PtOpenXml.TabWidth)).Sum(); var totalWidth = precedingElementsWidth + tabWidth; - var txElementsPrecedingTab = elementsPrecedingTab - .Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m)) - .ToList(); + var txElementsPrecedingTab = elementsPrecedingTab.Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m)).ToList(); if (txElementsPrecedingTab.Count > 1) { var span = new XElement(Xhtml.span, txElementsPrecedingTab); var spanStyle = new Dictionary { - { "display", "inline-block" }, { "text-indent", "0" }, - { "width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}in", totalWidth) } + { "margin-right", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}in", totalWidth) } }; span.AddAnnotation(spanStyle); } - else if (txElementsPrecedingTab.Count == 1) + else if (txElementsPrecedingTab.Count == 1 && txElementsPrecedingTab.First() is XElement element) + { + var spanStyle = element.Annotation>(); + spanStyle.AddIfMissing("text-indent", "0"); + spanStyle.AddIfMissing("margin-right", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}in", totalWidth)); + } + else if (totalWidth > 0m) { - var element = txElementsPrecedingTab.First() as XElement; - if (element != null) + // Leading tabs in paragraphs + var div = new XElement(Xhtml.span); + var spanStyle = new Dictionary { - var spanStyle = element.Annotation>(); - spanStyle.AddIfMissing("display", "inline-block"); - spanStyle.AddIfMissing("text-indent", "0"); - spanStyle.AddIfMissing("width", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}in", totalWidth)); - } + { "display", "inline-block" }, + { "text-indent", "0" }, + { "margin", string.Format(NumberFormatInfo.InvariantInfo, "0 0 0 {0:0.000}in", totalWidth) } + }; + div.AddAnnotation(spanStyle); + div.SetValue("\u00A0"); + return new List { div }; } + return txElementsPrecedingTab; } - private static Dictionary DefineParagraphStyle(XElement paragraph, XName elementName, - bool suppressTrailingWhiteSpace, decimal currentMarginLeft, bool isBidi) + private static Dictionary DefineParagraphStyle(XElement paragraph, XName elementName, bool suppressTrailingWhiteSpace, decimal currentMarginLeft, bool isBidi, IFontHandler fontHandler) { var style = new Dictionary(); + var styleName = (string)paragraph.Attribute(PtOpenXml.StyleName); - var styleName = (string) paragraph.Attribute(PtOpenXml.StyleName); if (styleName != null) + { style.Add("PtStyleName", styleName); + } var pPr = paragraph.Element(W.pPr); - if (pPr == null) return style; + + if (pPr == null) + { + return style; + } CreateStyleFromSpacing(style, pPr.Element(W.spacing), elementName, suppressTrailingWhiteSpace); CreateStyleFromInd(style, pPr.Element(W.ind), elementName, currentMarginLeft, isBidi); @@ -1167,9 +1243,11 @@ private static Dictionary DefineParagraphStyle(XElement paragrap CreateStyleFromShd(style, pPr.Element(W.shd)); // Pt.FontName - var font = (string) paragraph.Attributes(PtOpenXml.FontName).FirstOrDefault(); + var font = fontHandler.TranslateParagraphStyleFont(paragraph); if (font != null) + { CreateFontCssProperty(font, style); + } DefineFontSize(style, paragraph); DefineLineHeight(style, paragraph); @@ -1188,22 +1266,25 @@ private static Dictionary DefineParagraphStyle(XElement paragrap private static void CreateStyleFromInd(Dictionary style, XElement ind, XName elementName, decimal currentMarginLeft, bool isBidi) { - if (ind == null) return; + if (ind == null) + { + return; + } - var left = (decimal?) ind.Attribute(W.left); + var left = (decimal?)ind.Attribute(W.left); if (left != null && elementName != Xhtml.span) { - var leftInInches = (decimal) left/1440 - currentMarginLeft; + var leftInInches = (decimal)left / 1440 - currentMarginLeft; style.AddIfMissing(isBidi ? "margin-right" : "margin-left", leftInInches > 0m ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", leftInInches) : "0"); } - var right = (decimal?) ind.Attribute(W.right); + var right = (decimal?)ind.Attribute(W.right); if (right != null) { - var rightInInches = (decimal) right/1440; + var rightInInches = (decimal)right / 1440; style.AddIfMissing(isBidi ? "margin-left" : "margin-right", rightInInches > 0m ? string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", rightInInches) @@ -1213,7 +1294,7 @@ private static void CreateStyleFromInd(Dictionary style, XElemen var firstLine = WordprocessingMLUtil.AttributeToTwips(ind.Attribute(W.firstLine)); if (firstLine != null && elementName != Xhtml.span) { - var firstLineInInches = (decimal) firstLine/1440m; + var firstLineInInches = (decimal)firstLine / 1440m; style.AddIfMissing("text-indent", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", firstLineInInches)); } @@ -1221,7 +1302,7 @@ private static void CreateStyleFromInd(Dictionary style, XElemen var hanging = WordprocessingMLUtil.AttributeToTwips(ind.Attribute(W.hanging)); if (hanging != null && elementName != Xhtml.span) { - var hangingInInches = (decimal) -hanging/1440m; + var hangingInInches = (decimal)-hanging / 1440m; style.AddIfMissing("text-indent", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.00}in", hangingInInches)); } @@ -1233,86 +1314,116 @@ private static void CreateStyleFromJc(Dictionary style, XElement { var jcVal = (string)jc.Attributes(W.val).FirstOrDefault() ?? "left"; if (jcVal == "left") + { style.AddIfMissing("text-align", isBidi ? "right" : "left"); + } else if (jcVal == "right") + { style.AddIfMissing("text-align", isBidi ? "left" : "right"); + } else if (jcVal == "center") + { style.AddIfMissing("text-align", "center"); + } else if (jcVal == "both") + { style.AddIfMissing("text-align", "justify"); + } } } private static void CreateStyleFromSpacing(Dictionary style, XElement spacing, XName elementName, bool suppressTrailingWhiteSpace) { - if (spacing == null) return; + if (spacing == null) + { + return; + } - var spacingBefore = (decimal?) spacing.Attribute(W.before); + var spacingBefore = (decimal?)spacing.Attribute(W.before); if (spacingBefore != null && elementName != Xhtml.span) + { style.AddIfMissing("margin-top", spacingBefore > 0m - ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingBefore/20.0m) + ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingBefore / 20.0m) : "0"); + } - var lineRule = (string) spacing.Attribute(W.lineRule); + var lineRule = (string)spacing.Attribute(W.lineRule); if (lineRule == "auto") { - var line = (decimal) spacing.Attribute(W.line); + var line = (decimal)spacing.Attribute(W.line); if (line != 240m) { - var pct = (line/240m)*100m; + var pct = line / 240m * 100m; style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}%", pct)); } } if (lineRule == "exact") { - var line = (decimal) spacing.Attribute(W.line); - var points = line/20m; + var line = (decimal)spacing.Attribute(W.line); + var points = line / 20m; style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", points)); } if (lineRule == "atLeast") { - var line = (decimal) spacing.Attribute(W.line); - var points = line/20m; + var line = (decimal)spacing.Attribute(W.line); + var points = line / 20m; if (points >= 14m) + { style.Add("line-height", string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", points)); + } } var spacingAfter = suppressTrailingWhiteSpace ? 0 : WordprocessingMLUtil.AttributeToTwips(spacing.Attribute(W.after)); if (spacingAfter != null) + { style.AddIfMissing("margin-bottom", spacingAfter > 0m - ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingAfter/20.0m) + ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingAfter / 20.0m) : "0"); + } } private static void CreateStyleFromTextAlignment(Dictionary style, XElement textAlignment) { - if (textAlignment == null) return; + if (textAlignment == null) + { + return; + } var verticalTextAlignment = (string)textAlignment.Attributes(W.val).FirstOrDefault(); - if (verticalTextAlignment == null || verticalTextAlignment == "auto") return; + if (verticalTextAlignment == null || verticalTextAlignment == "auto") + { + return; + } if (verticalTextAlignment == "top") + { style.AddIfMissing("vertical-align", "top"); + } else if (verticalTextAlignment == "center") + { style.AddIfMissing("vertical-align", "middle"); + } else if (verticalTextAlignment == "baseline") + { style.AddIfMissing("vertical-align", "baseline"); + } else if (verticalTextAlignment == "bottom") + { style.AddIfMissing("vertical-align", "bottom"); + } } private static void DefineFontSize(Dictionary style, XElement paragraph) { - var sz = paragraph - .DescendantsTrimmed(W.txbxContent) - .Where(e => e.Name == W.r) - .Select(r => GetFontSize(r)) - .Max(); + var sz = paragraph.DescendantsTrimmed(W.txbxContent).Where(e => e.Name == W.r).Select(r => GetFontSize(r)).Max(); + if (sz != null) + { style.AddIfMissing("font-size", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", sz / 2.0m)); + } } private static void DefineLineHeight(Dictionary style, XElement paragraph) @@ -1323,7 +1434,9 @@ private static void DefineLineHeight(Dictionary style, XElement .Select(run => (string)run.Attribute(PtOpenXml.LanguageType)) .All(lt => lt != "bidi"); if (allRunsAreUniDirectional) + { style.AddIfMissing("line-height", "108%"); + } } /* @@ -1358,52 +1471,55 @@ private static void DefineLineHeight(Dictionary style, XElement * */ - private static object ConvertRun(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement run) + private static object? ConvertRun(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, XElement run) { var rPr = run.Element(W.rPr); if (rPr == null) + { return run.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m)); + } // hide all content that contains the w:rPr/w:webHidden element if (rPr.Element(W.webHidden) != null) + { return null; + } - var style = DefineRunStyle(run); - object content = run.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m)); + var style = DefineRunStyle(run, settings.FontHandler); + object content = run.Elements().Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, 0m, style)); // Wrap content in h:sup or h:sub elements as necessary. if (rPr.Element(W.vertAlign) != null) { - XElement newContent = null; + XElement? newContent = null; var vertAlignVal = (string)rPr.Elements(W.vertAlign).Attributes(W.val).FirstOrDefault(); switch (vertAlignVal) { case "superscript": newContent = new XElement(Xhtml.sup, content); break; + case "subscript": newContent = new XElement(Xhtml.sub, content); break; } if (newContent != null && newContent.Nodes().Any()) + { content = newContent; + } } var langAttribute = GetLangAttribute(run); - XEntity runStartMark; - XEntity runEndMark; - DetermineRunMarks(run, rPr, style, out runStartMark, out runEndMark); + DetermineRunMarks(run, rPr, style, out var runStartMark, out var runEndMark); - if (style.Any() || langAttribute != null || runStartMark != null) + var listItemRun = (string?)run.Attribute(PtOpenXml.ListItemRun); + var listItemRunAttribute = !string.IsNullOrEmpty(listItemRun) ? new XAttribute("data-pt-list-item-run", listItemRun) : null; + if (style.Any() || langAttribute != null || runStartMark != null || listItemRunAttribute != null) { style.AddIfMissing("margin", "0"); style.AddIfMissing("padding", "0"); - var xe = new XElement(Xhtml.span, - langAttribute, - runStartMark, - content, - runEndMark); + var xe = new XElement(Xhtml.span, langAttribute, runStartMark, listItemRunAttribute, content, runEndMark); xe.AddAnnotation(style); content = xe; @@ -1411,84 +1527,103 @@ private static object ConvertRun(WordprocessingDocument wordDoc, WmlToHtmlConver return content; } - [SuppressMessage("ReSharper", "FunctionComplexityOverflow")] - private static Dictionary DefineRunStyle(XElement run) + private static Dictionary DefineRunStyle(XElement run, IFontHandler fontHandler) { var style = new Dictionary(); var rPr = run.Elements(W.rPr).First(); - var styleName = (string) run.Attribute(PtOpenXml.StyleName); + var styleName = (string)run.Attribute(PtOpenXml.StyleName); if (styleName != null) + { style.Add("PtStyleName", styleName); + } // W.bdr - if (rPr.Element(W.bdr) != null && (string) rPr.Elements(W.bdr).Attributes(W.val).FirstOrDefault() != "none") + if (rPr.Element(W.bdr) != null && (string)rPr.Elements(W.bdr).Attributes(W.val).FirstOrDefault() != "none") { style.AddIfMissing("border", "solid windowtext 1.0pt"); style.AddIfMissing("padding", "0"); } // W.color - var color = (string) rPr.Elements(W.color).Attributes(W.val).FirstOrDefault(); + var color = (string)rPr.Elements(W.color).Attributes(W.val).FirstOrDefault(); if (color != null) + { CreateColorProperty("color", color, style); + } // W.highlight - var highlight = (string) rPr.Elements(W.highlight).Attributes(W.val).FirstOrDefault(); + var highlight = (string)rPr.Elements(W.highlight).Attributes(W.val).FirstOrDefault(); if (highlight != null) + { CreateColorProperty("background", highlight, style); + } // W.shd - var shade = (string) rPr.Elements(W.shd).Attributes(W.fill).FirstOrDefault(); + var shade = (string)rPr.Elements(W.shd).Attributes(W.fill).FirstOrDefault(); if (shade != null) + { CreateColorProperty("background", shade, style); + } // Pt.FontName - var sym = run.Element(W.sym); - var font = sym != null - ? (string) sym.Attributes(W.font).FirstOrDefault() - : (string) run.Attributes(PtOpenXml.FontName).FirstOrDefault(); + var font = fontHandler.TranslateRunStyleFont(run); + if (font != null) + { CreateFontCssProperty(font, style); + } // W.sz var languageType = (string)run.Attribute(PtOpenXml.LanguageType); var sz = GetFontSize(languageType, rPr); if (sz != null) - style.AddIfMissing("font-size", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", sz/2.0m)); + { + style.AddIfMissing("font-size", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", sz / 2.0m)); + } // W.caps if (GetBoolProp(rPr, W.caps)) + { style.AddIfMissing("text-transform", "uppercase"); + } // W.smallCaps if (GetBoolProp(rPr, W.smallCaps)) + { style.AddIfMissing("font-variant", "small-caps"); + } // W.spacing - var spacingInTwips = (decimal?) rPr.Elements(W.spacing).Attributes(W.val).FirstOrDefault(); + var spacingInTwips = (decimal?)rPr.Elements(W.spacing).Attributes(W.val).FirstOrDefault(); if (spacingInTwips != null) + { style.AddIfMissing("letter-spacing", spacingInTwips > 0m - ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingInTwips/20) + ? string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", spacingInTwips / 20) : "0"); + } // W.position - var position = (decimal?) rPr.Elements(W.position).Attributes(W.val).FirstOrDefault(); + var position = (decimal?)rPr.Elements(W.position).Attributes(W.val).FirstOrDefault(); if (position != null) { style.AddIfMissing("position", "relative"); - style.AddIfMissing("top", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", -(position/2))); + style.AddIfMissing("top", string.Format(NumberFormatInfo.InvariantInfo, "{0}pt", -(position / 2))); } // W.vanish if (GetBoolProp(rPr, W.vanish) && !GetBoolProp(rPr, W.specVanish)) + { style.AddIfMissing("display", "none"); + } // W.u - if (rPr.Element(W.u) != null && (string) rPr.Elements(W.u).Attributes(W.val).FirstOrDefault() != "none") + if (rPr.Element(W.u) != null && (string)rPr.Elements(W.u).Attributes(W.val).FirstOrDefault() != "none") + { style.AddIfMissing("text-decoration", "underline"); + } // W.i style.AddIfMissing("font-style", GetBoolProp(rPr, W.i) ? "italic" : "normal"); @@ -1498,7 +1633,9 @@ private static Dictionary DefineRunStyle(XElement run) // W.strike if (GetBoolProp(rPr, W.strike) || GetBoolProp(rPr, W.dstrike)) + { style.AddIfMissing("text-decoration", "line-through"); + } return style; } @@ -1519,28 +1656,35 @@ private static Dictionary DefineRunStyle(XElement run) private static decimal? GetFontSize(string languageType, XElement rPr) { - if (rPr == null) return null; - return languageType == "bidi" - ? (decimal?) rPr.Elements(W.szCs).Attributes(W.val).FirstOrDefault() - : (decimal?) rPr.Elements(W.sz).Attributes(W.val).FirstOrDefault(); + if (rPr == null) + { + return null; + } + + return languageType == "bidi" ? (decimal?)rPr.Elements(W.szCs).Attributes(W.val).FirstOrDefault() : (decimal?)rPr.Elements(W.sz).Attributes(W.val).FirstOrDefault(); } - private static void DetermineRunMarks(XElement run, XElement rPr, Dictionary style, out XEntity runStartMark, out XEntity runEndMark) + private static void DetermineRunMarks(XElement run, XElement rPr, Dictionary style, out XEntity? runStartMark, out XEntity? runEndMark) { runStartMark = null; runEndMark = null; // Only do the following for text runs. - if (run.Element(W.t) == null) return; + if (run.Element(W.t) == null) + { + return; + } // Can't add directional marks if the font-family is symbol - they are visible, and display as a ? var addDirectionalMarks = true; - if (style.ContainsKey("font-family")) + if (style.ContainsKey("font-family") && style["font-family"].ToLower() == "symbol") { - if (style["font-family"].ToLower() == "symbol") - addDirectionalMarks = false; + addDirectionalMarks = false; + } + if (!addDirectionalMarks) + { + return; } - if (!addDirectionalMarks) return; var isRtl = rPr.Element(W.rtl) != null; if (isRtl) @@ -1564,22 +1708,31 @@ private static void DetermineRunMarks(XElement run, XElement rPr, Dictionary Enumerable.Repeat(c, - (int?) c.Elements(W.tcPr).Elements(W.gridSpan).Attributes(W.val).FirstOrDefault() ?? 1)) + (int?)c.Elements(W.tcPr).Elements(W.gridSpan).Attributes(W.val).FirstOrDefault() ?? 1)) .ToArray()) .ToArray(); @@ -1627,7 +1783,7 @@ private static void FixTopBorder(XElement[][] ta, XElement thisCell, int x, int var rowAbove = ta[y - 1]; if (x < rowAbove.Length - 1) { - XElement cellAbove = ta[y - 1][x]; + var cellAbove = ta[y - 1][x]; if (cellAbove != null && thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null && cellAbove.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null) @@ -1644,7 +1800,7 @@ private static void FixLeftBorder(XElement[][] ta, XElement thisCell, int x, int { if (x > 0) { - XElement cellLeft = ta[y][x - 1]; + var cellLeft = ta[y][x - 1]; if (cellLeft != null && thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null && cellLeft.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null) @@ -1663,7 +1819,7 @@ private static void FixBottomBorder(XElement[][] ta, XElement thisCell, int x, i var rowBelow = ta[y + 1]; if (x < rowBelow.Length - 1) { - XElement cellBelow = ta[y + 1][x]; + var cellBelow = ta[y + 1][x]; if (cellBelow != null && thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null && cellBelow.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null) @@ -1680,7 +1836,7 @@ private static void FixRightBorder(XElement[][] ta, XElement thisCell, int x, in { if (x < ta[y].Length - 1) { - XElement cellRight = ta[y][x + 1]; + var cellRight = ta[y][x + 1]; if (cellRight != null && thisCell.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null && cellRight.Elements(W.tcPr).Elements(W.tcBorders).FirstOrDefault() != null) @@ -1732,28 +1888,44 @@ private static void FixRightBorder(XElement[][] ta, XElement thisCell, int x, in private static void ResolveCellBorder(XElement border1, XElement border2) { if (border1 == null || border2 == null) + { return; + } + if ((string)border1.Attribute(W.val) == "nil" || (string)border2.Attribute(W.val) == "nil") + { return; + } + if ((string)border1.Attribute(W.sz) == "nil" || (string)border2.Attribute(W.sz) == "nil") + { return; + } var border1Val = (string)border1.Attribute(W.val); var border1Weight = 1; if (BorderNumber.ContainsKey(border1Val)) + { border1Weight = BorderNumber[border1Val]; + } var border2Val = (string)border2.Attribute(W.val); var border2Weight = 1; if (BorderNumber.ContainsKey(border2Val)) + { border2Weight = BorderNumber[border2Val]; + } if (border1Weight != border2Weight) { if (border1Weight < border2Weight) + { BorderOverride(border2, border1); + } else + { BorderOverride(border1, border2); + } } if ((decimal)border1.Attribute(W.sz) > (decimal)border2.Attribute(W.sz)) @@ -1789,10 +1961,16 @@ private static void ResolveCellBorder(XElement border1, XElement border2) var color1Str = (string)border1.Attribute(W.color); if (color1Str == "auto") + { color1Str = "000000"; + } + var color2Str = (string)border2.Attribute(W.color); if (color2Str == "auto") + { color2Str = "000000"; + } + if (color1Str != null && color2Str != null && color1Str != color2Str) { try @@ -1809,8 +1987,15 @@ private static void ResolveCellBorder(XElement border1, XElement border2) BorderOverride(border2, border1); } } - // if the above throws ArgumentException, FormatException, or OverflowException, then abort - catch (Exception) + catch (ArgumentException) + { + // Ignore + } + catch (FormatException) + { + // Ignore + } + catch (OverflowException) { // Ignore } @@ -1821,50 +2006,59 @@ private static void BorderOverride(XElement fromBorder, XElement toBorder) { toBorder.Attribute(W.val).Value = fromBorder.Attribute(W.val).Value; if (fromBorder.Attribute(W.color) != null) + { toBorder.SetAttributeValue(W.color, fromBorder.Attribute(W.color).Value); + } + if (fromBorder.Attribute(W.sz) != null) + { toBorder.SetAttributeValue(W.sz, fromBorder.Attribute(W.sz).Value); + } + if (fromBorder.Attribute(W.themeColor) != null) + { toBorder.SetAttributeValue(W.themeColor, fromBorder.Attribute(W.themeColor).Value); + } + if (fromBorder.Attribute(W.themeTint) != null) + { toBorder.SetAttributeValue(W.themeTint, fromBorder.Attribute(W.themeTint).Value); + } } private static void CalculateSpanWidthForTabs(WordprocessingDocument wordDoc) { - // Note: when implementing a paging version of the HTML transform, this needs to be done - // for all content parts, not just the main document part. + // Note: when implementing a paging version of the HTML transform, this needs to be done for all content parts, not just the main document part. // w:defaultTabStop in settings - var sxd = wordDoc.MainDocumentPart.DocumentSettingsPart.GetXDocument(); - var defaultTabStopValue = (string)sxd.Descendants(W.defaultTabStop).Attributes(W.val).FirstOrDefault(); + var sxd = wordDoc.MainDocumentPart?.DocumentSettingsPart?.GetXDocument(); + var defaultTabStopValue = (string)sxd?.Descendants(W.defaultTabStop).Attributes(W.val).FirstOrDefault(); var defaultTabStop = defaultTabStopValue != null ? WordprocessingMLUtil.StringToTwips(defaultTabStopValue) : 720; - var pxd = wordDoc.MainDocumentPart.GetXDocument(); - var root = pxd.Root; - if (root == null) return; + var pxd = wordDoc.MainDocumentPart?.GetXDocument(); + var root = pxd?.Root; + if (root == null) + { + return; + } var newRoot = (XElement)CalculateSpanWidthTransform(root, defaultTabStop); root.ReplaceWith(newRoot); - wordDoc.MainDocumentPart.PutXDocument(); + wordDoc.MainDocumentPart?.PutXDocument(); } - // TODO: Refactor. This method is way too long. - [SuppressMessage("ReSharper", "FunctionComplexityOverflow")] private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } - // if it is not a paragraph or if there are no tabs in the paragraph, - // then no need to continue processing. - if (element.Name != W.p || - !element.DescendantsTrimmed(W.txbxContent).Where(d => d.Name == W.r).Elements(W.tab).Any()) + // if it is not a paragraph or if there are no tabs in the paragraph, then no need to continue processing. + if (element.Name != W.p || !element.DescendantsTrimmed(W.txbxContent).Where(d => d.Name == W.r).Elements(W.tab).Any()) { // TODO: Revisit. Can we just return the node if it is a paragraph that does not have any tab? - return new XElement(element.Name, - element.Attributes(), - element.Nodes().Select(n => CalculateSpanWidthTransform(n, defaultTabStop))); + return new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => CalculateSpanWidthTransform(n, defaultTabStop))); } var clonedPara = new XElement(element); @@ -1879,25 +2073,28 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop var left = WordprocessingMLUtil.AttributeToTwips(ind.Attribute(W.left)); if (left != null) + { leftInTwips = (int)left; + } var firstLine = 0; var firstLineAtt = WordprocessingMLUtil.AttributeToTwips(ind.Attribute(W.firstLine)); if (firstLineAtt != null) + { firstLine = (int)firstLineAtt; + } var hangingAtt = WordprocessingMLUtil.AttributeToTwips(ind.Attribute(W.hanging)); if (hangingAtt != null) + { firstLine = -(int)hangingAtt; + } firstInTwips = leftInTwips + firstLine; } // calculate the tab stops, in twips - var tabs = clonedPara - .Elements(W.pPr) - .Elements(W.tabs) - .FirstOrDefault(); + var tabs = clonedPara.Elements(W.pPr).Elements(W.tabs).FirstOrDefault(); if (tabs == null) { @@ -1943,11 +2140,13 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop currentElement.Add(new XAttribute(PtOpenXml.TabWidth, string.Format(NumberFormatInfo.InvariantInfo, - "{0:0.000}", (decimal)firstInTwips / 1440m))); + "{0:0.000}", firstInTwips / 1440m))); currentElementIdx++; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } } if (currentElement.Name == W.tab) @@ -1958,16 +2157,16 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop var testAmount = twipCounter; - var tabAfterText = tabs - .Elements(W.tab) - .FirstOrDefault(t => WordprocessingMLUtil.StringToTwips((string)t.Attribute(W.pos)) > testAmount); + var tabAfterText = tabs.Elements(W.tab).FirstOrDefault(t => WordprocessingMLUtil.StringToTwips((string)t.Attribute(W.pos)) > testAmount); if (tabAfterText == null) { // something has gone wrong, so put 1/2 inch in if (currentElement.Attribute(PtOpenXml.TabWidth) == null) - currentElement.Add( - new XAttribute(PtOpenXml.TabWidth, 720m)); + { + currentElement.Add(new XAttribute(PtOpenXml.TabWidth, 720m)); + } + break; } @@ -1978,12 +2177,7 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop .Skip(currentElementIdx + 1); // take all the content until another tab, br, or cr - var textElementsToMeasure = textAfterElements - .TakeWhile(z => - z.Name != W.tab && - z.Name != W.br && - z.Name != W.cr) - .ToList(); + var textElementsToMeasure = textAfterElements.TakeWhile(z => z.Name != W.tab && z.Name != W.br && z.Name != W.cr).ToList(); var textAfterTab = textElementsToMeasure .Where(z => z.Name == W.t) @@ -1998,20 +2192,27 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop var widthOfTextAfterTab = CalcWidthOfRunInTwips(dummyRun2); var delta2 = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) - widthOfTextAfterTab - twipCounter; if (delta2 < 0) + { delta2 = 0; + } + currentElement.Add( new XAttribute(PtOpenXml.TabWidth, - string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)), + string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", delta2 / 1440m)), GetLeader(tabAfterText)); twipCounter = Math.Max(WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)), twipCounter + widthOfTextAfterTab); var lastElement = textElementsToMeasure.LastOrDefault(); if (lastElement == null) - break; // we're done + { + break; + } currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } continue; } @@ -2045,10 +2246,13 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop var widthOfMantissa = CalcWidthOfRunInTwips(dummyRun4); var delta2 = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) - widthOfMantissa - twipCounter; if (delta2 < 0) + { delta2 = 0; + } + currentElement.Add( new XAttribute(PtOpenXml.TabWidth, - string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)), + string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", delta2 / 1440m)), GetLeader(tabAfterText)); var decims = textAfterTab.Substring(textAfterTab.IndexOf('.')); @@ -2062,11 +2266,15 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop var lastElement = textElementsToMeasure.LastOrDefault(); if (lastElement == null) - break; // we're done + { + break; + } currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } continue; } @@ -2080,20 +2288,27 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop var widthOfTextAfterTab = CalcWidthOfRunInTwips(dummyRun2); var delta2 = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) - widthOfTextAfterTab - twipCounter; if (delta2 < 0) + { delta2 = 0; + } + currentElement.Add( new XAttribute(PtOpenXml.TabWidth, - string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)), + string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", delta2 / 1440m)), GetLeader(tabAfterText)); twipCounter = Math.Max(WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)), twipCounter + widthOfTextAfterTab); var lastElement = textElementsToMeasure.LastOrDefault(); if (lastElement == null) - break; // we're done + { + break; + } currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } continue; } @@ -2122,22 +2337,29 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop new XElement(W.t, textAfterTab)); var widthOfText = CalcWidthOfRunInTwips(dummyRun4); - var delta2 = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) - (widthOfText / 2) - twipCounter; + var delta2 = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) - widthOfText / 2 - twipCounter; if (delta2 < 0) + { delta2 = 0; + } + currentElement.Add( new XAttribute(PtOpenXml.TabWidth, - string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta2 / 1440m)), + string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", delta2 / 1440m)), GetLeader(tabAfterText)); twipCounter = Math.Max(WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) + widthOfText / 2, twipCounter + widthOfText); var lastElement = textElementsToMeasure.LastOrDefault(); if (lastElement == null) - break; // we're done + { + break; + } currentElementIdx = Array.IndexOf(contentToMeasure, lastElement) + 1; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } continue; } @@ -2146,13 +2368,15 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop var delta = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)) - twipCounter; currentElement.Add( new XAttribute(PtOpenXml.TabWidth, - string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal)delta / 1440m)), + string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", delta / 1440m)), GetLeader(tabAfterText)); twipCounter = WordprocessingMLUtil.StringToTwips((string)tabAfterText.Attribute(W.pos)); currentElementIdx++; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } continue; } @@ -2160,36 +2384,24 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop if (currentElement.Name == W.t) { - // TODO: Revisit. This is a quick fix because it doesn't work on Azure. - // Given the changes we've made elsewhere, though, this is not required - // for the first tab at least. We could also enhance that other change - // to deal with all tabs. - //var runContainingTabToReplace = currentElement.Parent; - //var paragraphForRun = runContainingTabToReplace.Ancestors(W.p).First(); - //var fontNameAtt = runContainingTabToReplace.Attribute(PtOpenXml.FontName) ?? - // paragraphForRun.Attribute(PtOpenXml.FontName); - //var languageTypeAtt = runContainingTabToReplace.Attribute(PtOpenXml.LanguageType) ?? - // paragraphForRun.Attribute(PtOpenXml.LanguageType); - - //var dummyRun3 = new XElement(W.r, fontNameAtt, languageTypeAtt, - // runContainingTabToReplace.Elements(W.rPr), - // currentElement); - //var widthOfText = CalcWidthOfRunInTwips(dummyRun3); const int widthOfText = 0; - currentElement.Add(new XAttribute(PtOpenXml.TabWidth, - string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", (decimal) widthOfText/1440m))); + currentElement.Add(new XAttribute(PtOpenXml.TabWidth, string.Format(NumberFormatInfo.InvariantInfo, "{0:0.000}", widthOfText / 1440m))); twipCounter += widthOfText; currentElementIdx++; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } continue; } currentElementIdx++; if (currentElementIdx >= contentToMeasure.Length) - break; // we're done + { + break; + } } return new XElement(element.Name, @@ -2197,11 +2409,14 @@ private static object CalculateSpanWidthTransform(XNode node, int defaultTabStop element.Nodes().Select(n => CalculateSpanWidthTransform(n, defaultTabStop))); } - private static XAttribute GetLeader(XElement tabAfterText) + private static XAttribute? GetLeader(XElement tabAfterText) { var leader = (string)tabAfterText.Attribute(W.leader); if (leader == null) + { return null; + } + return new XAttribute(PtOpenXml.Leader, leader); } @@ -2215,7 +2430,10 @@ private static XElement AddDefaultTabsAfterLastTab(XElement tabs, int defaultTab if (lastTabElement != null) { if (defaultTabStop == 0) + { defaultTabStop = 720; + } + var rangeStart = WordprocessingMLUtil.StringToTwips((string)lastTabElement.Attribute(W.pos)) / defaultTabStop + 1; var tempTabs = new XElement(W.tabs, tabs.Elements().Where(t => (string)t.Attribute(W.val) != "clear" && (string)t.Attribute(W.val) != "bar"), @@ -2239,7 +2457,7 @@ private static XElement AddDefaultTabsAfterLastTab(XElement tabs, int defaultTab } private static readonly HashSet UnknownFonts = new HashSet(); - private static HashSet _knownFamilies; + private static HashSet? _knownFamilies; private static HashSet KnownFamilies { @@ -2248,9 +2466,11 @@ private static HashSet KnownFamilies if (_knownFamilies == null) { _knownFamilies = new HashSet(); - var families = FontFamily.Families; + var families = SixLabors.Fonts.SystemFonts.Families; foreach (var fam in families) + { _knownFamilies.Add(fam.Name); + } } return _knownFamilies; } @@ -2261,25 +2481,35 @@ private static int CalcWidthOfRunInTwips(XElement r) var fontName = (string)r.Attribute(PtOpenXml.pt + "FontName") ?? (string)r.Ancestors(W.p).First().Attribute(PtOpenXml.pt + "FontName"); if (fontName == null) + { throw new OpenXmlPowerToolsException("Internal Error, should have FontName attribute"); + } + if (UnknownFonts.Contains(fontName)) + { return 0; + } var rPr = r.Element(W.rPr); if (rPr == null) + { throw new OpenXmlPowerToolsException("Internal Error, should have run properties"); + } var sz = GetFontSize(r) ?? 22m; // unknown font families will throw ArgumentException, in which case just return 0 if (!KnownFamilies.Contains(fontName)) + { return 0; + } // in theory, all unknown fonts are found by the above test, but if not... - FontFamily ff; + SixLabors.Fonts.FontFamily ff; + try { - ff = new FontFamily(fontName); + ff = SixLabors.Fonts.SystemFonts.Families.Single(font => font.Name == fontName); } catch (ArgumentException) { @@ -2288,46 +2518,57 @@ private static int CalcWidthOfRunInTwips(XElement r) return 0; } - var fs = FontStyle.Regular; + var fs = SixLabors.Fonts.FontStyle.Regular; if (GetBoolProp(rPr, W.b) || GetBoolProp(rPr, W.bCs)) - fs |= FontStyle.Bold; + { + fs |= SixLabors.Fonts.FontStyle.Bold; + } + if (GetBoolProp(rPr, W.i) || GetBoolProp(rPr, W.iCs)) - fs |= FontStyle.Italic; - - // Appended blank as a quick fix to accommodate   that will get - // appended to some layout-critical runs such as list item numbers. - // In some cases, this might not be required or even wrong, so this - // must be revisited. - // TODO: Revisit. - var runText = r.DescendantsTrimmed(W.txbxContent) - .Where(e => e.Name == W.t) - .Select(t => (string) t) - .StringConcatenate() + " "; - - var tabLength = r.DescendantsTrimmed(W.txbxContent) - .Where(e => e.Name == W.tab) - .Select(t => (decimal)t.Attribute(PtOpenXml.TabWidth)) - .Sum(); + { + fs |= SixLabors.Fonts.FontStyle.Italic; + } + + // Appended blank as a quick fix to accommodate   that will get appended to some layout-critical runs such as list item numbers. In some cases, this might not be required or even wrong, so this must be revisited. + var runText = r.DescendantsTrimmed(W.txbxContent).Where(e => e.Name == W.t).Select(t => (string)t).StringConcatenate() + " "; + + var tabLength = r.DescendantsTrimmed(W.txbxContent).Where(e => e.Name == W.tab).Select(t => (decimal)t.Attribute(PtOpenXml.TabWidth)).Sum(); if (runText.Length == 0 && tabLength == 0) + { return 0; + } - int multiplier = 1; + var multiplier = 1; if (runText.Length <= 2) + { multiplier = 100; + } else if (runText.Length <= 4) + { multiplier = 50; + } else if (runText.Length <= 8) + { multiplier = 25; + } else if (runText.Length <= 16) + { multiplier = 12; + } else if (runText.Length <= 32) + { multiplier = 6; + } + if (multiplier != 1) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < multiplier; i++) + var sb = new StringBuilder(); + for (var i = 0; i < multiplier; i++) + { sb.Append(runText); + } + runText = sb.ToString(); } @@ -2342,7 +2583,10 @@ private static void InsertAppropriateNonbreakingSpaces(WordprocessingDocument wo { var pxd = part.GetXDocument(); var root = pxd.Root; - if (root == null) return; + if (root == null) + { + return; + } var newRoot = (XElement)InsertAppropriateNonbreakingSpacesTransform(root); root.ReplaceWith(newRoot); @@ -2351,15 +2595,11 @@ private static void InsertAppropriateNonbreakingSpaces(WordprocessingDocument wo } // Non-breaking spaces are not required if we use appropriate CSS, i.e., "white-space: pre-wrap;". - // We only need to make sure that empty w:p elements are translated into non-empty h:p elements, - // because empty h:p elements would be ignored by browsers. - // Further, in addition to not being required, non-breaking spaces would change the layout behavior - // of spans having consecutive spaces. Therefore, avoiding non-breaking spaces has the additional - // benefit of leading to a more faithful representation of the Word document in HTML. + // We only need to make sure that empty w:p elements are translated into non-empty h:p elements, because empty h:p elements would be ignored by browsers. + // Further, in addition to not being required, non-breaking spaces would change the layout behavior of spans having consecutive spaces. Therefore, avoiding non-breaking spaces has the additional benefit of leading to a more faithful representation of the Word document in HTML. private static object InsertAppropriateNonbreakingSpacesTransform(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { // child content of run to look for // W.br @@ -2385,7 +2625,7 @@ private static object InsertAppropriateNonbreakingSpacesTransform(XNode node) // Translate empty paragraphs to paragraphs having one run with // a normal space. A non-breaking space, i.e., \x00A0, is not // required if we use appropriate CSS. - bool hasContent = element + var hasContent = element .Elements() .Where(e => e.Name != W.pPr) .DescendantsAndSelf() @@ -2407,13 +2647,15 @@ private static object InsertAppropriateNonbreakingSpacesTransform(XNode node) e.Name == W.yearLong || e.Name == W.yearShort ); - if (hasContent == false) + if (!hasContent) + { return new XElement(element.Name, element.Attributes(), element.Nodes().Select(n => InsertAppropriateNonbreakingSpacesTransform(n)), new XElement(W.r, element.Elements(W.pPr).Elements(W.rPr), new XElement(W.t, " "))); + } } return new XElement(element.Name, @@ -2423,20 +2665,26 @@ private static object InsertAppropriateNonbreakingSpacesTransform(XNode node) return node; } - private class SectionAnnotation + private sealed class SectionAnnotation { - public XElement SectionElement; + public XElement? SectionElement; } private static void AnnotateForSections(WordprocessingDocument wordDoc) { - var xd = wordDoc.MainDocumentPart.GetXDocument(); + var xd = wordDoc?.MainDocumentPart?.GetXDocument(); - var document = xd.Root; - if (document == null) return; + var document = xd?.Root; + if (document == null) + { + return; + } var body = document.Element(W.body); - if (body == null) return; + if (body == null) + { + return; + } // move last sectPr into last paragraph var lastSectPr = body.Elements(W.sectPr).LastOrDefault(); @@ -2451,15 +2699,19 @@ private static void AnnotateForSections(WordprocessingDocument wordDoc) { var lastParaProps = lastPara.Element(W.pPr); if (lastParaProps != null) + { lastParaProps.Add(lastSectPr); + } else + { lastPara.Add(new XElement(W.pPr, lastSectPr)); + } lastSectPr.Remove(); } } - var reverseDescendants = xd.Descendants().Reverse().ToList(); + var reverseDescendants = xd?.Descendants().Reverse().ToList(); var currentSection = InitializeSectionAnnotation(reverseDescendants); foreach (var d in reverseDescendants) @@ -2467,7 +2719,9 @@ private static void AnnotateForSections(WordprocessingDocument wordDoc) if (d.Name == W.sectPr) { if (d.Attribute(XNamespace.Xmlns + "w") == null) + { d.Add(new XAttribute(XNamespace.Xmlns + "w", W.w)); + } currentSection = new SectionAnnotation() { @@ -2475,7 +2729,9 @@ private static void AnnotateForSections(WordprocessingDocument wordDoc) }; } else + { d.AddAnnotation(currentSection); + } } } @@ -2487,10 +2743,13 @@ private static SectionAnnotation InitializeSectionAnnotation(IEnumerable elements) { + var htmlNestedListTypes = new List(); return elements.GroupAdjacent(e => { var pBdr = e.Elements(W.pPr).Elements(W.pBdr).FirstOrDefault(); @@ -2525,16 +2786,19 @@ private static object CreateBorderDivs(WordprocessingDocument wordDoc, WmlToHtml var indStr = string.Empty; var ind = e.Elements(W.pPr).Elements(W.ind).FirstOrDefault(); if (ind != null) + { indStr = ind.ToString(SaveOptions.DisableFormatting); + } + return pBdr.ToString(SaveOptions.DisableFormatting) + indStr; } return e.Name == W.tbl ? "table" : string.Empty; }) .Select(g => { - if (g.Key == string.Empty) + if (string.IsNullOrEmpty(g.Key)) { - return (object) GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, 0m); + return (object)GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, 0m, htmlNestedListTypes); } if (g.Key == "table") { @@ -2552,8 +2816,8 @@ private static object CreateBorderDivs(WordprocessingDocument wordDoc, WmlToHtml var ind = pPr.Element(W.ind); if (ind != null) { - var leftInInches = (decimal?) ind.Attribute(W.left)/1440m ?? 0; - var hangingInInches = -(decimal?) ind.Attribute(W.hanging)/1440m ?? 0; + var leftInInches = (decimal?)ind.Attribute(W.left) / 1440m ?? 0; + var hangingInInches = -(decimal?)ind.Attribute(W.hanging) / 1440m ?? 0; currentMarginLeft = leftInInches + hangingInInches; style.AddIfMissing("margin-left", @@ -2563,28 +2827,38 @@ private static object CreateBorderDivs(WordprocessingDocument wordDoc, WmlToHtml } var div = new XElement(Xhtml.div, - GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, currentMarginLeft)); + GroupAndVerticallySpaceNumberedParagraphs(wordDoc, settings, g, currentMarginLeft, htmlNestedListTypes)); div.AddAnnotation(style); return div; }) .ToList(); } - private static IEnumerable GroupAndVerticallySpaceNumberedParagraphs(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, - IEnumerable elements, decimal currentMarginLeft) + private static IEnumerable GroupAndVerticallySpaceNumberedParagraphs( + WordprocessingDocument wordDoc, + WmlToHtmlConverterSettings settings, + IEnumerable elements, + decimal currentMarginLeft, + IList htmlNestedListTypes) { var grouped = elements .GroupAdjacent(e => { var abstractNumId = (string)e.Attribute(PtOpenXml.pt + "AbstractNumId"); if (abstractNumId != null) + { return "num:" + abstractNumId; + } + var contextualSpacing = e.Elements(W.pPr).Elements(W.contextualSpacing).FirstOrDefault(); if (contextualSpacing != null) { var styleName = (string)e.Elements(W.pPr).Elements(W.pStyle).Attributes(W.val).FirstOrDefault(); if (styleName == null) + { return ""; + } + return "sty:" + styleName; } return ""; @@ -2593,65 +2867,85 @@ private static IEnumerable GroupAndVerticallySpaceNumberedParagraphs(Wor var newContent = grouped .Select(g => { - if (g.Key == "") + if (string.IsNullOrEmpty(g.Key)) + { return g.Select(e => ConvertToHtmlTransform(wordDoc, settings, e, false, currentMarginLeft)); + } + + if (g.Key.StartsWith("num:", StringComparison.Ordinal)) + { + return ListAwareConvertToHtmlTransform(wordDoc, settings, g, currentMarginLeft, htmlNestedListTypes); + } + var last = g.Count() - 1; return g.Select((e, i) => ConvertToHtmlTransform(wordDoc, settings, e, i != last, currentMarginLeft)); }); - return (IEnumerable)newContent; + return newContent; } private class BorderMappingInfo { - public string CssName; + public string CssName = string.Empty; public decimal CssSize; } private static readonly Dictionary BorderStyleMap = new Dictionary() { - { "single", new BorderMappingInfo() { CssName = "solid", CssSize = 1.0m }}, - { "dotted", new BorderMappingInfo() { CssName = "dotted", CssSize = 1.0m }}, - { "dashSmallGap", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }}, - { "dashed", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }}, - { "dotDash", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }}, - { "dotDotDash", new BorderMappingInfo() { CssName = "dashed", CssSize = 1.0m }}, - { "double", new BorderMappingInfo() { CssName = "double", CssSize = 2.5m }}, - { "triple", new BorderMappingInfo() { CssName = "double", CssSize = 2.5m }}, - { "thinThickSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 4.5m }}, - { "thickThinSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 4.5m }}, - { "thinThickThinSmallGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }}, - { "thickThinMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }}, - { "thinThickMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 6.0m }}, - { "thinThickThinMediumGap", new BorderMappingInfo() { CssName = "double", CssSize = 9.0m }}, - { "thinThickLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }}, - { "thickThinLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }}, - { "thinThickThinLargeGap", new BorderMappingInfo() { CssName = "double", CssSize = 9.0m }}, - { "wave", new BorderMappingInfo() { CssName = "solid", CssSize = 3.0m }}, - { "doubleWave", new BorderMappingInfo() { CssName = "double", CssSize = 5.25m }}, - { "dashDotStroked", new BorderMappingInfo() { CssName = "solid", CssSize = 3.0m }}, - { "threeDEmboss", new BorderMappingInfo() { CssName = "ridge", CssSize = 6.0m }}, - { "threeDEngrave", new BorderMappingInfo() { CssName = "groove", CssSize = 6.0m }}, - { "outset", new BorderMappingInfo() { CssName = "outset", CssSize = 4.5m }}, - { "inset", new BorderMappingInfo() { CssName = "inset", CssSize = 4.5m }}, + { "single", new BorderMappingInfo { CssName = "solid", CssSize = 1.0m }}, + { "dotted", new BorderMappingInfo { CssName = "dotted", CssSize = 1.0m }}, + { "dashSmallGap", new BorderMappingInfo { CssName = "dashed", CssSize = 1.0m }}, + { "dashed", new BorderMappingInfo { CssName = "dashed", CssSize = 1.0m }}, + { "dotDash", new BorderMappingInfo { CssName = "dashed", CssSize = 1.0m }}, + { "dotDotDash", new BorderMappingInfo { CssName = "dashed", CssSize = 1.0m }}, + { "double", new BorderMappingInfo { CssName = "double", CssSize = 2.5m }}, + { "triple", new BorderMappingInfo { CssName = "double", CssSize = 2.5m }}, + { "thinThickSmallGap", new BorderMappingInfo { CssName = "double", CssSize = 4.5m }}, + { "thickThinSmallGap", new BorderMappingInfo { CssName = "double", CssSize = 4.5m }}, + { "thinThickThinSmallGap", new BorderMappingInfo { CssName = "double", CssSize = 6.0m }}, + { "thickThinMediumGap", new BorderMappingInfo { CssName = "double", CssSize = 6.0m }}, + { "thinThickMediumGap", new BorderMappingInfo { CssName = "double", CssSize = 6.0m }}, + { "thinThickThinMediumGap", new BorderMappingInfo { CssName = "double", CssSize = 9.0m }}, + { "thinThickLargeGap", new BorderMappingInfo { CssName = "double", CssSize = 5.25m }}, + { "thickThinLargeGap", new BorderMappingInfo { CssName = "double", CssSize = 5.25m }}, + { "thinThickThinLargeGap", new BorderMappingInfo { CssName = "double", CssSize = 9.0m }}, + { "wave", new BorderMappingInfo { CssName = "solid", CssSize = 3.0m }}, + { "doubleWave", new BorderMappingInfo { CssName = "double", CssSize = 5.25m }}, + { "dashDotStroked", new BorderMappingInfo { CssName = "solid", CssSize = 3.0m }}, + { "threeDEmboss", new BorderMappingInfo { CssName = "ridge", CssSize = 6.0m }}, + { "threeDEngrave", new BorderMappingInfo { CssName = "groove", CssSize = 6.0m }}, + { "outset", new BorderMappingInfo { CssName = "outset", CssSize = 4.5m }}, + { "inset", new BorderMappingInfo { CssName = "inset", CssSize = 4.5m }}, }; private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictionary style, BorderType borderType) { string whichSide; if (sideXName == W.top) + { whichSide = "top"; + } else if (sideXName == W.right) + { whichSide = "right"; + } else if (sideXName == W.bottom) + { whichSide = "bottom"; + } else + { whichSide = "left"; + } + if (pBdr == null) { style.Add("border-" + whichSide, "none"); if (borderType == BorderType.Cell && (whichSide == "left" || whichSide == "right")) + { style.Add("padding-" + whichSide, "5.4pt"); + } + return; } @@ -2659,9 +2953,11 @@ private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictiona if (side == null) { style.Add("border-" + whichSide, "none"); - if (borderType == BorderType.Cell && - (whichSide == "left" || whichSide == "right")) + if (borderType == BorderType.Cell && (whichSide == "left" || whichSide == "right")) + { style.Add("padding-" + whichSide, "5.4pt"); + } + return; } var type = (string)side.Attribute(W.val); @@ -2670,13 +2966,13 @@ private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictiona style.Add("border-" + whichSide + "-style", "none"); var space = (decimal?)side.Attribute(W.space) ?? 0; - if (borderType == BorderType.Cell && - (whichSide == "left" || whichSide == "right")) - if (space < 5.4m) - space = 5.4m; + if (borderType == BorderType.Cell && (whichSide == "left" || whichSide == "right") && space < 5.4m) + { + space = 5.4m; + } + style.Add("padding-" + whichSide, space == 0 ? "0" : string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", space)); - } else { @@ -2684,11 +2980,15 @@ private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictiona var space = (decimal?)side.Attribute(W.space) ?? 0; var color = (string)side.Attribute(W.color); if (color == null || color == "auto") + { color = "windowtext"; + } else + { color = ConvertColor(color); + } - decimal borderWidthInPoints = Math.Max(1m, Math.Min(96m, Math.Max(2m, sz)) / 8m); + var borderWidthInPoints = Math.Max(1m, Math.Min(96m, Math.Max(2m, sz)) / 8m); var borderStyle = "solid"; if (BorderStyleMap.ContainsKey(type)) @@ -2698,49 +2998,72 @@ private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictiona if (type == "double") { if (sz <= 8) + { borderWidthInPoints = 2.5m; + } else if (sz <= 18) + { borderWidthInPoints = 6.75m; + } else + { borderWidthInPoints = sz / 3m; + } } else if (type == "triple") { if (sz <= 8) + { borderWidthInPoints = 8m; + } else if (sz <= 18) + { borderWidthInPoints = 11.25m; + } else + { borderWidthInPoints = 11.25m; + } } else if (type.ToLower().Contains("dash")) { if (sz <= 4) + { borderWidthInPoints = 1m; + } else if (sz <= 12) + { borderWidthInPoints = 1.5m; + } else + { borderWidthInPoints = 2m; + } } else if (type != "single") + { borderWidthInPoints = borderInfo.CssSize; + } } if (type == "outset" || type == "inset") + { color = ""; + } + var borderWidth = string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", borderWidthInPoints); style.Add("border-" + whichSide, borderStyle + " " + color + " " + borderWidth); if (borderType == BorderType.Cell && - (whichSide == "left" || whichSide == "right")) - if (space < 5.4m) - space = 5.4m; + (whichSide == "left" || whichSide == "right") && space < 5.4m) + { + space = 5.4m; + } - style.Add("padding-" + whichSide, - space == 0 ? "0" : string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", space)); + style.Add("padding-" + whichSide, space == 0 ? "0" : string.Format(NumberFormatInfo.InvariantInfo, "{0:0.0}pt", space)); } } - private static readonly Dictionary> ShadeMapper = new Dictionary>() + private static readonly Dictionary> ShadeMapper = new Dictionary>() { { "auto", (c, f) => c }, { "clear", (c, f) => f }, @@ -2787,12 +3110,21 @@ private static void GenerateBorderStyle(XElement pBdr, XName sideXName, Dictiona private static string ConvertColorFillPct(string color, string fill, double pct) { if (color == "auto") + { color = "000000"; + } + if (fill == "auto") + { fill = "ffffff"; + } + var key = color + fill + pct.ToString(CultureInfo.InvariantCulture); if (ShadeCache.ContainsKey(key)) + { return ShadeCache[key]; + } + var fillRed = Convert.ToInt32(fill.Substring(0, 2), 16); var fillGreen = Convert.ToInt32(fill.Substring(2, 2), 16); var fillBlue = Convert.ToInt32(fill.Substring(4, 2), 16); @@ -2810,8 +3142,11 @@ private static string ConvertColorFillPct(string color, string fill, double pct) private static void CreateStyleFromShd(Dictionary style, XElement shd) { if (shd == null) + { return; - var shadeType = (string)shd.Attribute(W.val); + } + + var shadeType = (string)shd.Attribute(W.val) ?? "clear"; var color = (string)shd.Attribute(W.color); var fill = (string)shd.Attribute(W.fill); if (ShadeMapper.ContainsKey(shadeType)) @@ -2822,7 +3157,9 @@ private static void CreateStyleFromShd(Dictionary style, XElemen { var cvtColor = ConvertColor(color); if (!string.IsNullOrEmpty(cvtColor)) + { style.AddIfMissing("background", cvtColor); + } } } @@ -2850,17 +3187,24 @@ private static void CreateStyleFromShd(Dictionary style, XElemen private static void CreateColorProperty(string propertyName, string color, Dictionary style) { if (color == null) + { return; + } // "auto" color is black for "color" and white for "background" property. if (color == "auto") + { color = propertyName == "color" ? "black" : "white"; + } if (NamedColors.ContainsKey(color)) { var lc = NamedColors[color]; - if (lc == "") + if (string.IsNullOrEmpty(lc)) + { return; + } + style.AddIfMissing(propertyName, lc); return; } @@ -2873,13 +3217,18 @@ private static string ConvertColor(string color) // As this method is only called for "background" colors, "auto" is translated // to "white" and never "black". if (color == "auto") + { color = "white"; + } if (NamedColors.ContainsKey(color)) { var lc = NamedColors[color]; - if (lc == "") + if (string.IsNullOrEmpty(lc)) + { return "black"; + } + return lc; } return "#" + color; @@ -2934,23 +3283,33 @@ private static bool GetBoolProp(XElement runProps, XName xName) { var p = runProps.Element(xName); if (p == null) + { return false; + } + var v = p.Attribute(W.val); if (v == null) + { return true; + } + var s = v.Value.ToLower(); if (s == "0" || s == "false") + { return false; + } + if (s == "1" || s == "true") + { return true; + } + return false; } - private static object ConvertContentThatCanContainFields(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, - IEnumerable elements) + private static object ConvertContentThatCanContainFields(WordprocessingDocument wordDoc, WmlToHtmlConverterSettings settings, IEnumerable elements) { - var grouped = elements - .GroupAdjacent(e => + var grouped = elements.GroupAdjacent(e => { var stack = e.Annotation>(); return stack == null || !stack.Any() ? (int?)null : stack.Select(st => st.Id).Min(); @@ -2962,20 +3321,24 @@ private static object ConvertContentThatCanContainFields(WordprocessingDocument { var key = g.Key; if (key == null) + { return (object)g.Select(n => ConvertToHtmlTransform(wordDoc, settings, n, false, 0m)); + } var instrText = FieldRetriever.InstrText(g.First().Ancestors().Last(), (int)key) .TrimStart('{').TrimEnd('}'); var parsed = FieldRetriever.ParseField(instrText); if (parsed.FieldType != "HYPERLINK") + { return g.Select(n => ConvertToHtmlTransform(wordDoc, settings, n, false, 0m)); + } var content = g.DescendantsAndSelf(W.r).Select(run => ConvertRun(wordDoc, settings, run)); var a = parsed.Arguments.Length > 0 ? new XElement(Xhtml.a, new XAttribute("href", parsed.Arguments[0]), content) : new XElement(Xhtml.a, content); - var a2 = a as XElement; + var a2 = a; if (!a2.Nodes().Any()) { a2.Add(new XText("")); @@ -2988,19 +3351,14 @@ private static object ConvertContentThatCanContainFields(WordprocessingDocument return txformed; } - #region Image Processing - - // Don't process wmf files (with contentType == "image/x-wmf") because GDI consumes huge amounts - // of memory when dealing with wmf perhaps because it loads a DLL to do the rendering? + // Don't process wmf files (with contentType == "image/x-wmf") because GDI consumes huge amounts of memory when dealing with wmf perhaps because it loads a DLL to do the rendering? // It actually works, but is not recommended. private static readonly List ImageContentTypes = new List { "image/png", "image/gif", "image/tiff", "image/jpeg" }; - - public static XElement ProcessImage(WordprocessingDocument wordDoc, - XElement element, Func imageHandler) + internal static XElement? ProcessImage(WordprocessingDocument wordDoc, XElement element, IImageHandler imageHandler) { if (imageHandler == null) { @@ -3017,14 +3375,16 @@ public static XElement ProcessImage(WordprocessingDocument wordDoc, return null; } - private static XElement ProcessDrawing(WordprocessingDocument wordDoc, - XElement element, Func imageHandler) + private static XElement? ProcessDrawing(WordprocessingDocument wordDoc, XElement element, IImageHandler imageHandler) { var containerElement = element.Elements() .FirstOrDefault(e => e.Name == WP.inline || e.Name == WP.anchor); - if (containerElement == null) return null; + if (containerElement == null) + { + return null; + } - string hyperlinkUri = null; + string? hyperlinkUri = null; var hyperlinkElement = element .Elements(WP.inline) .Elements(WP.docPr) @@ -3035,7 +3395,7 @@ private static XElement ProcessDrawing(WordprocessingDocument wordDoc, var rId = (string)hyperlinkElement.Attribute(R.id); if (rId != null) { - var hyperlinkRel = wordDoc.MainDocumentPart.HyperlinkRelationships.FirstOrDefault(hlr => hlr.Id == rId); + var hyperlinkRel = wordDoc.MainDocumentPart?.HyperlinkRelationships.FirstOrDefault(hlr => hlr.Id == rId); if (hyperlinkRel != null) { hyperlinkUri = hyperlinkRel.Uri.ToString(); @@ -3043,141 +3403,172 @@ private static XElement ProcessDrawing(WordprocessingDocument wordDoc, } } - var extentCx = (int?)containerElement.Elements(WP.extent) - .Attributes(NoNamespace.cx).FirstOrDefault(); - var extentCy = (int?)containerElement.Elements(WP.extent) - .Attributes(NoNamespace.cy).FirstOrDefault(); - var altText = (string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.descr).FirstOrDefault() ?? - ((string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.name).FirstOrDefault() ?? ""); + var extentCx = (int?)containerElement.Elements(WP.extent).Attributes(NoNamespace.cx).FirstOrDefault(); + var extentCy = (int?)containerElement.Elements(WP.extent).Attributes(NoNamespace.cy).FirstOrDefault(); + var altText = (string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.descr).FirstOrDefault() ?? (string)containerElement.Elements(WP.docPr).Attributes(NoNamespace.name).FirstOrDefault() ?? ""; + var blipFill = containerElement.Elements(A.graphic).Elements(A.graphicData).Elements(Pic._pic).Elements(Pic.blipFill).FirstOrDefault(); - var blipFill = containerElement.Elements(A.graphic) - .Elements(A.graphicData) - .Elements(Pic._pic).Elements(Pic.blipFill).FirstOrDefault(); - if (blipFill == null) return null; + if (blipFill == null) + { + return null; + } var imageRid = (string)blipFill.Elements(A.blip).Attributes(R.embed).FirstOrDefault(); - if (imageRid == null) return null; - var pp3 = wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp => pp.RelationshipId == imageRid); - if (pp3 == null) return null; + if (imageRid == null) + { + return null; + } - var imagePart = (ImagePart)pp3.OpenXmlPart; - if (imagePart == null) return null; + var pp3 = wordDoc.MainDocumentPart?.Parts.FirstOrDefault(pp => pp.RelationshipId == imageRid); + + if (pp3 == default) + { + return null; + } + + var imagePart = pp3?.OpenXmlPart as ImagePart; + + if (imagePart == null) + { + return null; + } // If the image markup points to a NULL image, then following will throw an ArgumentOutOfRangeException try { - imagePart = (ImagePart)wordDoc.MainDocumentPart.GetPartById(imageRid); + var mainDocumentPart = wordDoc.MainDocumentPart; + imagePart = mainDocumentPart?.GetPartById(imageRid) as ImagePart; } catch (ArgumentOutOfRangeException) { return null; } - var contentType = imagePart.ContentType; - if (!ImageContentTypes.Contains(contentType)) + var contentType = imagePart?.ContentType; + if (contentType == null || !ImageContentTypes.Contains(contentType)) + { return null; + } - using (var partStream = imagePart.GetStream()) - using (var bitmap = new Bitmap(partStream)) + using var partStream = imagePart?.GetStream(); + if (extentCx != null && extentCy != null) { - if (extentCx != null && extentCy != null) - { - var imageInfo = new ImageInfo() - { - Bitmap = bitmap, - ImgStyleAttribute = new XAttribute("style", - string.Format(NumberFormatInfo.InvariantInfo, - "width: {0}in; height: {1}in", - (float)extentCx / (float)ImageInfo.EmusPerInch, - (float)extentCy / (float)ImageInfo.EmusPerInch)), - ContentType = contentType, - DrawingElement = element, - AltText = altText, - }; - var imgElement2 = imageHandler(imageInfo); - if (hyperlinkUri != null) - { - return new XElement(XhtmlNoNamespace.a, - new XAttribute(XhtmlNoNamespace.href, hyperlinkUri), - imgElement2); - } - return imgElement2; - } - - var imageInfo2 = new ImageInfo() + var imageInfo = new ImageInfo { - Bitmap = bitmap, + Image = partStream, + ImgStyleAttribute = new XAttribute("style", + string.Format(NumberFormatInfo.InvariantInfo, + "width: {0}in; height: {1}in", + (float)extentCx / ImageInfo.EmusPerInch, + (float)extentCy / ImageInfo.EmusPerInch)), ContentType = contentType, DrawingElement = element, AltText = altText, }; - var imgElement = imageHandler(imageInfo2); + var imgElement2 = imageHandler.TransformImage(imageInfo); if (hyperlinkUri != null) { return new XElement(XhtmlNoNamespace.a, new XAttribute(XhtmlNoNamespace.href, hyperlinkUri), - imgElement); + imgElement2); } - return imgElement; + return imgElement2; } + + var imageInfo2 = new ImageInfo + { + Image = partStream, + ContentType = contentType, + DrawingElement = element, + AltText = altText, + }; + var imgElement = imageHandler.TransformImage(imageInfo2); + + if (hyperlinkUri != null) + { + return new XElement(XhtmlNoNamespace.a, new XAttribute(XhtmlNoNamespace.href, hyperlinkUri), imgElement); + } + return imgElement; } - private static XElement ProcessPictureOrObject(WordprocessingDocument wordDoc, - XElement element, Func imageHandler) + private static XElement? ProcessPictureOrObject(WordprocessingDocument wordDoc, XElement element, IImageHandler imageHandler) { - var imageRid = (string)element.Elements(VML.shape).Elements(VML.imagedata).Attributes(R.id).FirstOrDefault(); - if (imageRid == null) return null; + var imageRid = (string?)element.Elements(VML.shape).Elements(VML.imagedata).Attributes(R.id).FirstOrDefault(); + if (imageRid == null) + { + // Check if this is horizontal line + var rects = element.Elements(VML.rect).Take(2).ToList(); + if (rects.Count == 1) + { + var rect = rects[0]; + var horizontal = (string?)rect.Attribute(O.hr); + var horizontalStandard = (string?)rect.Attribute(O.hrstd); + if (ConvertTrueFalseValueToBool(horizontal) + && ConvertTrueFalseValueToBool(horizontalStandard)) + { + return new XElement(Xhtml.hr); + } + } + + return null; + } try { - var pp = wordDoc.MainDocumentPart.Parts.FirstOrDefault(pp2 => pp2.RelationshipId == imageRid); - if (pp == null) return null; + var pp = wordDoc.MainDocumentPart?.Parts.FirstOrDefault(pp2 => pp2.RelationshipId == imageRid); + if (!pp.HasValue) + { + return null; + } - var imagePart = (ImagePart)pp.OpenXmlPart; - if (imagePart == null) return null; + var imagePart = (ImagePart)pp.Value.OpenXmlPart; + if (imagePart == null) + { + return null; + } var contentType = imagePart.ContentType; if (!ImageContentTypes.Contains(contentType)) + { return null; + } - using (var partStream = imagePart.GetStream()) + using var partStream = imagePart.GetStream(); + try { - try + var imageInfo = new ImageInfo() { - using (var bitmap = new Bitmap(partStream)) - { - var imageInfo = new ImageInfo() - { - Bitmap = bitmap, - ContentType = contentType, - DrawingElement = element - }; - - var style = (string)element.Elements(VML.shape).Attributes("style").FirstOrDefault(); - if (style == null) return imageHandler(imageInfo); - - var tokens = style.Split(';'); - var widthInPoints = WidthInPoints(tokens); - var heightInPoints = HeightInPoints(tokens); - if (widthInPoints != null && heightInPoints != null) - { - imageInfo.ImgStyleAttribute = new XAttribute("style", - string.Format(NumberFormatInfo.InvariantInfo, - "width: {0}pt; height: {1}pt", widthInPoints, heightInPoints)); - } - return imageHandler(imageInfo); - } - } - catch (OutOfMemoryException) + Image = partStream, + ContentType = contentType, + DrawingElement = element + }; + + var style = (string)element.Elements(VML.shape).Attributes("style").FirstOrDefault(); + if (style == null) { - // the Bitmap class can throw OutOfMemoryException, which means the bitmap is messed up, so punt. - return null; + return imageHandler.TransformImage(imageInfo); } - catch (ArgumentException) + + var tokens = style.Split(';'); + var widthInPoints = WidthInPoints(tokens); + var heightInPoints = HeightInPoints(tokens); + if (widthInPoints != null && heightInPoints != null) { - return null; + imageInfo.ImgStyleAttribute = new XAttribute("style", + string.Format(NumberFormatInfo.InvariantInfo, + "width: {0}pt; height: {1}pt", widthInPoints, heightInPoints)); } + return imageHandler.TransformImage(imageInfo); + } + catch (OutOfMemoryException) + { + // the Bitmap class can throw OutOfMemoryException, which means the bitmap is messed up, so punt. + return null; + } + catch (ArgumentException) + { + return null; } } catch (ArgumentOutOfRangeException) @@ -3208,27 +3599,23 @@ private static XElement ProcessPictureOrObject(WordprocessingDocument wordDoc, .Select(p => p.Value) .FirstOrDefault(); - if (sizeString != null && - sizeString.Length > 2 && - sizeString.Substring(sizeString.Length - 2) == "pt") + if (sizeString != null && sizeString.Length > 2 && sizeString.Substring(sizeString.Length - 2) == "pt" && float.TryParse(sizeString.Substring(0, sizeString.Length - 2), out var size)) { - float size; - if (float.TryParse(sizeString.Substring(0, sizeString.Length - 2), out size)) - return size; + return size; } return null; } - #endregion - } - - public static class HtmlConverterExtensions - { - public static void AddIfMissing(this Dictionary style, string propName, string value) + private static bool ConvertTrueFalseValueToBool(string? trueFalseValue) { - if (style.ContainsKey(propName)) - return; - style.Add(propName, value); + var returnValue = false; + if (trueFalseValue != null + && (trueFalseValue.Equals("t", StringComparison.OrdinalIgnoreCase) || trueFalseValue.Equals("true", StringComparison.OrdinalIgnoreCase))) + { + returnValue = true; + } + + return returnValue; } } } diff --git a/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterSettings.cs b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterSettings.cs new file mode 100644 index 00000000..b91c3648 --- /dev/null +++ b/OpenXmlPowerTools/OpenXMLWordprocessingMLToHtmlConverter/WmlToHtmlConverterSettings.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter +{ + /// + /// WmlToHtmlConverterSettings + /// + public class WmlToHtmlConverterSettings + { + private const string defaultgeneralCss = "span { white-space: pre-wrap; }"; + private const string defaultAdditionalCss = "body { margin: 1cm auto; max-width: 20cm; padding: 0; }"; + private const string defaultCssClassPrefix = "pt-"; + + /// + /// Page title of HTML + /// + public string PageTitle { get; } + + /// + /// CSS class name prefix + /// + public string CssClassPrefix { get; } + + /// + /// If FabricateCssClasses is true, CSS Classes will be generated instead of using inline styles + /// + public bool FabricateCssClasses { get; } + + public string GeneralCss { get; } + public string AdditionalCss { get; } + public bool RestrictToSupportedLanguages { get; } + public bool RestrictToSupportedNumberingFormats { get; } + + public Dictionary> ListItemImplementations { get; set; } = ListItemRetrieverSettings.DefaultListItemTextImplementations; + + /// + /// Image handler + /// + public IImageHandler ImageHandler { get; } + + /// + /// Break handler + /// + public IBreakHandler BreakHandler { get; } + + /// + /// Handler that get applied to w:t + /// + public ITextHandler TextHandler { get; } + + /// + /// Symbol handler + /// + public ISymbolHandler SymbolHandler { get; } + + /// + /// Font handler + /// + public IFontHandler FontHandler { get; } + + /// + /// Default ctor WmlToHtmlConverterSettings + /// + /// Page title + public WmlToHtmlConverterSettings(string pageTitle) + { + AdditionalCss = defaultAdditionalCss; + GeneralCss = defaultgeneralCss; + PageTitle = pageTitle ?? string.Empty; + FabricateCssClasses = true; + CssClassPrefix = defaultCssClassPrefix; + ImageHandler = new ImageHandler(); + TextHandler = new TextDummyHandler(); + SymbolHandler = new SymbolHandler(); + BreakHandler = new BreakHandler(); + FontHandler = new FontHandler(); + } + + /// + /// Ctor WmlToHtmlConverterSettings + /// + /// Page title + /// Handler used to convert open XML images to HTML images + /// Handler used to convert open XML text to HTML compatible text + /// Handler used to convert open XML symbols to HTML compatible text + /// Handler used to convert open XML breaks to HTML equivalent + /// Handler used translate open XML fonts to HTML equivalent + /// Set to true, if CSS style should be stored in classes instead of an inline attribute on each node + /// Optional CSS, default is "span { white-space: pre-wrap; }" + /// Optional CSS, default is "body { margin: 1cm auto; max-width: 20cm; padding: 0; }" + /// Optional CSS class name prefix, default is "pt-" + public WmlToHtmlConverterSettings(string pageTitle, IImageHandler customImageHandler, ITextHandler textHandler, ISymbolHandler symbolHandler, IBreakHandler breakHandler, IFontHandler fontHandler, bool fabricateCssClasses, string generalCss = defaultgeneralCss, string additionalCss = defaultAdditionalCss, string cssClassPrefix = defaultCssClassPrefix) + { + AdditionalCss = additionalCss; + GeneralCss = generalCss; + PageTitle = pageTitle; + FabricateCssClasses = fabricateCssClasses; + CssClassPrefix = cssClassPrefix; + ImageHandler = customImageHandler; + TextHandler = textHandler; + SymbolHandler = symbolHandler; + BreakHandler = breakHandler; + FontHandler = fontHandler; + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXmlMemoryStreamDocument.cs b/OpenXmlPowerTools/OpenXmlMemoryStreamDocument.cs new file mode 100644 index 00000000..bd3302ea --- /dev/null +++ b/OpenXmlPowerTools/OpenXmlMemoryStreamDocument.cs @@ -0,0 +1,264 @@ +using Codeuctivity.OpenXmlPowerTools.Exceptions; +using DocumentFormat.OpenXml.Packaging; +using System; +using System.IO; +using System.IO.Packaging; +using System.Linq; +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools +{ + public class OpenXmlMemoryStreamDocument : IDisposable + { + private readonly OpenXmlPowerToolsDocument Document; + private MemoryStream DocMemoryStream; + private Package? DocPackage; + + public OpenXmlMemoryStreamDocument(OpenXmlPowerToolsDocument doc) + { + Document = doc; + DocMemoryStream = new MemoryStream(); + DocMemoryStream.Write(doc.DocumentByteArray, 0, doc.DocumentByteArray.Length); + try + { + DocPackage = Package.Open(DocMemoryStream, FileMode.Open); + } + catch (Exception e) + { + throw new PowerToolsDocumentException(e.Message); + } + } + + internal OpenXmlMemoryStreamDocument(MemoryStream stream) + { + DocMemoryStream = stream; + try + { + DocPackage = Package.Open(DocMemoryStream, FileMode.Open); + } + catch (Exception e) + { + throw new PowerToolsDocumentException(e.Message); + } + } + + public static OpenXmlMemoryStreamDocument CreateWordprocessingDocument() + { + var stream = new MemoryStream(); + using var doc = WordprocessingDocument.Create(stream, DocumentFormat.OpenXml.WordprocessingDocumentType.Document); + doc.AddMainDocumentPart(); + doc.MainDocumentPart.PutXDocument(new XDocument( + new XElement(W.document, + new XAttribute(XNamespace.Xmlns + "w", W.w), + new XAttribute(XNamespace.Xmlns + "r", R.r), + new XElement(W.body)))); + doc.Dispose(); + return new OpenXmlMemoryStreamDocument(stream); + } + + public static OpenXmlMemoryStreamDocument CreateSpreadsheetDocument() + { + var stream = new MemoryStream(); + using var doc = SpreadsheetDocument.Create(stream, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook); + doc.AddWorkbookPart(); + XNamespace ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; + XNamespace relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; + doc.WorkbookPart.PutXDocument(new XDocument( + new XElement(ns + "workbook", + new XAttribute("xmlns", ns), + new XAttribute(XNamespace.Xmlns + "r", relationshipsns), + new XElement(ns + "sheets")))); + doc.Dispose(); + return new OpenXmlMemoryStreamDocument(stream); + } + + public static OpenXmlMemoryStreamDocument CreatePresentationDocument() + { + var stream = new MemoryStream(); + using var doc = PresentationDocument.Create(stream, DocumentFormat.OpenXml.PresentationDocumentType.Presentation); + doc.AddPresentationPart(); + XNamespace ns = "http://schemas.openxmlformats.org/presentationml/2006/main"; + XNamespace relationshipsns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; + XNamespace drawingns = "http://schemas.openxmlformats.org/drawingml/2006/main"; + doc.PresentationPart.PutXDocument(new XDocument( + new XElement(ns + "presentation", + new XAttribute(XNamespace.Xmlns + "a", drawingns), + new XAttribute(XNamespace.Xmlns + "r", relationshipsns), + new XAttribute(XNamespace.Xmlns + "p", ns), + new XElement(ns + "sldMasterIdLst"), + new XElement(ns + "sldIdLst"), + new XElement(ns + "notesSz", new XAttribute("cx", "6858000"), new XAttribute("cy", "9144000"))))); + doc.Dispose(); + return new OpenXmlMemoryStreamDocument(stream); + } + + public static OpenXmlMemoryStreamDocument CreatePackage() + { + var stream = new MemoryStream(); + using var package = Package.Open(stream, FileMode.Create); + package.Close(); + return new OpenXmlMemoryStreamDocument(stream); + } + + public Package GetPackage() + { + return DocPackage; + } + + public WordprocessingDocument GetWordprocessingDocument() + { + try + { + if (GetDocumentType() != typeof(WordprocessingDocument)) + { + throw new PowerToolsDocumentException("Not a Wordprocessing document."); + } + + return WordprocessingDocument.Open(DocPackage); + } + catch (Exception e) + { + throw new PowerToolsDocumentException(e.Message); + } + } + + public SpreadsheetDocument GetSpreadsheetDocument() + { + try + { + if (GetDocumentType() != typeof(SpreadsheetDocument)) + { + throw new PowerToolsDocumentException("Not a Spreadsheet document."); + } + + return SpreadsheetDocument.Open(DocPackage); + } + catch (Exception e) + { + throw new PowerToolsDocumentException(e.Message); + } + } + + public PresentationDocument GetPresentationDocument() + { + try + { + if (GetDocumentType() != typeof(PresentationDocument)) + { + throw new PowerToolsDocumentException("Not a Presentation document."); + } + + return PresentationDocument.Open(DocPackage); + } + catch (Exception e) + { + throw new PowerToolsDocumentException(e.Message); + } + } + + public Type? GetDocumentType() + { + var relationship = DocPackage?.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument").FirstOrDefault(); + if (relationship == null) + { + relationship = DocPackage?.GetRelationshipsByType("http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument").FirstOrDefault(); + } + + if (relationship == null) + { + throw new PowerToolsDocumentException("Not an Open XML Document."); + } + + var part = DocPackage.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri)); + switch (part.ContentType) + { + case "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": + case "application/vnd.ms-word.document.macroEnabled.main+xml": + case "application/vnd.ms-word.template.macroEnabledTemplate.main+xml": + case "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": + return typeof(WordprocessingDocument); + + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": + case "application/vnd.ms-excel.sheet.macroEnabled.main+xml": + case "application/vnd.ms-excel.template.macroEnabled.main+xml": + case "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": + return typeof(SpreadsheetDocument); + + case "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": + case "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": + case "application/vnd.ms-powerpoint.template.macroEnabled.main+xml": + case "application/vnd.ms-powerpoint.addin.macroEnabled.main+xml": + case "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": + case "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml": + return typeof(PresentationDocument); + } + return null; + } + + public OpenXmlPowerToolsDocument GetModifiedDocument() + { + DocPackage?.Close(); + DocPackage = null; + return new OpenXmlPowerToolsDocument(Document?.FileName, DocMemoryStream); + } + + public WmlDocument GetModifiedWmlDocument() + { + DocPackage?.Close(); + DocPackage = null; + return new WmlDocument(Document?.FileName, DocMemoryStream); + } + + public SmlDocument GetModifiedSmlDocument() + { + DocPackage?.Close(); + DocPackage = null; + return new SmlDocument(Document?.FileName, DocMemoryStream); + } + + public PmlDocument GetModifiedPmlDocument() + { + DocPackage?.Close(); + DocPackage = null; + return new PmlDocument(Document?.FileName, DocMemoryStream); + } + + public void Close() + { + Dispose(true); + } + + public void Dispose() + { + Dispose(true); + } + + ~OpenXmlMemoryStreamDocument() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + if (DocPackage != null) + { + DocPackage.Close(); + } + if (DocMemoryStream != null) + { + DocMemoryStream.Dispose(); + } + } + if (DocPackage == null && DocMemoryStream == null) + { + return; + } + + DocPackage = null; + DocMemoryStream = null; + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXmlPowerTools.csproj b/OpenXmlPowerTools/OpenXmlPowerTools.csproj index c7220506..208cf72a 100644 --- a/OpenXmlPowerTools/OpenXmlPowerTools.csproj +++ b/OpenXmlPowerTools/OpenXmlPowerTools.csproj @@ -1,24 +1,61 @@  - - net45;net46;netstandard2.0 - - - - - - - - - - - - - - - - - - - - + + net8.0 + true + true + https://github.com/Codeuctivity/OpenXmlPowerTools + OpenXML DOCX Word XLSX Excel PPTX Powerpoint + Stefan Seeland + Codeuctivity + $(CURRENT_VERSION) + 0.0.1 + $(Version) + $(Version) + $(Version) + $(LAST_COMMIT_MESSAGE) + NugetIcon.png + https://github.com/Codeuctivity/OpenXmlPowerTools + The Open XML SDK provides tools for working with Office Word, Excel, and PowerPoint documents. This fork supports current .net versions. + MIT + OpenXmlPowerTools.snk + true + true + snupkg + true + true + enable + 8.0 + Codeuctivity.OpenXmlPowerTools + en + true + Codeuctivity.OpenXmlPowerTools + Codeuctivity.OpenXmlPowerTools + nugetReadme.md + true + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXmlPowerTools.csproj.DotSettings b/OpenXmlPowerTools/OpenXmlPowerTools.csproj.DotSettings deleted file mode 100644 index bf4e5992..00000000 --- a/OpenXmlPowerTools/OpenXmlPowerTools.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXmlPowerTools.snk b/OpenXmlPowerTools/OpenXmlPowerTools.snk new file mode 100644 index 00000000..c02435c6 Binary files /dev/null and b/OpenXmlPowerTools/OpenXmlPowerTools.snk differ diff --git a/OpenXmlPowerTools/OpenXmlPowerToolsDocument.cs b/OpenXmlPowerTools/OpenXmlPowerToolsDocument.cs new file mode 100644 index 00000000..8066219b --- /dev/null +++ b/OpenXmlPowerTools/OpenXmlPowerToolsDocument.cs @@ -0,0 +1,326 @@ +using Codeuctivity.OpenXmlPowerTools.Exceptions; +using DocumentFormat.OpenXml.Packaging; +using System; +using System.IO; +using System.IO.Packaging; +using System.Linq; + +namespace Codeuctivity.OpenXmlPowerTools +{ + public class OpenXmlPowerToolsDocument + { + public string FileName { get; set; } + public byte[] DocumentByteArray { get; set; } + + public static OpenXmlPowerToolsDocument FromFileName(string fileName) + { + var bytes = File.ReadAllBytes(fileName); + Type? type; + try + { + type = GetDocumentType(bytes); + } + catch (FileFormatException) + { + throw new PowerToolsDocumentException("Not an Open XML document."); + } + if (type == typeof(WordprocessingDocument)) + { + return new WmlDocument(fileName, bytes); + } + + if (type == typeof(SpreadsheetDocument)) + { + return new SmlDocument(fileName, bytes); + } + + if (type == typeof(PresentationDocument)) + { + return new PmlDocument(fileName, bytes); + } + + if (type == typeof(Package)) + { + var pkg = new OpenXmlPowerToolsDocument(bytes) + { + FileName = fileName + }; + return pkg; + } + throw new PowerToolsDocumentException("Not an Open XML document."); + } + + public static OpenXmlPowerToolsDocument FromDocument(OpenXmlPowerToolsDocument doc) + { + if (doc == null) + { + throw new ArgumentNullException(nameof(doc)); + } + + var type = doc.GetDocumentType(); + if (type == typeof(WordprocessingDocument)) + { + return new WmlDocument(doc); + } + + if (type == typeof(SpreadsheetDocument)) + { + return new SmlDocument(doc); + } + + if (type == typeof(PresentationDocument)) + { + return new PmlDocument(doc); + } + + throw new PowerToolsDocumentException("Invalid OpenXmlPowerToolsDocument object"); + } + + public OpenXmlPowerToolsDocument(OpenXmlPowerToolsDocument original) + { + if (original == null) + { + throw new ArgumentNullException(nameof(original)); + } + + DocumentByteArray = new byte[original.DocumentByteArray.Length]; + Array.Copy(original.DocumentByteArray, DocumentByteArray, original.DocumentByteArray.Length); + FileName = original.FileName; + } + + public OpenXmlPowerToolsDocument(OpenXmlPowerToolsDocument original, bool convertToTransitional) + { + if (original == null) + { + throw new ArgumentNullException(nameof(original)); + } + + if (convertToTransitional) + { + ConvertToTransitional(original.FileName, original.DocumentByteArray); + } + else + { + DocumentByteArray = new byte[original.DocumentByteArray.Length]; + Array.Copy(original.DocumentByteArray, DocumentByteArray, original.DocumentByteArray.Length); + FileName = original.FileName; + } + } + + public OpenXmlPowerToolsDocument(string fileName) + { + FileName = fileName; + DocumentByteArray = File.ReadAllBytes(fileName); + } + + public OpenXmlPowerToolsDocument(string fileName, bool convertToTransitional) + { + FileName = fileName; + + if (convertToTransitional) + { + var tempByteArray = File.ReadAllBytes(fileName); + ConvertToTransitional(fileName, tempByteArray); + } + else + { + FileName = fileName; + DocumentByteArray = File.ReadAllBytes(fileName); + } + } + + private void ConvertToTransitional(string fileName, byte[] tempByteArray) + { + Type? type; + try + { + type = GetDocumentType(tempByteArray); + } + catch (FileFormatException) + { + throw new PowerToolsDocumentException("Not an Open XML document."); + } + + using var ms = new MemoryStream(); + ms.Write(tempByteArray, 0, tempByteArray.Length); + if (type == typeof(WordprocessingDocument)) + { + using var sDoc = WordprocessingDocument.Open(ms, true); + // following code forces the SDK to serialize + foreach (var part in sDoc.Parts) + { + try + { + var z = part.OpenXmlPart.RootElement; + } + catch (Exception) + { + continue; + } + } + } + else if (type == typeof(SpreadsheetDocument)) + { + using var sDoc = SpreadsheetDocument.Open(ms, true); + // following code forces the SDK to serialize + foreach (var part in sDoc.Parts) + { + try + { + var z = part.OpenXmlPart.RootElement; + } + catch (Exception) + { + continue; + } + } + } + else if (type == typeof(PresentationDocument)) + { + using var sDoc = PresentationDocument.Open(ms, true); + // following code forces the SDK to serialize + foreach (var part in sDoc.Parts) + { + try + { + var z = part.OpenXmlPart.RootElement; + } + catch (Exception) + { + continue; + } + } + } + FileName = fileName; + DocumentByteArray = ms.ToArray(); + } + + public OpenXmlPowerToolsDocument(byte[] byteArray) + { + DocumentByteArray = new byte[byteArray.Length]; + Array.Copy(byteArray, DocumentByteArray, byteArray.Length); + FileName = null; + } + + public OpenXmlPowerToolsDocument(byte[] byteArray, bool convertToTransitional) + { + if (convertToTransitional) + { + ConvertToTransitional(null, byteArray); + } + else + { + DocumentByteArray = new byte[byteArray.Length]; + Array.Copy(byteArray, DocumentByteArray, byteArray.Length); + FileName = null; + } + } + + public OpenXmlPowerToolsDocument(string fileName, MemoryStream memStream) + { + FileName = fileName; + DocumentByteArray = new byte[memStream.Length]; + Array.Copy(memStream.GetBuffer(), DocumentByteArray, memStream.Length); + } + + public OpenXmlPowerToolsDocument(string fileName, MemoryStream memStream, bool convertToTransitional) + { + if (convertToTransitional) + { + ConvertToTransitional(fileName, memStream.ToArray()); + } + else + { + FileName = fileName; + DocumentByteArray = new byte[memStream.Length]; + Array.Copy(memStream.GetBuffer(), DocumentByteArray, memStream.Length); + } + } + + public string GetName() + { + if (FileName == null) + { + return "Unnamed Document"; + } + + var file = new FileInfo(FileName); + return file.Name; + } + + public void SaveAs(string fileName) + { + File.WriteAllBytes(fileName, DocumentByteArray); + } + + public void Save() + { + if (FileName == null) + { + throw new InvalidOperationException("Attempting to Save a document that has no file name. Use SaveAs instead."); + } + + File.WriteAllBytes(FileName, DocumentByteArray); + } + + public void WriteByteArray(Stream stream) + { + stream.Write(DocumentByteArray, 0, DocumentByteArray.Length); + } + + public Type? GetDocumentType() + { + return GetDocumentType(DocumentByteArray); + } + + private static Type? GetDocumentType(byte[] bytes) + { + using var stream = new MemoryStream(); + stream.Write(bytes, 0, bytes.Length); + using var package = Package.Open(stream, FileMode.Open); + var relationship = package.GetRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument").FirstOrDefault(); + if (relationship == null) + { + relationship = package.GetRelationshipsByType("http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument").FirstOrDefault(); + } + + if (relationship != null) + { + var part = package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri)); + switch (part.ContentType) + { + case "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml": + case "application/vnd.ms-word.document.macroEnabled.main+xml": + case "application/vnd.ms-word.template.macroEnabledTemplate.main+xml": + case "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml": + return typeof(WordprocessingDocument); + + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": + case "application/vnd.ms-excel.sheet.macroEnabled.main+xml": + case "application/vnd.ms-excel.template.macroEnabled.main+xml": + case "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml": + return typeof(SpreadsheetDocument); + + case "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml": + case "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml": + case "application/vnd.ms-powerpoint.template.macroEnabled.main+xml": + case "application/vnd.ms-powerpoint.addin.macroEnabled.main+xml": + case "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml": + case "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml": + return typeof(PresentationDocument); + } + return typeof(Package); + } + return null; + } + + public static void SavePartAs(OpenXmlPart part, string filePath) + { + var partStream = part.GetStream(FileMode.Open, FileAccess.Read); + var partContent = new byte[partStream.Length]; + partStream.Read(partContent, 0, (int)partStream.Length); + + File.WriteAllBytes(filePath, partContent); + } + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXmlRegex.cs b/OpenXmlPowerTools/OpenXmlRegex.cs index 09b77f0d..6e930992 100644 --- a/OpenXmlPowerTools/OpenXmlRegex.cs +++ b/OpenXmlPowerTools/OpenXmlRegex.cs @@ -1,13 +1,10 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Xml.Linq; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public class OpenXmlRegex { @@ -57,21 +54,25 @@ public static int Match(IEnumerable content, Regex regex, Action { - if (found != null) found.Invoke(x, m); + if (found != null) + { + found.Invoke(x, m); + } + return true; }, false, null, true); } /// - /// If replacement == "new content" && callback == null + /// If replacement == "new content" and callback == null /// Then replaces all matches - /// If replacement == "" && callback == null) + /// If replacement == "" and callback == null) /// Then deletes all matches - /// If replacement == "new content" && callback != null) + /// If replacement == "new content" and callback != null) /// Then the callback can return true / false to indicate whether to replace or not /// If the callback returns true once, and false on all subsequent calls, then this method replaces only the first found. - /// If replacement == "" && callback != null) + /// If replacement == "" and callback != null) /// Then the callback can return true / false to indicate whether to delete or not /// public static int Replace(IEnumerable content, Regex regex, string replacement, @@ -90,14 +91,14 @@ public static int Replace(IEnumerable content, Regex regex, string rep } /// - /// If replacement == "new content" && callback == null + /// If replacement == "new content" and callback == null /// Then replaces all matches - /// If replacement == "" && callback == null) + /// If replacement == "" and callback == null) /// Then deletes all matches - /// If replacement == "new content" && callback != null) + /// If replacement == "new content" and callback != null) /// Then the callback can return true / false to indicate whether to replace or not /// If the callback returns true once, and false on all subsequent calls, then this method replaces only the first found. - /// If replacement == "" && callback != null) + /// If replacement == "" and callback != null) /// Then the callback can return true / false to indicate whether to delete or not /// If trackRevisions == true /// Then replacement is done using revision tracking markup, with author as the revision tracking author @@ -114,68 +115,89 @@ private static int ReplaceInternal(IEnumerable content, Regex regex, s Func callback, bool trackRevisions, string revisionTrackingAuthor, bool coalesceContent) { - if (content == null) throw new ArgumentNullException("content"); - if (regex == null) throw new ArgumentNullException("regex"); + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (regex == null) + { + throw new ArgumentNullException(nameof(regex)); + } IEnumerable contentList = content as IList ?? content.ToList(); - XElement first = contentList.FirstOrDefault(); + var first = contentList.FirstOrDefault(); if (first == null) + { return 0; + } if (first.Name.Namespace == W.w) { if (!contentList.Any()) + { return 0; + } var replInfo = new ReplaceInternalInfo { Count = 0 }; - foreach (XElement c in contentList) + foreach (var c in contentList) { - var newC = (XElement) WmlSearchAndReplaceTransform(c, regex, replacement, callback, trackRevisions, + var newC = (XElement)WmlSearchAndReplaceTransform(c, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent); c.ReplaceNodes(newC.Nodes()); } - XElement root = contentList.First().AncestorsAndSelf().Last(); - int nextId = new[] { 0 } + var root = contentList.First().AncestorsAndSelf().Last(); + var nextId = new[] { 0 } .Concat(root .Descendants() .Where(d => RevTrackMarkupWithId.Contains(d.Name)) .Attributes(W.id) - .Select(a => (int) a)) + .Select(a => (int)a)) .Max() + 1; - IEnumerable revTrackingWithoutId = root + var revTrackingWithoutId = root .DescendantsAndSelf() - .Where(d => RevTrackMarkupWithId.Contains(d.Name) && (d.Attribute(W.id) == null)); - foreach (XElement item in revTrackingWithoutId) + .Where(d => RevTrackMarkupWithId.Contains(d.Name) && d.Attribute(W.id) == null); + foreach (var item in revTrackingWithoutId) + { item.Add(new XAttribute(W.id, nextId++)); + } - List> revTrackingWithDuplicateIds = root + var revTrackingWithDuplicateIds = root .DescendantsAndSelf() .Where(d => RevTrackMarkupWithId.Contains(d.Name)) - .GroupBy(d => (int) d.Attribute(W.id)) + .GroupBy(d => (int)d.Attribute(W.id)) .Where(g => g.Count() > 1) .ToList(); - foreach (IGrouping group in revTrackingWithDuplicateIds) - foreach (XElement gc in group.Skip(1)) + foreach (var group in revTrackingWithDuplicateIds) + { + foreach (var gc in group.Skip(1)) { - XAttribute xAttribute = gc.Attribute(W.id); - if (xAttribute != null) xAttribute.Value = nextId.ToString(); + var xAttribute = gc.Attribute(W.id); + if (xAttribute != null) + { + xAttribute.Value = nextId.ToString(); + } + nextId++; } + } return replInfo.Count; } - if ((first.Name.Namespace == P.p) || (first.Name.Namespace == A.a)) + if (first.Name.Namespace == P.p || first.Name.Namespace == A.a) { if (trackRevisions) + { throw new OpenXmlPowerToolsException("PPTX does not support revision tracking"); + } var counter = new ReplaceInternalInfo { Count = 0 }; - foreach (XElement c in contentList) + foreach (var c in contentList) { - var newC = (XElement) PmlSearchAndReplaceTransform(c, regex, replacement, callback, counter); + var newC = (XElement)PmlSearchAndReplaceTransform(c, regex, replacement, callback, counter); c.ReplaceNodes(newC.Nodes()); } @@ -189,14 +211,16 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri Func callback, bool trackRevisions, string revisionTrackingAuthor, ReplaceInternalInfo replInfo, bool coalesceContent) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (element.Name == W.p) { - XElement paragraph = element; + var paragraph = element; - string preliminaryContent = paragraph + var preliminaryContent = paragraph .DescendantsTrimmed(W.txbxContent) .Where(d => d.Name == W.r && (d.Parent == null || d.Parent.Name != W.del)) .Select(UnicodeMapper.RunToString) @@ -208,7 +232,7 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri paragraph.Nodes().Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent))); - IEnumerable runsTrimmed = paragraphWithSplitRuns + var runsTrimmed = paragraphWithSplitRuns .DescendantsTrimmed(W.txbxContent) .Where(d => d.Name == W.r && (d.Parent == null || d.Parent.Name != W.del)); @@ -216,38 +240,50 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri .Select(r => new { Ch = UnicodeMapper.RunToString(r), r }) .ToList(); - string content = charsAndRuns.Select(t => t.Ch).StringConcatenate(); - XElement[] alignedRuns = charsAndRuns.Select(t => t.r).ToArray(); + var content = charsAndRuns.Select(t => t.Ch).StringConcatenate(); + var alignedRuns = charsAndRuns.Select(t => t.r).ToArray(); - MatchCollection matchCollection = regex.Matches(content); + var matchCollection = regex.Matches(content); replInfo.Count += matchCollection.Count; // Process Match if (replacement == null) { - if (callback == null) return paragraph; + if (callback == null) + { + return paragraph; + } - foreach (Match match in matchCollection.Cast()) + foreach (var match in matchCollection.Cast()) + { callback(paragraph, match); + } return paragraph; } // Process Replace - foreach (Match match in matchCollection.Cast()) + foreach (var match in matchCollection.Cast()) { - if (match.Length == 0) continue; - if ((callback != null) && !callback(paragraph, match)) continue; + if (match.Length == 0) + { + continue; + } - List runCollection = alignedRuns + if (callback != null && !callback(paragraph, match)) + { + continue; + } + + var runCollection = alignedRuns .Skip(match.Index) .Take(match.Length) .ToList(); // uses the Skip / Take special semantics of array to implement efficient finding of sub array - XElement firstRun = runCollection.First(); - XElement firstRunProperties = firstRun.Elements(W.rPr).FirstOrDefault(); + var firstRun = runCollection.First(); + var firstRunProperties = firstRun.Elements(W.rPr).FirstOrDefault(); // save away first run properties @@ -258,8 +294,8 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri // We coalesce runs as some methods, e.g., in DocumentAssembler, // will try to find the replacement string even though they // set coalesceContent to false. - string newTextValue = match.Result(replacement); - List newRuns = UnicodeMapper.StringToCoalescedRunList(newTextValue, + var newTextValue = match.Result(replacement); + var newRuns = UnicodeMapper.StringToCoalescedRunList(newTextValue, firstRunProperties); var newIns = new XElement(W.ins, new XAttribute(W.author, revisionTrackingAuthor), @@ -267,24 +303,28 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri newRuns); if (firstRun.Parent != null && firstRun.Parent.Name == W.ins) + { firstRun.Parent.AddBeforeSelf(newIns); + } else + { firstRun.AddBeforeSelf(newIns); + } } - foreach (XElement run in runCollection) + foreach (var run in runCollection) { - bool isInIns = run.Parent != null && run.Parent.Name == W.ins; + var isInIns = run.Parent != null && run.Parent.Name == W.ins; if (isInIns) { - XElement parentIns = run.Parent; - XElement grandParentParagraph = parentIns.Parent; + var parentIns = run.Parent; + var grandParentParagraph = parentIns.Parent; if (grandParentParagraph != null) { - if ((string) parentIns.Attributes(W.author).FirstOrDefault() == + if ((string)parentIns.Attributes(W.author).FirstOrDefault() == revisionTrackingAuthor) { - List parentInsSiblings = grandParentParagraph + var parentInsSiblings = grandParentParagraph .Elements() .Where(c => c != parentIns) .ToList(); @@ -292,7 +332,7 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri } else { - List parentInsSiblings = grandParentParagraph + var parentInsSiblings = grandParentParagraph .Elements() .Select(c => c == parentIns ? new XElement(W.ins, @@ -319,22 +359,32 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri } else // not tracked revisions { - foreach (XElement runToDelete in runCollection.Skip(1).ToList()) + foreach (var runToDelete in runCollection.Skip(1).ToList()) + { if (runToDelete.Parent != null && runToDelete.Parent.Name == W.ins) + { runToDelete.Parent.Remove(); + } else + { runToDelete.Remove(); + } + } // We coalesce runs as some methods, e.g., in DocumentAssembler, // will try to find the replacement string even though they // set coalesceContent to false. - string newTextValue = match.Result(replacement); - List newRuns = UnicodeMapper.StringToCoalescedRunList(newTextValue, + var newTextValue = match.Result(replacement); + var newRuns = UnicodeMapper.StringToCoalescedRunList(newTextValue, firstRunProperties); if (firstRun.Parent != null && firstRun.Parent.Name == W.ins) + { firstRun.Parent.ReplaceWith(newRuns); + } else + { firstRun.ReplaceWith(newRuns); + } } } @@ -347,15 +397,25 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri paragraph.Attributes(), paragraph.Nodes().Select(n => { - var e = n as XElement; - if (e == null) return n; + if (!(n is XElement e)) + { + return n; + } if (e.Name == W.pPr) + { return e; - if (((e.Name == W.r) && e.Elements(W.t).Any()) || e.Elements(W.tab).Any()) + } + + if (e.Name == W.r && e.Elements(W.t).Any() || e.Elements(W.tab).Any()) + { return e; - if ((e.Name == W.ins) && e.Elements(W.r).Elements(W.t).Any()) + } + + if (e.Name == W.ins && e.Elements(W.r).Elements(W.t).Any()) + { return e; + } return WmlSearchAndReplaceTransform(e, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent); @@ -367,16 +427,15 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri if (element.Name == W.ins && element.Elements(W.r).Any()) { - List collectionOfCollections = element + var collectionOfCollections = element .Elements() .Select(n => WmlSearchAndReplaceTransform(n, regex, replacement, callback, trackRevisions, revisionTrackingAuthor, replInfo, coalesceContent)) .ToList(); - List collectionOfIns = collectionOfCollections + var collectionOfIns = collectionOfCollections .Select(c => { - var elements = c as IEnumerable; - return elements != null + return c is IEnumerable elements ? elements.Select(ixc => new XElement(W.ins, element.Attributes(), ixc)) : c; }) @@ -389,10 +448,10 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri return element.Elements() .Where(e => e.Name != W.rPr) .Select(e => e.Name == W.t - ? ((string) e).Select(c => - new XElement(W.r, - element.Elements(W.rPr), - new XElement(W.t, XmlUtil.GetXmlSpaceAttribute(c), c))) + ? ((string)e).Select(c => + new XElement(W.r, + element.Elements(W.rPr), + new XElement(W.t, XmlUtil.GetXmlSpaceAttribute(c), c))) : new[] { new XElement(W.r, element.Elements(W.rPr), e) }) .SelectMany(t => t); } @@ -406,13 +465,17 @@ private static object WmlSearchAndReplaceTransform(XNode node, Regex regex, stri private static object TransformToDelText(XNode node) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (element.Name == W.t) + { return new XElement(W.delText, XmlUtil.GetXmlSpaceAttribute(element.Value), element.Value); + } return new XElement(element.Name, element.Attributes(), @@ -422,22 +485,26 @@ private static object TransformToDelText(XNode node) private static object PmlSearchAndReplaceTransform(XNode node, Regex regex, string replacement, Func callback, ReplaceInternalInfo counter) { - var element = node as XElement; - if (element == null) return node; + if (!(node is XElement element)) + { + return node; + } if (element.Name == A.p) { - XElement paragraph = element; - string contents = element.Descendants(A.t).Select(t => (string) t).StringConcatenate(); + var paragraph = element; + var contents = element.Descendants(A.t).Select(t => (string)t).StringConcatenate(); if (!regex.IsMatch(contents)) + { return new XElement(element.Name, element.Attributes(), element.Nodes()); + } var paragraphWithSplitRuns = new XElement(A.p, paragraph.Attributes(), paragraph.Nodes() .Select(n => PmlSearchAndReplaceTransform(n, regex, replacement, callback, counter))); - List runsTrimmed = paragraphWithSplitRuns + var runsTrimmed = paragraphWithSplitRuns .Descendants(A.r) .ToList(); @@ -448,30 +515,35 @@ private static object PmlSearchAndReplaceTransform(XNode node, Regex regex, stri : new { Ch = "\x01", r }) .ToList(); - string content = charsAndRuns.Select(t => t.Ch).StringConcatenate(); - XElement[] alignedRuns = charsAndRuns.Select(t => t.r).ToArray(); + var content = charsAndRuns.Select(t => t.Ch).StringConcatenate(); + var alignedRuns = charsAndRuns.Select(t => t.r).ToArray(); - MatchCollection matchCollection = regex.Matches(content); + var matchCollection = regex.Matches(content); counter.Count += matchCollection.Count; if (replacement == null) { - foreach (Match match in matchCollection.Cast()) + foreach (var match in matchCollection.Cast()) + { callback(paragraph, match); + } } else { - foreach (Match match in matchCollection.Cast()) + foreach (var match in matchCollection.Cast()) { - if ((callback != null) && !callback(paragraph, match)) continue; + if (callback != null && !callback(paragraph, match)) + { + continue; + } - List runCollection = alignedRuns + var runCollection = alignedRuns .Skip(match.Index) .Take(match.Length) .ToList(); // uses the Skip / Take special semantics of array to implement efficient finding of sub array - XElement firstRun = runCollection.First(); + var firstRun = runCollection.First(); // save away first run because we want the run properties @@ -495,29 +567,36 @@ private static object PmlSearchAndReplaceTransform(XNode node, Regex regex, stri // sets newFirstRun's parent to firstRuns old parent, and inserts in the list // of children at the right place. } - XElement paragraphWithReplacedRuns = paragraphWithSplitRuns; + var paragraphWithReplacedRuns = paragraphWithSplitRuns; - IEnumerable> groupedAdjacentRunsWithIdenticalFormatting = + var groupedAdjacentRunsWithIdenticalFormatting = paragraphWithReplacedRuns .Elements() .GroupAdjacent(ce => { if (ce.Name != A.r) + { return DontConsolidate; - if ((ce.Elements().Count(e => e.Name != A.rPr) != 1) || (ce.Element(A.t) == null)) + } + + if (ce.Elements().Count(e => e.Name != A.rPr) != 1 || ce.Element(A.t) == null) + { return DontConsolidate; + } - XElement rPr = ce.Element(A.rPr); + var rPr = ce.Element(A.rPr); return rPr == null ? "" : rPr.ToString(SaveOptions.None); }); var paragraphWithConsolidatedRuns = new XElement(A.p, groupedAdjacentRunsWithIdenticalFormatting.Select(g => { if (g.Key == DontConsolidate) - return (object) g; + { + return (object)g; + } - string textValue = g.Select(r => r.Element(A.t).Value).StringConcatenate(); - XAttribute xs = XmlUtil.GetXmlSpaceAttribute(textValue); + var textValue = g.Select(r => r.Element(A.t).Value).StringConcatenate(); + var xs = XmlUtil.GetXmlSpaceAttribute(textValue); return new XElement(A.r, g.First().Elements(A.rPr), new XElement(A.t, xs, textValue)); @@ -528,7 +607,7 @@ private static object PmlSearchAndReplaceTransform(XNode node, Regex regex, stri return paragraph; } - if ((element.Name == A.r) && element.Elements(A.t).Any()) + if (element.Name == A.r && element.Elements(A.t).Any()) { return element.Elements() .Where(e => e.Name != A.rPr) @@ -536,11 +615,11 @@ private static object PmlSearchAndReplaceTransform(XNode node, Regex regex, stri { if (e.Name == A.t) { - var s = (string) e; - IEnumerable collectionOfSubRuns = s.Select(c => new XElement(A.r, + var s = (string)e; + var collectionOfSubRuns = s.Select(c => new XElement(A.r, element.Elements(A.rPr), new XElement(A.t, XmlUtil.GetXmlSpaceAttribute(c), c))); - return (object) collectionOfSubRuns; + return (object)collectionOfSubRuns; } return new XElement(A.r, @@ -559,4 +638,4 @@ private class ReplaceInternalInfo public int Count; } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OpenXmlUtils/PtOpenXmlUtil.cs b/OpenXmlPowerTools/OpenXmlUtils/PtOpenXmlUtil.cs new file mode 100644 index 00000000..a7c1400b --- /dev/null +++ b/OpenXmlPowerTools/OpenXmlUtils/PtOpenXmlUtil.cs @@ -0,0 +1,13 @@ +using System.Xml.Linq; + +namespace Codeuctivity.OpenXmlPowerTools.OpenXmlUtils +{ + public static class PtOpenXmlUtil + { + public static readonly XNamespace mp = "http://schemas.microsoft.com/office/mac/powerpoint/2008/main"; + + public static readonly XName cube = mp + "cube"; + public static readonly XName flip = mp + "flip"; + public static readonly XName transition = mp + "transition"; + } +} \ No newline at end of file diff --git a/OpenXmlPowerTools/OxPtHelpers.cs b/OpenXmlPowerTools/OxPtHelpers.cs index b48bed19..2b027f55 100644 --- a/OpenXmlPowerTools/OxPtHelpers.cs +++ b/OpenXmlPowerTools/OxPtHelpers.cs @@ -1,22 +1,17 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Validation; +using DocumentFormat.OpenXml.Wordprocessing; using System; using System.Collections.Generic; -using System.Drawing; using System.IO; using System.Linq; +using System.Text; using System.Xml; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; -using System.Text; -using DocumentFormat.OpenXml; -using System.Drawing.Imaging; -namespace OpenXmlPowerTools +namespace Codeuctivity.OpenXmlPowerTools { public static class AddDocxTextHelper { @@ -30,61 +25,70 @@ public static WmlDocument AppendParagraphToDocument( string backColor, string styleName) { - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(wmlDoc)) + using var streamDoc = new OpenXmlMemoryStreamDocument(wmlDoc); + using (var wDoc = streamDoc.GetWordprocessingDocument()) { - using (WordprocessingDocument wDoc = streamDoc.GetWordprocessingDocument()) - { - StyleDefinitionsPart part = wDoc.MainDocumentPart.StyleDefinitionsPart; - - Body body = wDoc.MainDocumentPart.Document.Body; + var part = wDoc.MainDocumentPart.StyleDefinitionsPart; - SectionProperties sectionProperties = body.Elements().FirstOrDefault(); + var body = wDoc.MainDocumentPart.Document.Body; - Paragraph paragraph = new Paragraph(); - Run run = paragraph.AppendChild(new Run()); - RunProperties runProperties = new RunProperties(); + var sectionProperties = body.Elements().FirstOrDefault(); - if (isBold) - runProperties.AppendChild(new Bold()); + var paragraph = new Paragraph(); + var run = paragraph.AppendChild(new Run()); + var runProperties = new RunProperties(); - if (isItalic) - runProperties.AppendChild(new Italic()); + if (isBold) + { + runProperties.AppendChild(new Bold()); + } + if (isItalic) + { + runProperties.AppendChild(new Italic()); + } - if (!string.IsNullOrEmpty(foreColor)) + if (!string.IsNullOrEmpty(foreColor)) + { + SkiaSharp.SKColor colorValue; + if (!ColorParser.TryFromName(backColor, out colorValue)) { - int colorValue = ColorParser.FromName(foreColor).ToArgb(); - if (colorValue == 0) - throw new OpenXmlPowerToolsException(String.Format("Add-DocxText: The specified color {0} is unsupported, Please specify the valid color. Ex, Red, Green", foreColor)); - - string ColorHex = string.Format("{0:x6}", colorValue); - runProperties.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Color() { Val = ColorHex.Substring(2) }); + throw new OpenXmlPowerToolsException(string.Format("Add-DocxText: The specified color {0} is unsupported, Please specify the valid color. Ex, Red, Green", foreColor)); } - if (isUnderline) - runProperties.AppendChild(new Underline() { Val = UnderlineValues.Single }); + var colorHex = $"{colorValue.Red:X2}{colorValue.Green:X2}{colorValue.Blue:X2}"; + runProperties.AppendChild(new Color() { Val = colorHex }); + } - if (!string.IsNullOrEmpty(backColor)) - { - int colorShade = ColorParser.FromName(backColor).ToArgb(); - if (colorShade == 0) - throw new OpenXmlPowerToolsException(String.Format("Add-DocxText: The specified color {0} is unsupported, Please specify the valid color. Ex, Red, Green", foreColor)); + if (isUnderline) + { + runProperties.AppendChild(new Underline() { Val = UnderlineValues.Single }); + } - string ColorShadeHex = string.Format("{0:x6}", colorShade); - runProperties.AppendChild(new Shading() { Fill = ColorShadeHex.Substring(2), Val = ShadingPatternValues.Clear }); + if (!string.IsNullOrEmpty(backColor)) + { + SkiaSharp.SKColor colorShade; + if (!ColorParser.TryFromName(backColor, out colorShade)) + { + throw new OpenXmlPowerToolsException(string.Format("Add-DocxText: The specified color {0} is unsupported, Please specify the valid color. Ex, Red, Green", foreColor)); } - if (!string.IsNullOrEmpty(styleName)) + var colorShadeHex = $"{colorShade.Red:X2}{colorShade.Green:X2}{colorShade.Blue:X2}"; + runProperties.AppendChild(new Shading() { Fill = colorShadeHex, Val = ShadingPatternValues.Clear }); + } + + if (!string.IsNullOrEmpty(styleName)) + { + var style = part.Styles.Elements

    5 日游行程计划

    1 天

    目的地:[您要去哪儿?]

    在哪里吃饭:[早餐吃什么?]

    安排什么活动:[是否要买演出的票?]

    待在哪里:[沙滩屋还是朋友的沙发?]

    抵达方式:[飞机、火车还是 GPS 自驾?]

    图片 4

    2 天

    [要替换任何占位符文本(例如此文本),只需选中一行或一段文本并开始键入。为达到最佳效果,请勿在您选中的字符左侧或右侧包含空格。]

    图片 5

    3 天

    [要将任何占位符照片替换为您自己的照片,请删除它,然后在功能区的“插入”选项卡上单击“图片”。]

    图片 6

    4 天

    目的地:

    在哪里吃饭:

    安排什么活动:

    待在哪里

    抵达方式:

    图片 7

    5 天

    目的地:

    在哪里吃饭:

    安排什么活动:

    待在哪里

    抵达方式:

    图片 8

    \ No newline at end of file + + + + + + + + + +

    5 日游行程计划

    1 天

    目的地:[您要去哪儿?]

    在哪里吃饭:[早餐吃什么?]

    安排什么活动:[是否要买演出的票?]

    待在哪里:[沙滩屋还是朋友的沙发?]

    抵达方式:[飞机、火车还是 GPS 自驾?]

    图片 4

    2 天

    [要替换任何占位符文本(例如此文本),只需选中一行或一段文本并开始键入。为达到最佳效果,请勿在您选中的字符左侧或右侧包含空格。]

    图片 5

    3 天

    [要将任何占位符照片替换为您自己的照片,请删除它,然后在功能区的“插入”选项卡上单击“图片”。]

    图片 6

    4 天

    目的地:

    在哪里吃饭:

    安排什么活动:

    待在哪里

    抵达方式:

    图片 7

    5 天

    目的地:

    在哪里吃饭:

    安排什么活动:

    待在哪里

    抵达方式:

    图片 8

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Contract.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Contract.html index 4d6265ba..06b7e3da 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Contract.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Contract.html @@ -1,174 +1,212 @@ -C:\Users\Eric\Documents\Open-Xml-PowerTools\OpenXmlPowerToolsExamples\HtmlConverter01\Contract.docx

    1.0EINLEITUNG

    1.Der IT-DIENSTLEISTER bestätigt, dass er – falls nicht anders angegeben – eine Lösung bereitstellen wird, die alle in dieser LEISTUNGSBESCHREIBUNG (Statement of Work - SOW) und ihren Anhängen angeführten Geschäftsprozesse unterstützt und dass alle SERVICES - falls nicht ausdrücklich anderweitig festgelegt - in den BASE CHARGES enthalten sind. Der IT-DIENSTLEISTER verpflichtet sich zu dem Ansatz einer kontinuierlichen Verbesserung.

    2.Der IT-DIENSTLEISTER reagiert auf derzeitige und künftige Anforderungen von KUNDE, schätzt proaktiv den künftigen Bedarf und wird seine Dienstleistungen innerhalb der BASE CHARGES entsprechend adjustieren. Anforderungen bezüglich neuer Dienstleistungen oder gravierender Änderung der Rahmenbedingungen werden innerhalb des CHANGE CONTROL VERFAHRENS behandelt, wobei der IT-DIENSTLEISTER in Zusammenarbeit mit KUNDE die Auswirkungen einer solchen Anforderung auf die Betriebsumgebung von KUNDE beurteilt.

    3.Ab dem ANFANGSDATUM wird der IT-DIENSTLEISTER für die Ausführung der in dieser LEISTUNGSBESCHREIBUNG festgelegten Application Development und Maintenance Service (ADM SERVICES) verantwortlich sein. Der IT-DIENSTLEISTER wird den in Anhang X festgelegten Umfang der ADM Services für die ANWENDUNGSSOFTWARE erbringen.

    2.0Beispiel-Überschrift

    KUNDE pflegt und verwendet mehrere Entwicklungs-Methoden, die grundsätzlich bei der Leistungserbringung zu berücksichtigen sind. Der IT-DIENSTLEISTER hat diese Methoden zu verwenden, kann aber bei berechtigten Gründen eine alternative Methode vorschlagen, die dann von KUNDE geprüft und genehmigt werden muss.

    2.1Methodik, Werkzeuge und Verfahren

    Zu den Aufgaben des IT-DIENSTLEISTERS gehören unter Einhaltung der KUNDE Standards:

    1.Die Dokumentation und Verfeinerung der ANWENDUNGS-Entwicklungsmethoden

    2.Die Weiterentwicklung von Methoden, Abläufen und Verfahren für die Erbringung von Services in der ANWENDUNGS-Entwicklungsmethoden und -betrieb.

    3.Die Koordination der Implementierung von Methoden, Abläufen und Verfahren.

    2.2Anwendungsstandards

    Zu den Aufgaben des IT-DIENSTLEISTERS gehören:

    1.Die Einhaltung der Standards für Benutzeroberfläche, Maschinenschnittstelle und Programmierung von KUNDE (z.B. GUI, EDI und IP) bei allen Entwicklungs-, Erweiterungs- und Wartungsaktivitäten. Aufbauend auf früheren Erfahrungen kann der IT-DIENSTLEISTER Verbesserungen an den ANWENDUNGS-Standards vorschlagen, und im Falle einer Annahme und Genehmigung dieser Vorschläge durch KUNDE, die ANWENDUNGS-Standards überarbeiten.

    2.Die Entwicklung und Kommunikation dieser ANWENDUNGS-Standards.

    2.3Fehlerkorrektur

    Zu den Aufgaben des IT-DIENSTLEISTERS gehören:

    1.Die Behebung aller ANWENDUNGS-Incidents bzw. -Probleme, die Änderungen der Datenbank, des ANWENDUNGS-Codes und/oder des Betriebsablaufs erfordern, die aus der Fehlerkorrektur resultieren.

    2.Die Übernahme der Zuständigkeit für vom IT-DIENSTLEISTER gewartete SOFTWARE, THIRD PARTY SOFTWARE von KUNDE und THIRD PARTY SOFTWARE des IT-DIENSTLEISTERS durch folgende Maßnahmen in Abstimmung und Freigabe durch KUNDE:

    2.1.Die Erkennung von ANWENDUNGS- und/oder Datenbankproblemen.

    2.2.Die Benachrichtigung des zuständigen IT-DIENSTLEISTERS.

    2.3.Die Veranlassung der durchzuführenden Korrekturen.

    3.Die Bereitstellung eines Notfallsupports mit folgenden Zielen:

    3.1.Die Vermeidung abnormaler Programmausfälle in der Produktionsumgebung.

    3.2.Die Behebung aller anderen Probleme, die in Verbindung mit einer ANWENDUNG, Datenbank und, falls zutreffend, SYSTEM SOFTWARE auftreten können („Korrektur im Fehlerfall“).

    Es müssen Workarounds/Zwischenlösungen geschaffen bzw. implementiert werden, zur Eingrenzung möglicher Business Beeinträchtigungen.

    Dies schließt alle Aktionen ein, die erforderlich sind, um sowohl die ANWENDUNG als auch alle SERVICES für KUNDE wiederherzustellen, einschließlich der Koordination der Betriebsabläufe mit den Betriebsverantwortlichen des ANWENDUNGS- und IT-Infrastrukturbetriebs, um Produktionspläne in folgenden Fällen neu aufzusetzen oder zu ergänzen:

    3.2.1.Die eingeschränkte VERFÜGBARKEIT von kritischen Schnittstellen bzw. ANWENDUNGEN, Datenbanken oder SYSTEM SOFTWARE.

    3.2.2.Probleme mit EQUIPMENT oder NETZWERK Kommunikation.

    4.Die enge Zusammenarbeit mit zuständigen MITARBEITERN von KUNDE, um eine angemessene Berichterstattung über Fortschritte und eine effektive Lösung von Produktionsproblemen sicherzustellen.

    5.Die Durchführung von Aktivitäten zur Fehlerkorrektur in Übereinstimmung mit SERVICE LEVEL-Anforderungen.

    \ No newline at end of file + + + + + C:\Users\Eric\Documents\Open-Xml-PowerTools\OpenXmlPowerToolsExamples\HtmlConverter01\Contract.docx + + + + +

    1.0EINLEITUNG

    1.Der IT-DIENSTLEISTER bestätigt, dass er – falls nicht anders angegeben – eine Lösung bereitstellen wird, die alle in dieser LEISTUNGSBESCHREIBUNG (Statement of Work - SOW) und ihren Anhängen angeführten Geschäftsprozesse unterstützt und dass alle SERVICES - falls nicht ausdrücklich anderweitig festgelegt - in den BASE CHARGES enthalten sind. Der IT-DIENSTLEISTER verpflichtet sich zu dem Ansatz einer kontinuierlichen Verbesserung.

    2.Der IT-DIENSTLEISTER reagiert auf derzeitige und künftige Anforderungen von KUNDE, schätzt proaktiv den künftigen Bedarf und wird seine Dienstleistungen innerhalb der BASE CHARGES entsprechend adjustieren. Anforderungen bezüglich neuer Dienstleistungen oder gravierender Änderung der Rahmenbedingungen werden innerhalb des CHANGE CONTROL VERFAHRENS behandelt, wobei der IT-DIENSTLEISTER in Zusammenarbeit mit KUNDE die Auswirkungen einer solchen Anforderung auf die Betriebsumgebung von KUNDE beurteilt.

    3.Ab dem ANFANGSDATUM wird der IT-DIENSTLEISTER für die Ausführung der in dieser LEISTUNGSBESCHREIBUNG festgelegten Application Development und Maintenance Service (ADM SERVICES) verantwortlich sein. Der IT-DIENSTLEISTER wird den in Anhang X festgelegten Umfang der ADM Services für die ANWENDUNGSSOFTWARE erbringen.

    2.0Beispiel-Überschrift

    KUNDE pflegt und verwendet mehrere Entwicklungs-Methoden, die grundsätzlich bei der Leistungserbringung zu berücksichtigen sind. Der IT-DIENSTLEISTER hat diese Methoden zu verwenden, kann aber bei berechtigten Gründen eine alternative Methode vorschlagen, die dann von KUNDE geprüft und genehmigt werden muss.

    2.1Methodik, Werkzeuge und Verfahren

    Zu den Aufgaben des IT-DIENSTLEISTERS gehören unter Einhaltung der KUNDE Standards:

    1.Die Dokumentation und Verfeinerung der ANWENDUNGS-Entwicklungsmethoden

    2.Die Weiterentwicklung von Methoden, Abläufen und Verfahren für die Erbringung von Services in der ANWENDUNGS-Entwicklungsmethoden und -betrieb.

    3.Die Koordination der Implementierung von Methoden, Abläufen und Verfahren.

    2.2Anwendungsstandards

    Zu den Aufgaben des IT-DIENSTLEISTERS gehören:

    1.Die Einhaltung der Standards für Benutzeroberfläche, Maschinenschnittstelle und Programmierung von KUNDE (z.B. GUI, EDI und IP) bei allen Entwicklungs-, Erweiterungs- und Wartungsaktivitäten. Aufbauend auf früheren Erfahrungen kann der IT-DIENSTLEISTER Verbesserungen an den ANWENDUNGS-Standards vorschlagen, und im Falle einer Annahme und Genehmigung dieser Vorschläge durch KUNDE, die ANWENDUNGS-Standards überarbeiten.

    2.Die Entwicklung und Kommunikation dieser ANWENDUNGS-Standards.

    2.3Fehlerkorrektur

    Zu den Aufgaben des IT-DIENSTLEISTERS gehören:

    1.Die Behebung aller ANWENDUNGS-Incidents bzw. -Probleme, die Änderungen der Datenbank, des ANWENDUNGS-Codes und/oder des Betriebsablaufs erfordern, die aus der Fehlerkorrektur resultieren.

    2.Die Übernahme der Zuständigkeit für vom IT-DIENSTLEISTER gewartete SOFTWARE, THIRD PARTY SOFTWARE von KUNDE und THIRD PARTY SOFTWARE des IT-DIENSTLEISTERS durch folgende Maßnahmen in Abstimmung und Freigabe durch KUNDE:

    2.1.Die Erkennung von ANWENDUNGS- und/oder Datenbankproblemen.

    2.2.Die Benachrichtigung des zuständigen IT-DIENSTLEISTERS.

    2.3.Die Veranlassung der durchzuführenden Korrekturen.

    3.Die Bereitstellung eines Notfallsupports mit folgenden Zielen:

    3.1.Die Vermeidung abnormaler Programmausfälle in der Produktionsumgebung.

    3.2.Die Behebung aller anderen Probleme, die in Verbindung mit einer ANWENDUNG, Datenbank und, falls zutreffend, SYSTEM SOFTWARE auftreten können („Korrektur im Fehlerfall“).

    Es müssen Workarounds/Zwischenlösungen geschaffen bzw. implementiert werden, zur Eingrenzung möglicher Business Beeinträchtigungen.

    Dies schließt alle Aktionen ein, die erforderlich sind, um sowohl die ANWENDUNG als auch alle SERVICES für KUNDE wiederherzustellen, einschließlich der Koordination der Betriebsabläufe mit den Betriebsverantwortlichen des ANWENDUNGS- und IT-Infrastrukturbetriebs, um Produktionspläne in folgenden Fällen neu aufzusetzen oder zu ergänzen:

    3.2.1.Die eingeschränkte VERFÜGBARKEIT von kritischen Schnittstellen bzw. ANWENDUNGEN, Datenbanken oder SYSTEM SOFTWARE.

    3.2.2.Probleme mit EQUIPMENT oder NETZWERK Kommunikation.

    4.Die enge Zusammenarbeit mit zuständigen MITARBEITERN von KUNDE, um eine angemessene Berichterstattung über Fortschritte und eine effektive Lösung von Produktionsproblemen sicherzustellen.

    5.Die Durchführung von Aktivitäten zur Fehlerkorrektur in Übereinstimmung mit SERVICE LEVEL-Anforderungen.

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-01.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-01.html index b3bb7e5a..33026592 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-01.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-01.html @@ -1,143 +1,180 @@ -

    ‏ ‏

    Picture 1

    ‏ ‏

    ‏טיול ליפן אפריל 2014‏

    ‏ ‏

    ‏שלום רב,‏

    ‏ ‏

    ‏בחרתם לצאת ולתור את "ארץ השמש העולה", הלא היא יפן.‏

    ‏ ‏

    ‏מרגע נחיתתכם באי הונשו ‏‏–‏‏ שהוא האי הגדול מבין ארבעת איי יפן העיקריים, תבינו שיפן היא לא רק "סוני", "טיוטה", "מיצובישי" וכו' אלא מדינה מרתקת, יפה ומגוונת.‏

    ‏ ‏

    ‏ההתרגשות לקראת הנסיעה הולכת וגוברת עם התקרב מועד היציאה, ויחד עם זאת ישנן שאלות שעליהן הנכם מחכים לקבל תשובות.‏

    ‏ ‏

    ‏מקווה שדף מידע זה יהיה לכם לעזר. אם לאחר מפגש הקבוצה וקריאת המידע המובא כאן תתעוררנה שאלות, הרגישו חופשיים ליצור עמי קשר עוד בטרם היציאה לטיול. אשתדל לתת לכם את האינפורמציה הנדרשת.‏

    ‏ ‏

    ‏להלן מספרי הטלפון שלי ‏‏–‏‏ ‏

    ‏ ‏

    ‏בית ‏‏–‏‏ 09-7425733‏

    ‏נייד ‏‏–‏‏ 052-2231525 ‏

    ‏ ‏

    ‏מזג אויר ‏‏–‏‏ אביבי ‏‏–‏‏ (כדאי ורצוי לבדוק באינטרנט לפני היציאה לטיול)‏

    ‏מטבע מקומי ‏‏–‏‏ יין (‏‎JPY‎‏ )‏

    ‏שער חליפין ‏‏–‏

    ‏ ‏‏1$= 102.3 יין‏

     ‏1ש"ח=29.41 יין‏

    ‏(שער ההמרה נכון ל ‏‏–‏‏ 9/4/2014) ‏

    ‏הוצאה יומית לאדם ‏‏–‏‏ כ- 25$‏

    ‏ ‏

    ‏בבתי עסק ובבתי המלון ניתן להשתמש בכרטיסי אשראי.‏

    ‏ ‏

    ‏הפרשי השעות בין ישראל ליפן ‏‏–‏‏ 6+ (יפן מקדימה את ישראל ב ‏‏–‏‏ 6 שעות).‏

    ‏תקשורת ‏‏–‏‏ הקידומת ליפן היא 81 ‏

    ‏טלפון מיפן לארץ: קוד גישה בינלאומי; ‏‎JDC 001;KDD 0041;JT 0061 ‎‏ +972 +קידומת האזור בלי "0"'‏‎ ‎‏+מספר הטלפון.‏

    ‏לבעלי סמארט פון ממליצה להוריד את התוכנה ‏‎BPHON‎‏ של בזק. (רק בעלי קו בזק בביתם יכולים להוריד תוכנה זו).‏

    ‏ ‏

    ‏ביפן ניתן להשתמש רק בפלפונים מהדור השלישי ומעלה.‏

    ‏ ‏

    ‏תקשורת אינטרנטית ברוב בתי המלון.‏

    ‏ ‏

    ‏ ‏ ib2225

    ‏חשמל ‏‏–‏‏ הספק ‏‏–‏‏ 100‏‎V‎‏ .כך נראה תקע יפאני ולו יש להתאים מעביר.‏‎ ‎

    ‏ ‏

    ‏לבוש וציוד ‏‏–‏‏ בגדים מתאימים למזג האויר. נכון לימים אלה הטמפ' היא בסביבות 20+ מעלות במהלך היום.יש להביא מעיל ,צעיף,מטריה, נעלי הליכה נוחות, נעלים להחלפה.‏

    ‏שימו לב ‏‏–‏‏ בביקור בהר קויה עשוי להיות קר יותר מאשר ביתר חלקי הטיול.‏

    ‏קרם הגנה‏

    ‏משקפי קריאה רזרביים‏

    ‏משקפי שמש‏

    ‏תרופות ‏‏–‏‏ לעזרה ראשונה, כמו קולדקס, סטופ איט, דקסמול ומעורר פעולת מעים.‏

    ‏צילום של הדרכון ושל כרטיס הטיסה.‏

    ‏ ‏

    ‏באם יש לך ספור מעניין/מוסיקה טובה/ רעיון למשחק ‏‏–‏‏ נחמד באם גם פריטים אלה יהיו בין הדברים שהנך מביא/ה לטיול.‏

    ‏ ‏

    ‏מזוודה ‏‏–‏‏ מזוודת המטען לא תעלה על ‏‏20 ק"ג‏‏ לנוסע.‏

    ‏ ‏

    ‏תיק יד ‏‏–‏‏ (מזוודת יד) ‏‏–‏‏ כדאי ורצוי לארוז חליפת בגדים ובגדים תחתונים;תרופות לשימוש יום יומי.‏

    ‏בתיק היד ניתן להכניס מיכלים שהתכולה שלהם לא עולה על 50 מ"ל. אנא הקפידו על גודל זה.‏

    ‏כמו כן יש להוציא כל מכשיר חד מתיק היד.‏

    ‏ ‏

    ‏מבקשת מכל חברי הקבוצה להגיע ביום רביעי ה- 16/4/2014 בשעה 09:30 לשער מס' 32 שבשדה התעופה. אודה לכולם באם תעמדו בלוח זמנים זה.‏

    ‏ ‏

    ‏רק לאחר שהודעתם לי על הגעתכם תתחילו בתהליך הצ'אק אין. את מזוודת המטען יש לשלח ישירות לטוקיו.‏

    ‏ ‏

    ‏עלינו לזכור שהצלחת הטיול תלויה בכל אחד מאתנו. אחד הכללים החשבוים הוא עמידה בלוח זמנים !!!!‏

    ‏ ‏

    ‏ ‏

    ‏ברצוני לאחל לכם ולב"ב חג אביב צבעוני ושמח !!!‏

    ‏ ‏

    ‏ ‏

    ‏בברכה,‏

    ‏ ‏

    ‏שרה פרי‏

    ‏ ‏

    ‏ ‏

    ‏ ‏

    ‏ ‏

    \ No newline at end of file + + + + + + + + + +

    ‏ ‏

    Picture 1

    ‏ ‏

    ‏טיול ליפן אפריל 2014‏

    ‏ ‏

    ‏שלום רב,‏

    ‏ ‏

    ‏בחרתם לצאת ולתור את "ארץ השמש העולה", הלא היא יפן.‏

    ‏ ‏

    ‏מרגע נחיתתכם באי הונשו ‏‏–‏‏ שהוא האי הגדול מבין ארבעת איי יפן העיקריים, תבינו שיפן היא לא רק "סוני", "טיוטה", "מיצובישי" וכו' אלא מדינה מרתקת, יפה ומגוונת.‏

    ‏ ‏

    ‏ההתרגשות לקראת הנסיעה הולכת וגוברת עם התקרב מועד היציאה, ויחד עם זאת ישנן שאלות שעליהן הנכם מחכים לקבל תשובות.‏

    ‏ ‏

    ‏מקווה שדף מידע זה יהיה לכם לעזר. אם לאחר מפגש הקבוצה וקריאת המידע המובא כאן תתעוררנה שאלות, הרגישו חופשיים ליצור עמי קשר עוד בטרם היציאה לטיול. אשתדל לתת לכם את האינפורמציה הנדרשת.‏

    ‏ ‏

    ‏להלן מספרי הטלפון שלי ‏‏–‏‏ ‏

    ‏ ‏

    ‏בית ‏‏–‏‏ 09-7425733‏

    ‏נייד ‏‏–‏‏ 052-2231525 ‏

    ‏ ‏

    ‏מזג אויר ‏‏–‏‏ אביבי ‏‏–‏‏ (כדאי ורצוי לבדוק באינטרנט לפני היציאה לטיול)‏

    ‏מטבע מקומי ‏‏–‏‏ יין (‏‎JPY‎‏ )‏

    ‏שער חליפין ‏‏–‏

    ‏ ‏‏1$= 102.3 יין‏

     ‏1ש"ח=29.41 יין‏

    ‏(שער ההמרה נכון ל ‏‏–‏‏ 9/4/2014) ‏

    ‏הוצאה יומית לאדם ‏‏–‏‏ כ- 25$‏

    ‏ ‏

    ‏בבתי עסק ובבתי המלון ניתן להשתמש בכרטיסי אשראי.‏

    ‏ ‏

    ‏הפרשי השעות בין ישראל ליפן ‏‏–‏‏ 6+ (יפן מקדימה את ישראל ב ‏‏–‏‏ 6 שעות).‏

    ‏תקשורת ‏‏–‏‏ הקידומת ליפן היא 81 ‏

    ‏טלפון מיפן לארץ: קוד גישה בינלאומי; ‏‎JDC 001;KDD 0041;JT 0061 ‎‏ +972 +קידומת האזור בלי "0"'‏‎ ‎‏+מספר הטלפון.‏

    ‏לבעלי סמארט פון ממליצה להוריד את התוכנה ‏‎BPHON‎‏ של בזק. (רק בעלי קו בזק בביתם יכולים להוריד תוכנה זו).‏

    ‏ ‏

    ‏ביפן ניתן להשתמש רק בפלפונים מהדור השלישי ומעלה.‏

    ‏ ‏

    ‏תקשורת אינטרנטית ברוב בתי המלון.‏

    ‏ ‏

    ‏ ‏ ib2225

    ‏חשמל ‏‏–‏‏ הספק ‏‏–‏‏ 100‏‎V‎‏ .כך נראה תקע יפאני ולו יש להתאים מעביר.‏‎ ‎

    ‏ ‏

    ‏לבוש וציוד ‏‏–‏‏ בגדים מתאימים למזג האויר. נכון לימים אלה הטמפ' היא בסביבות 20+ מעלות במהלך היום.יש להביא מעיל ,צעיף,מטריה, נעלי הליכה נוחות, נעלים להחלפה.‏

    ‏שימו לב ‏‏–‏‏ בביקור בהר קויה עשוי להיות קר יותר מאשר ביתר חלקי הטיול.‏

    ‏קרם הגנה‏

    ‏משקפי קריאה רזרביים‏

    ‏משקפי שמש‏

    ‏תרופות ‏‏–‏‏ לעזרה ראשונה, כמו קולדקס, סטופ איט, דקסמול ומעורר פעולת מעים.‏

    ‏צילום של הדרכון ושל כרטיס הטיסה.‏

    ‏ ‏

    ‏באם יש לך ספור מעניין/מוסיקה טובה/ רעיון למשחק ‏‏–‏‏ נחמד באם גם פריטים אלה יהיו בין הדברים שהנך מביא/ה לטיול.‏

    ‏ ‏

    ‏מזוודה ‏‏–‏‏ מזוודת המטען לא תעלה על ‏‏20 ק"ג‏‏ לנוסע.‏

    ‏ ‏

    ‏תיק יד ‏‏–‏‏ (מזוודת יד) ‏‏–‏‏ כדאי ורצוי לארוז חליפת בגדים ובגדים תחתונים;תרופות לשימוש יום יומי.‏

    ‏בתיק היד ניתן להכניס מיכלים שהתכולה שלהם לא עולה על 50 מ"ל. אנא הקפידו על גודל זה.‏

    ‏כמו כן יש להוציא כל מכשיר חד מתיק היד.‏

    ‏ ‏

    ‏מבקשת מכל חברי הקבוצה להגיע ביום רביעי ה- 16/4/2014 בשעה 09:30 לשער מס' 32 שבשדה התעופה. אודה לכולם באם תעמדו בלוח זמנים זה.‏

    ‏ ‏

    ‏רק לאחר שהודעתם לי על הגעתכם תתחילו בתהליך הצ'אק אין. את מזוודת המטען יש לשלח ישירות לטוקיו.‏

    ‏ ‏

    ‏עלינו לזכור שהצלחת הטיול תלויה בכל אחד מאתנו. אחד הכללים החשבוים הוא עמידה בלוח זמנים !!!!‏

    ‏ ‏

    ‏ ‏

    ‏ברצוני לאחל לכם ולב"ב חג אביב צבעוני ושמח !!!‏

    ‏ ‏

    ‏ ‏

    ‏בברכה,‏

    ‏ ‏

    ‏שרה פרי‏

    ‏ ‏

    ‏ ‏

    ‏ ‏

    ‏ ‏

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-02.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-02.html index af21ae08..e47f4823 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-02.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Hebrew-02.html @@ -1,215 +1,260 @@ -

    ‎ ‎

    MMj02831010000[1]

    ‏ ‏

    ‏ ‏

    ‏טיול להודו נפאל פברואר 2013 ‏

    ‏ ‏

    ‏לנוסעים שלום,‏

    ‏ ‏

    ‏מועד הטיול הולך ומתקרב. אתם עומדים לצאת לחבל ארץ בתוך תת היבשת ההודית. הטיול הנו טיול מרתק. טיול שיפעיל את כל החושים. זה לא עוד טיול, אלא חוויה שאי-אפשר לחזור ממנה אדישים. ‏

    ‏מקום שמעורר מחשבות רבות. מעלה הרבה מאוד סימני שאלה ויחד עם זאת רבים מהנוסעים חוזרים הביתה עם תחושה שהם רוצים לחזור ולתור בה פעם נוספת. מקווה שאתם תשובו עם הרגשה דומה.‏

    ‏ ‏

    ‏להלן מספר עצות לנוסעים:‏

    ‏ ‏

    ‏הכנות טרום נסיעה ‏‏–‏

    ‏כדאי לפנות ללשכת הבריאות הקרובה למקום מגוריכם, בכדי להתייעץ עם המחלקה לטיולים ולקבל הדרכה לגבי החיסונים הדרושים לפי מצב הבריאות שלכם.‏

    ‏ ‏

    ‏ויזות,דרכונים,ביטוח ותיקי יד -‏

    ‏1.‏‏תוקף הדרכון לפחות חצי שנה.‏

    ‏2.‏‏ויזה להודו (כניסה כפולה). הויזה לנפאל נעשית בכניסה לנפאל, יש להצטייד בשתי תמונות פספורט להוצאת הויזה. ‏‏עלות ויזה הוא 25$ לנוסע. כ"א מהנוסעים ישלם סכום זה בכניסה.‏

    ‏3.‏‏ביטוח רפואי ומטען: כדאי לעשות ביטוח רפואי הכולל פינוי בהיטס ולא להסתפק בביטוח סטנדרטי של חברות כרטיסי האשראי.‏

    ‏4.‏‏יש לצלם את העמודים הרלוונטים מהדרכון (פרטים וויזה) וכן צילום של כרטיסי הטיסה ולהטמינם במזוודה.‏

    ‏5.‏‏בתיק היד/מזוודת היד רצוי להכניס חליפת בגדים להחלפה ותרופות.‏

    ‏6.‏‏בתיקי היד יש לקחת מיכלים (שפורפרות ובקבוקים) המכילים עד 50 ממ"ל.‏

    ‏7.‏‏משקל מזוודת המטען לא יעלה על 20 ק"ג.‏

    ‏ ‏

    ‏הטיסה מנתב"ג ב ‏‏–‏‏ 25/2/2013, מספר טיסה ‏‎TK ‎‎–‎‎ 787 ‎‏ ‏‏את המטען יש לשלוח ישירות למובאי. ‏

    ‏ ‏

    ‏יש להגיע לש"ת 3 לפני שעת ההמראה. כלומר,בשעה 12:30. אמתין לכם בשער 32 (על יד הדלפק שמצויין כנקודת מפגש לקבוצות). מבקשת מכל הנוסעים להגיע לנקודת המפגש עוד טרם תחילת תהליך הצ'ק אין.‏

    ‏ ‏

    ‏כסף והוצאות ‏‏–‏

    ‏כדאי להצטייד בדולר אמריקאי רצוי שהשטרות יהיו חדשים, כמו כן מומלץ להצטייד בשטרות הקטנים מ ‏‏–‏‏ 100$.‏

    ‏ההוצאות היומיות השוטפות לאדם הן כ- 20$ (אנחנו מקבלים ארוחת בוקר וערב).‏‏ סכום זה איננו כולל קניות.‏

    ‏בנוסף לסכום הנ"ל -‏

    ‏כל נוסע ישלם 25$ עבור ויזה לנפאל.‏

    ‏כל נוסע ישלם 30$ מס נמל בקטמנדו.‏

    ‏ ‏

    ‏רצוי לשמור את אחת מקבלות המרת הכספים וזאת בכדי שאפשר יהיה להחליף את הכסף העודף עם סיום הטיול.‏

    ‏שערי ההמרה:‏

    #x200fכ- 53.9 רופי הודי ל #x200f#x200f–#x200f#x200f 1$.#x200f ‏14.66 רופי הודי = 1 ש"ח‏

    #x200fכ- 86.68 רופי נפאלי ל #x200f#x200f–#x200f#x200f 1$.#x200f ‏23.75 רופי נפלאי = 1 ₪‏‏ ‏

    ‏ ‏

    ‏הפרשי זמנים ‏‏–‏

    ‏שעון נפאל ‏‏מקדים‏‏ את ישראל ב- 3 שעות ו- 45 דקות‏

    ‏שעון הודו ‏‏מקדים‏‏ את שעון ישראל ב ‏‏–‏‏ 3 שעות ו ‏‏–‏‏ 30 דקות‏

    ‏ ‏

    ‏תקשורת ‏‏–‏‏ ‏

    ‎-‎‏אינטרנט‏

    ‎-‎‏טלפונים ציבוריים‏

    ‎-‎‏טלפונים סלולריים ‏‏–‏‏ השימוש יקר.ישנם אזורים שבהם אין כיסוי לטלפונים.‏

    ‎-‎‏לבעלי הסמארט פון ובעלי קו בזק בבית, ממליצה להוריד את האפליקציה של בזק שנקראת בי-פון - ‏‎BPHONE‎

    ‎-‎‏צלמו את רשימת בתי המלון ומספרי הטלפון שתקבלו והשא‏‏ירו את הרשימה למשפחה.‏

    ‏ ‏

    ‏חשמל: 220 ‏‎W‎

    ‎ ‎

    ‏ ‏

    ‏תרופות והיגיינ‏‏ה‏‏ ‏‏–‏

    ‏למשתמשים בתרופות באופן קבוע יש לקחת אותן בתיק היד. רצוי לקחת תרופות לשימוש אישי כגון: אקומול, אקומול קולד, תרופה נגד הרעלת קיבה, וכדורים מעוררי קיבה, אגדים נדבקים.‏

    ‏ממליצה לקחת מטליות לחות וג'ל לחיטוי הידיים.‏

    ‏ ‏

    ‏ ‏

    ‏אוכל ומים ‏‏–‏

    ‏אפשר להביא מעט חטיפים, פירות יבשים וכו' לנשנוש בין הארוחות. (אל תעמיסו הרבה מדי !!).‏

    ‏למעונייני‏‏ם‏‏ באוכל צמחוני בטיסות יש לעדכן מבעוד מועד את סוכן הנסיעות שאצלו רכשתם את הטיול.‏

    ‏מים ‏‏–‏‏ ‏‏יש לשתות מי ‏‏–‏‏ פולארי‏‏ס‏‏. בעת קניית בקבוק מים יש לבדוק שהפקק הוא מקורי. ניתן לרכוש מים בכל מקום. אין צורך לקחת מהארץ.‏

    ‏ ‏

    ‏ ‏

    ‏ציוד אישי ‏‏–‏

    ‏נעלי הליכה, כובע משקפי שמש, משקפי ראיה רזרביים, קרם הגנה, מעיל, מטריה.‏

    ‏מבחינתלבוש כדאי להצטייד בלבוש הדומה לזה שאנו מתלבשים בימים אלה בארץ. הבוקר חמים ובערב עשוי להיות קריר/קר.‏

    ‏ ‏

    ‏נחמד יהיה באם תביאו עמכם מספר תקליטורי מוסיקה, ספור/משחק וכו'.‏

    ‏ ‏

    ‏הצלחת הטיול תלויה בכל אחד ואחת מאתנו. חלק מההצלחה הוא עמידה בלוחות זמנים וגילוי סבלנות וסובלנות . ‏

    ‏ ‏

    ‏חשוב להשאיר את טרדות היום יום בבית, להגיע לטיול עם לב פתוח, אוזן קשובה ועין פקוחה, בכדי שהטיול יהיה משמעותי וחוייתי.‏

    ‏ ‏

    ‏ ‏

    ‏בתקווה שנצא בשלום ונשוב כולנו לשלום.‏

    ‏ ‏

    ‏בברכה,‏

    ‏ ‏

    ‏שרה פרי‏

    ‏ ‏

    ‏טלפונים: בבית ‏‏–‏‏ 09-7425733 : נייד ‏‏–‏‏ 052-22315252‏

    \ No newline at end of file + + + + + + + + + +

    ‎ ‎

    MMj02831010000[1]

    ‏ ‏

    ‏ ‏

    ‏טיול להודו נפאל פברואר 2013 ‏

    ‏ ‏

    ‏לנוסעים שלום,‏

    ‏ ‏

    ‏מועד הטיול הולך ומתקרב. אתם עומדים לצאת לחבל ארץ בתוך תת היבשת ההודית. הטיול הנו טיול מרתק. טיול שיפעיל את כל החושים. זה לא עוד טיול, אלא חוויה שאי-אפשר לחזור ממנה אדישים. ‏

    ‏מקום שמעורר מחשבות רבות. מעלה הרבה מאוד סימני שאלה ויחד עם זאת רבים מהנוסעים חוזרים הביתה עם תחושה שהם רוצים לחזור ולתור בה פעם נוספת. מקווה שאתם תשובו עם הרגשה דומה.‏

    ‏ ‏

    ‏להלן מספר עצות לנוסעים:‏

    ‏ ‏

    ‏הכנות טרום נסיעה ‏‏–‏

    ‏כדאי לפנות ללשכת הבריאות הקרובה למקום מגוריכם, בכדי להתייעץ עם המחלקה לטיולים ולקבל הדרכה לגבי החיסונים הדרושים לפי מצב הבריאות שלכם.‏

    ‏ ‏

    ‏ויזות,דרכונים,ביטוח ותיקי יד -‏

    ‏1.‏‏תוקף הדרכון לפחות חצי שנה.‏

    ‏2.‏‏ויזה להודו (כניסה כפולה). הויזה לנפאל נעשית בכניסה לנפאל, יש להצטייד בשתי תמונות פספורט להוצאת הויזה. ‏‏עלות ויזה הוא 25$ לנוסע. כ"א מהנוסעים ישלם סכום זה בכניסה.‏

    ‏3.‏‏ביטוח רפואי ומטען: כדאי לעשות ביטוח רפואי הכולל פינוי בהיטס ולא להסתפק בביטוח סטנדרטי של חברות כרטיסי האשראי.‏

    ‏4.‏‏יש לצלם את העמודים הרלוונטים מהדרכון (פרטים וויזה) וכן צילום של כרטיסי הטיסה ולהטמינם במזוודה.‏

    ‏5.‏‏בתיק היד/מזוודת היד רצוי להכניס חליפת בגדים להחלפה ותרופות.‏

    ‏6.‏‏בתיקי היד יש לקחת מיכלים (שפורפרות ובקבוקים) המכילים עד 50 ממ"ל.‏

    ‏7.‏‏משקל מזוודת המטען לא יעלה על 20 ק"ג.‏

    ‏ ‏

    ‏הטיסה מנתב"ג ב ‏‏–‏‏ 25/2/2013, מספר טיסה ‏‎TK ‎‎–‎‎ 787 ‎‏ ‏‏את המטען יש לשלוח ישירות למובאי. ‏

    ‏ ‏

    ‏יש להגיע לש"ת 3 לפני שעת ההמראה. כלומר,בשעה 12:30. אמתין לכם בשער 32 (על יד הדלפק שמצויין כנקודת מפגש לקבוצות). מבקשת מכל הנוסעים להגיע לנקודת המפגש עוד טרם תחילת תהליך הצ'ק אין.‏

    ‏ ‏

    ‏כסף והוצאות ‏‏–‏

    ‏כדאי להצטייד בדולר אמריקאי רצוי שהשטרות יהיו חדשים, כמו כן מומלץ להצטייד בשטרות הקטנים מ ‏‏–‏‏ 100$.‏

    ‏ההוצאות היומיות השוטפות לאדם הן כ- 20$ (אנחנו מקבלים ארוחת בוקר וערב).‏‏ סכום זה איננו כולל קניות.‏

    ‏בנוסף לסכום הנ"ל -‏

    ‏כל נוסע ישלם 25$ עבור ויזה לנפאל.‏

    ‏כל נוסע ישלם 30$ מס נמל בקטמנדו.‏

    ‏ ‏

    ‏רצוי לשמור את אחת מקבלות המרת הכספים וזאת בכדי שאפשר יהיה להחליף את הכסף העודף עם סיום הטיול.‏

    ‏שערי ההמרה:‏

    #x200fכ- 53.9 רופי הודי ל #x200f#x200f–#x200f#x200f 1$.#x200f ‏14.66 רופי הודי = 1 ש"ח‏

    #x200fכ- 86.68 רופי נפאלי ל #x200f#x200f–#x200f#x200f 1$.#x200f ‏23.75 רופי נפלאי = 1 ₪‏‏ ‏

    ‏ ‏

    ‏הפרשי זמנים ‏‏–‏

    ‏שעון נפאל ‏‏מקדים‏‏ את ישראל ב- 3 שעות ו- 45 דקות‏

    ‏שעון הודו ‏‏מקדים‏‏ את שעון ישראל ב ‏‏–‏‏ 3 שעות ו ‏‏–‏‏ 30 דקות‏

    ‏ ‏

    ‏תקשורת ‏‏–‏‏ ‏

    ‎-‎‏אינטרנט‏

    ‎-‎‏טלפונים ציבוריים‏

    ‎-‎‏טלפונים סלולריים ‏‏–‏‏ השימוש יקר.ישנם אזורים שבהם אין כיסוי לטלפונים.‏

    ‎-‎‏לבעלי הסמארט פון ובעלי קו בזק בבית, ממליצה להוריד את האפליקציה של בזק שנקראת בי-פון - ‏‎BPHONE‎

    ‎-‎‏צלמו את רשימת בתי המלון ומספרי הטלפון שתקבלו והשא‏‏ירו את הרשימה למשפחה.‏

    ‏ ‏

    ‏חשמל: 220 ‏‎W‎

    ‎ ‎

    ‏ ‏

    ‏תרופות והיגיינ‏‏ה‏‏ ‏‏–‏

    ‏למשתמשים בתרופות באופן קבוע יש לקחת אותן בתיק היד. רצוי לקחת תרופות לשימוש אישי כגון: אקומול, אקומול קולד, תרופה נגד הרעלת קיבה, וכדורים מעוררי קיבה, אגדים נדבקים.‏

    ‏ממליצה לקחת מטליות לחות וג'ל לחיטוי הידיים.‏

    ‏ ‏

    ‏ ‏

    ‏אוכל ומים ‏‏–‏

    ‏אפשר להביא מעט חטיפים, פירות יבשים וכו' לנשנוש בין הארוחות. (אל תעמיסו הרבה מדי !!).‏

    ‏למעונייני‏‏ם‏‏ באוכל צמחוני בטיסות יש לעדכן מבעוד מועד את סוכן הנסיעות שאצלו רכשתם את הטיול.‏

    ‏מים ‏‏–‏‏ ‏‏יש לשתות מי ‏‏–‏‏ פולארי‏‏ס‏‏. בעת קניית בקבוק מים יש לבדוק שהפקק הוא מקורי. ניתן לרכוש מים בכל מקום. אין צורך לקחת מהארץ.‏

    ‏ ‏

    ‏ ‏

    ‏ציוד אישי ‏‏–‏

    ‏נעלי הליכה, כובע משקפי שמש, משקפי ראיה רזרביים, קרם הגנה, מעיל, מטריה.‏

    ‏מבחינתלבוש כדאי להצטייד בלבוש הדומה לזה שאנו מתלבשים בימים אלה בארץ. הבוקר חמים ובערב עשוי להיות קריר/קר.‏

    ‏ ‏

    ‏נחמד יהיה באם תביאו עמכם מספר תקליטורי מוסיקה, ספור/משחק וכו'.‏

    ‏ ‏

    ‏הצלחת הטיול תלויה בכל אחד ואחת מאתנו. חלק מההצלחה הוא עמידה בלוחות זמנים וגילוי סבלנות וסובלנות . ‏

    ‏ ‏

    ‏חשוב להשאיר את טרדות היום יום בבית, להגיע לטיול עם לב פתוח, אוזן קשובה ועין פקוחה, בכדי שהטיול יהיה משמעותי וחוייתי.‏

    ‏ ‏

    ‏ ‏

    ‏בתקווה שנצא בשלום ונשוב כולנו לשלום.‏

    ‏ ‏

    ‏בברכה,‏

    ‏ ‏

    ‏שרה פרי‏

    ‏ ‏

    ‏טלפונים: בבית ‏‏–‏‏ 09-7425733 : נייד ‏‏–‏‏ 052-22315252‏

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.cs b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.cs index 8617f35b..8dc0c269 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.cs +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.cs @@ -1,17 +1,9 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; using System.IO; using System.Linq; -using System.Runtime.InteropServices; -using System.Text; using System.Xml; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using OpenXmlPowerTools; -using OpenXmlPowerTools.HtmlToWml; /******************************************************************************************* * HtmlToWmlConverter expects the HTML to be passed as an XElement, i.e. as XML. While the HTML test files that @@ -19,22 +11,22 @@ * The best solution is to use the HtmlAgilityPack, which can parse HTML and save as XML. The HtmlAgilityPack * is licensed under the Ms-PL (same as Open-Xml-PowerTools) so it is convenient to include it in your solution, * and thereby you can convert HTML to XML that can be processed by the HtmlToWmlConverter. - * + * * A convenient way to get the DLL that has been checked out with HtmlToWmlConverter is to clone the repo at * https://github.com/EricWhiteDev/HtmlAgilityPack - * + * * That repo contains only the DLL that has been checked out with HtmlToWmlConverter. - * + * * Of course, you can also get the HtmlAgilityPack source and compile it to get the DLL. You can find it at * http://codeplex.com/HtmlAgilityPack - * + * * We don't include the HtmlAgilityPack in Open-Xml-PowerTools, to simplify installation. The example files * in this module do not require HtmlAgilityPack to run. *******************************************************************************************/ -class HtmlToWmlConverterExample +internal class HtmlToWmlConverterExample { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); @@ -48,7 +40,7 @@ static void Main(string[] args) private static void ConvertToDocx(string file, string destinationDir) { - bool s_ProduceAnnotatedHtml = true; + var s_ProduceAnnotatedHtml = true; var sourceHtmlFi = new FileInfo(file); Console.WriteLine("Converting " + sourceHtmlFi.Name); @@ -58,17 +50,17 @@ private static void ConvertToDocx(string file, string destinationDir) var destDocxFi = new FileInfo(Path.Combine(destinationDir, sourceHtmlFi.Name.Replace(".html", "-3-ConvertedByHtmlToWml.docx"))); var annotatedHtmlFi = new FileInfo(Path.Combine(destinationDir, sourceHtmlFi.Name.Replace(".html", "-4-Annotated.txt"))); - XElement html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceHtmlFi); - - string usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); + var html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceHtmlFi); + + var usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); File.WriteAllText(destCssFi.FullName, usedAuthorCss); - HtmlToWmlConverterSettings settings = HtmlToWmlConverter.GetDefaultSettings(); + var settings = HtmlToWmlConverter.GetDefaultSettings(); // image references in HTML files contain the path to the subdir that contains the images, so base URI is the name of the directory // that contains the HTML files settings.BaseUriForImages = sourceHtmlFi.DirectoryName; - WmlDocument doc = HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings, null, s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null); + var doc = HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings, null, s_ProduceAnnotatedHtml ? annotatedHtmlFi.FullName : null); doc.SaveAs(destDocxFi.FullName); } @@ -76,7 +68,7 @@ public class HtmlToWmlReadAsXElement { public static XElement ReadAsXElement(FileInfo sourceHtmlFi) { - string htmlString = File.ReadAllText(sourceHtmlFi.FullName); + var htmlString = File.ReadAllText(sourceHtmlFi.FullName); XElement html = null; try { @@ -104,9 +96,9 @@ public static XElement ReadAsXElement(FileInfo sourceHtmlFi) html = XElement.Parse(sb.ToString()); } #else - catch (XmlException e) + catch (XmlException) { - throw e; + throw; } #endif // HtmlToWmlConverter expects the HTML elements to be in no namespace, so convert all elements to no namespace. @@ -116,8 +108,7 @@ public static XElement ReadAsXElement(FileInfo sourceHtmlFi) private static object ConvertToNoNamespace(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { return new XElement(element.Name.LocalName, element.Attributes().Where(a => !a.IsNamespaceDeclaration), @@ -127,7 +118,7 @@ private static object ConvertToNoNamespace(XNode node) } } - static string defaultCss = + private static readonly string defaultCss = @"html, address, blockquote, body, dd, div, @@ -201,5 +192,5 @@ private static object ConvertToNoNamespace(XNode node) "; - static string userCss = @""; -} + private static readonly string userCss = @""; +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.csproj b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.csproj +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/HtmlToWmlConverter01.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/ResumeTemplate.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/ResumeTemplate.html index 58c9d654..8bc5a3ce 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/ResumeTemplate.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/ResumeTemplate.html @@ -1,156 +1,193 @@ -

    [您的姓名]   |   个人履历

    [邮政编码,省/市/自治区,市/县,地址]   [电话]   [电子邮件地址]

    教学经验

    [职务] - [课程或学院名称]

    [在此处添加简短说明]

    [起止年份]

    [职务] - [课程或学院名称]

    [要替换占位符文本(例如此文本),只需选择它并开始键入。请勿包括您的所选内容左侧或右侧的空格。]

    [起止年份]

    [职务] - [课程或学院名称]

    [要在此简历的任何表格中添加或删除行,只需单击某行,然后在功能区的“表格工具”下的“布局”选项卡上,单击“插入”或“删除”选项。]

    [起止年份]

    教育背景

    [获得的学位] - [学院,位置]

    [只需从“开始”选项卡的“样式”组中单击,即可应用您在此简历中看到的任何文本格式。]

    [起止年份]

    [获得的学位] - [学院,位置]

    [节标题使用“标题 1”样式。每个经验或教育条目的标题使用“标题 2”样式。此文本使用“普通”样式。右对齐文本使用“日期”样式。]

    [起止年份]

    发表作品

    [发表作品名称]

    [在此处添加简短说明]

    [发表日期]

    [发表作品名称]

    [在此处添加简短说明]

    [发表日期]

    获奖经历

    [奖名称],[学院]

    [获奖日期]

    [奖名称],[学院]

    [获奖日期]

    相关经验

    [职务] - [组织名称,地点]

    [在此处添加简短说明]

    [起止年份]

    \ No newline at end of file + + + + + + + + + +

    [您的姓名]   |   个人履历

    [邮政编码,省/市/自治区,市/县,地址]   [电话]   [电子邮件地址]

    教学经验

    [职务] - [课程或学院名称]

    [在此处添加简短说明]

    [起止年份]

    [职务] - [课程或学院名称]

    [要替换占位符文本(例如此文本),只需选择它并开始键入。请勿包括您的所选内容左侧或右侧的空格。]

    [起止年份]

    [职务] - [课程或学院名称]

    [要在此简历的任何表格中添加或删除行,只需单击某行,然后在功能区的“表格工具”下的“布局”选项卡上,单击“插入”或“删除”选项。]

    [起止年份]

    教育背景

    [获得的学位] - [学院,位置]

    [只需从“开始”选项卡的“样式”组中单击,即可应用您在此简历中看到的任何文本格式。]

    [起止年份]

    [获得的学位] - [学院,位置]

    [节标题使用“标题 1”样式。每个经验或教育条目的标题使用“标题 2”样式。此文本使用“普通”样式。右对齐文本使用“日期”样式。]

    [起止年份]

    发表作品

    [发表作品名称]

    [在此处添加简短说明]

    [发表日期]

    [发表作品名称]

    [在此处添加简短说明]

    [发表日期]

    获奖经历

    [奖名称],[学院]

    [获奖日期]

    [奖名称],[学院]

    [获奖日期]

    相关经验

    [职务] - [组织名称,地点]

    [在此处添加简短说明]

    [起止年份]

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/TaskPlanTemplate.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/TaskPlanTemplate.html index db2415eb..dc1807e3 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/TaskPlanTemplate.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/TaskPlanTemplate.html @@ -1,483 +1,546 @@ -

    [日期]

    [项目名称]

    [学生姓名] | [课程名称] | [教师姓名/上课时间/课时]

    需要在任务表中添加更多行?没问题。只需单击表格的最后一个单元格,然后按 Tab 键即可。

    或者,要在表格中间添加行,请单击某一行,然后在功能区的“表格工具”下的“布局”选项卡中单击“在上方插入”或“在下方插入”。

    要将(像这样的)占位符文本替换为您自己的文本,只需选中并开始键入即可。请勿在您选中的字符右侧或左侧包含空格。

    任务

    到期日

    已完成

    签名

    \ No newline at end of file + + + + + + + + + +

    [日期]

    [项目名称]

    [学生姓名] | [课程名称] | [教师姓名/上课时间/课时]

    需要在任务表中添加更多行?没问题。只需单击表格的最后一个单元格,然后按 Tab 键即可。

    或者,要在表格中间添加行,请单击某一行,然后在功能区的“表格工具”下的“布局”选项卡中单击“在上方插入”或“在下方插入”。

    要将(像这样的)占位符文本替换为您自己的文本,只需选中并开始键入即可。请勿在您选中的字符右侧或左侧包含空格。

    任务

    到期日

    已完成

    签名

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-01.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-01.html index a74237b2..86f49f27 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-01.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-01.html @@ -1,255 +1,303 @@ -

    Business Trip Checklist

    1.While You Are Away: Preparing the Office

    Organize any necessary meetings to take place on your trip; book appointments and meeting rooms.

    If traveling internationally, obtain any necessary paperwork and vaccinations.

    Familiarize yourself with local business customs common at destination.

    Confirm appointments, schedules, reservations, etc.

    Tie up any loose ends at the office (finish up projects; set up out-of-office replies; notify or remind coworkers about your departure).

    Print out hard copies of presentations, agendas, and important documents.

    2.While You Are Away: Preparing the Home

    Arrange for child, pet, and plant care; communicate needs and schedules.

    Pause routine deliveries.

    Make your home seem lived-in while away by putting lights and a radio on timers.

    Turn down thermostat.

    Leave house and car keys, and your complete itinerary, with a trusted friend.

    Lock windows, garages, and doors.

    3.Packing for the Trip

    Make a list of the specific items of clothing you’ll need to pack for your trip to suit the various functions you’ll attend.

    Try to pack everything you need in a carry-on bag, to avoid the possibility of lost luggage.

    If you check your bag, pack a second set of business clothes and toiletries in a carry-on bag, in case of lost luggage.

    Print several copies of this checklist, and save a copy on your computer’s hard drive to refer to when planning your next trip. Storing the checklist on your computer is the easiest way to make updates to it when necessary.

    4.What to Leave for Family and Caregivers at Home

    Leave your contact information—including the names, addresses, and phone numbers of the hotels where you are staying—with a family member, so they can reach you while you’re away.

    Phone numbers (all contact numbers for you; doctor/vet; pharmacy; mechanic; school/daycare; helpful friends/neighbors; alarm company).

    Cash for groceries and emergencies.

    Consent for medical treatment forms and insurance cards.

    Your travel itinerary.

    \ No newline at end of file + + + + + + + + + +

    Business Trip Checklist

    1.While You Are Away: Preparing the Office

    Organize any necessary meetings to take place on your trip; book appointments and meeting rooms.

    If traveling internationally, obtain any necessary paperwork and vaccinations.

    Familiarize yourself with local business customs common at destination.

    Confirm appointments, schedules, reservations, etc.

    Tie up any loose ends at the office (finish up projects; set up out-of-office replies; notify or remind coworkers about your departure).

    Print out hard copies of presentations, agendas, and important documents.

    2.While You Are Away: Preparing the Home

    Arrange for child, pet, and plant care; communicate needs and schedules.

    Pause routine deliveries.

    Make your home seem lived-in while away by putting lights and a radio on timers.

    Turn down thermostat.

    Leave house and car keys, and your complete itinerary, with a trusted friend.

    Lock windows, garages, and doors.

    3.Packing for the Trip

    Make a list of the specific items of clothing you’ll need to pack for your trip to suit the various functions you’ll attend.

    Try to pack everything you need in a carry-on bag, to avoid the possibility of lost luggage.

    If you check your bag, pack a second set of business clothes and toiletries in a carry-on bag, in case of lost luggage.

    Print several copies of this checklist, and save a copy on your computer’s hard drive to refer to when planning your next trip. Storing the checklist on your computer is the easiest way to make updates to it when necessary.

    4.What to Leave for Family and Caregivers at Home

    Leave your contact information—including the names, addresses, and phone numbers of the hotels where you are staying—with a family member, so they can reach you while you’re away.

    Phone numbers (all contact numbers for you; doctor/vet; pharmacy; mechanic; school/daycare; helpful friends/neighbors; alarm company).

    Cash for groceries and emergencies.

    Consent for medical treatment forms and insurance cards.

    Your travel itinerary.

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-02.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-02.html index 7455d964..2518d148 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-02.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-02.html @@ -1,686 +1,768 @@ -

    Weekly Assignments

    NAME:

    Eric White

    MONTH:

    January

    YEAR:

    2014

    Mon:

    1/27

    Tues:

    1/28

    Wed:

    1/29

    Thur:

    1/30

    Fri:

    1/31

    Math

    Read pages 24-50

    Read Book2 1-24

    Problems on page 51

    Word problem on 25

    English

    Report on Wind in the willows

    Read Catcher in the Rye

    SOCIAL STUDIES

    Current news report

    Science

    Lab 3-5

    Lab 3-5

    Lab 3-5

    Geography

    French

    \ No newline at end of file + + + + + + + + + +

    Weekly Assignments

    NAME:

    Eric White

    MONTH:

    January

    YEAR:

    2014

    Mon:

    1/27

    Tues:

    1/28

    Wed:

    1/29

    Thur:

    1/30

    Fri:

    1/31

    Math

    Read pages 24-50

    Read Book2 1-24

    Problems on page 51

    Word problem on 25

    English

    Report on Wind in the willows

    Read Catcher in the Rye

    SOCIAL STUDIES

    Current news report

    Science

    Lab 3-5

    Lab 3-5

    Lab 3-5

    Geography

    French

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-03.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-03.html index f3cdd108..b0cfe6a1 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-03.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-03.html @@ -1,335 +1,393 @@ -

    Sample Photo

    January 2014

    Sun.

    Mon.

    Tue.

    Wed.

    Thu.

    Fri.

    Sat.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    Little League

    Jane’s Birthday

    12

    13

    14

    15

    16

    17

    18

    Parent Teacher Conference

    19

    20

    21

    22

    23

    24

    25

    Run for Life 5K

    26

    27

    28

    29

    30

    31

    notes

    To replace the picture with your own, right-click it and then click Change Picture.

    To select new dates or change the calendar color, click the Calendar tab on the ribbon.

    To try out a different set of fonts or colors, on the Calendar tab or Design tab, click Fonts or Colors.

    \ No newline at end of file + + + + + + + + + +

    Sample Photo

    January 2014

    Sun.

    Mon.

    Tue.

    Wed.

    Thu.

    Fri.

    Sat.

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    Little League

    Jane’s Birthday

    12

    13

    14

    15

    16

    17

    18

    Parent Teacher Conference

    19

    20

    21

    22

    23

    24

    25

    Run for Life 5K

    26

    27

    28

    29

    30

    31

    notes

    To replace the picture with your own, right-click it and then click Change Picture.

    To select new dates or change the calendar color, click the Calendar tab on the ribbon.

    To try out a different set of fonts or colors, on the Calendar tab or Design tab, click Fonts or Colors.

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-04.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-04.html index d58a692f..5364a5de 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-04.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-04.html @@ -1,215 +1,259 @@ -C:\Users\Eric\Documents\Open-Xml-PowerTools\OpenXmlPowerToolsExamples\HtmlConverter01\Test-04.docx

    Team Operating Agreement

    Purpose of the Team Operating Agreement (TOA)

    [Describe the purpose of the document. Depending on the nature of the project or culture of the company, other sections may be added to the document.]

    Sample text:

    This TOA serves as the guidelines and ground rules to help the project team work most productively together over the course of the project. The TOA is a living document and may be updated as the need arises throughout the project. Any updates will be discussed with and ratified by the project team members.

    Team Communications

    [Describe how project team members will communicate with each other. Include where project documents will be stored and how they may be accessed, how and when meeting agendas and minutes will be distributed, and how confidential information will be handled.]

    Sample text:

    The project’s SharePoint site will house the most up-to-date version of project documents. All team members will be given access.

    Meeting agendas will be e-mailed to project team members at least 24 hours prior to meetings. Meeting minutes will be posted to the project SharePoint site within 48 hours after meetings.

    Team members will appreciate the sensitive nature of information discussed during this project and will share with care. Where applicable, documents will include a footer indicating that information is confidential.

    “Sidebar” conversations between team members during team meetings will not be allowed.

    All communication will be open and courteous. No “overtalking” or interrupting.

    Team members will keep each other informed.

    Decision Making

    [Describe how project team members will make decisions. Everyone must agree on how decisions will be made to ensure that everyone can live with the decisions made and to ensure that the project can move forward. Include guidelines for voting on decisions, how decisions will be documented, definitions of key terms, and what happens if the team cannot come to a decision (for example, escalation to the project sponsor or to a governing body).]

    Sample text:

    1.Consensus means that everyone can live with the decision. It doesn’t mean everyone has to agree 100%.

    2.The team will use thumbs up/thumbs down voting to make decisions quickly and move on.

    Thumbs up = agree with no further discussion.

    Thumbs sideways = agree, but have further questions. (Questions will be asked and answered immediately after the vote.)

    Thumbs down = cannot agree to the solution proposed. (Be prepared to answer the question: What would it take for you to go to thumbs sideways or thumbs up?)
    Anyone on the team may call for a vote at any time.

    3.Members may abstain from voting.

    4.No decision is made if there are any thumbs-down votes.

    5.Meeting minutes will document the decisions made. If you have questions after reviewing the minutes, contact the project manager and determine the course of action, such as to bring questions to the team for discussion again.

    Meetings

    [So much project work and decision-making happens during meetings that it is important to establish how project team meetings will work. Address what will happen at meetings (generally). Establish who will be responsible for the facilitating, frequency, and scheduling of meetings, and attendance expectations.]

    Sample text:

    Project subteam leaders will report status at each team meeting.

    Project team members will meet [frequency, date, time, and locations of meetings]. During each meeting, a “parking lot” will be used to record topics that require discussion at a later date.

    Issues, risks, change requests, and action items will be reviewed and updated at each meeting.

    The project manager will be responsible for facilitating and keeping meetings on track. Team members will accept the project manager’s decision to table or “park” a discussion topic.

    Meetings will start and end on time. Team members will attend meetings in person when feasible. A dial-in number will be available for remote attendance.

    Sending “stand ins” to meetings will not be allowed unless approved by the project manager prior to meetings.

    It is the responsibility of each team member to stay current on the project team activities, even when he or she has missed a meeting.

    Personal Courtesies

    [Outline the personal courtesies that team members will extend to one another. The contents of this section depend largely on the culture of your company. Do not assume that personal behaviors are understood.]

    Sample text:

    Each team member represents a specific area of expertise or business unit. Team members will bring their individual perspectives to the team and will also consider what is best for the company.

    All cell phones and other communication devices must be silenced during meetings and used on an exception basis only.

    Reviewed and approved by:

    ____________________________________________________________________

    Project Manager Date:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    \ No newline at end of file + + + + + C:\Users\Eric\Documents\Open-Xml-PowerTools\OpenXmlPowerToolsExamples\HtmlConverter01\Test-04.docx + + + + +

    Team Operating Agreement

    Purpose of the Team Operating Agreement (TOA)

    [Describe the purpose of the document. Depending on the nature of the project or culture of the company, other sections may be added to the document.]

    Sample text:

    This TOA serves as the guidelines and ground rules to help the project team work most productively together over the course of the project. The TOA is a living document and may be updated as the need arises throughout the project. Any updates will be discussed with and ratified by the project team members.

    Team Communications

    [Describe how project team members will communicate with each other. Include where project documents will be stored and how they may be accessed, how and when meeting agendas and minutes will be distributed, and how confidential information will be handled.]

    Sample text:

    The project’s SharePoint site will house the most up-to-date version of project documents. All team members will be given access.

    Meeting agendas will be e-mailed to project team members at least 24 hours prior to meetings. Meeting minutes will be posted to the project SharePoint site within 48 hours after meetings.

    Team members will appreciate the sensitive nature of information discussed during this project and will share with care. Where applicable, documents will include a footer indicating that information is confidential.

    “Sidebar” conversations between team members during team meetings will not be allowed.

    All communication will be open and courteous. No “overtalking” or interrupting.

    Team members will keep each other informed.

    Decision Making

    [Describe how project team members will make decisions. Everyone must agree on how decisions will be made to ensure that everyone can live with the decisions made and to ensure that the project can move forward. Include guidelines for voting on decisions, how decisions will be documented, definitions of key terms, and what happens if the team cannot come to a decision (for example, escalation to the project sponsor or to a governing body).]

    Sample text:

    1.Consensus means that everyone can live with the decision. It doesn’t mean everyone has to agree 100%.

    2.The team will use thumbs up/thumbs down voting to make decisions quickly and move on.

    Thumbs up = agree with no further discussion.

    Thumbs sideways = agree, but have further questions. (Questions will be asked and answered immediately after the vote.)

    Thumbs down = cannot agree to the solution proposed. (Be prepared to answer the question: What would it take for you to go to thumbs sideways or thumbs up?)
    Anyone on the team may call for a vote at any time.

    3.Members may abstain from voting.

    4.No decision is made if there are any thumbs-down votes.

    5.Meeting minutes will document the decisions made. If you have questions after reviewing the minutes, contact the project manager and determine the course of action, such as to bring questions to the team for discussion again.

    Meetings

    [So much project work and decision-making happens during meetings that it is important to establish how project team meetings will work. Address what will happen at meetings (generally). Establish who will be responsible for the facilitating, frequency, and scheduling of meetings, and attendance expectations.]

    Sample text:

    Project subteam leaders will report status at each team meeting.

    Project team members will meet [frequency, date, time, and locations of meetings]. During each meeting, a “parking lot” will be used to record topics that require discussion at a later date.

    Issues, risks, change requests, and action items will be reviewed and updated at each meeting.

    The project manager will be responsible for facilitating and keeping meetings on track. Team members will accept the project manager’s decision to table or “park” a discussion topic.

    Meetings will start and end on time. Team members will attend meetings in person when feasible. A dial-in number will be available for remote attendance.

    Sending “stand ins” to meetings will not be allowed unless approved by the project manager prior to meetings.

    It is the responsibility of each team member to stay current on the project team activities, even when he or she has missed a meeting.

    Personal Courtesies

    [Outline the personal courtesies that team members will extend to one another. The contents of this section depend largely on the culture of your company. Do not assume that personal behaviors are understood.]

    Sample text:

    Each team member represents a specific area of expertise or business unit. Team members will bring their individual perspectives to the team and will also consider what is best for the company.

    All cell phones and other communication devices must be silenced during meetings and used on an exception basis only.

    Reviewed and approved by:

    ____________________________________________________________________

    Project Manager Date:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    ____________________________________________________________________

    Project Team Member NameDate:

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-05.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-05.html index 020daabe..b7066b04 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-05.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-05.html @@ -1,263 +1,313 @@ -C:\Users\Eric\Documents\Open-Xml-PowerTools\OpenXmlPowerToolsExamples\HtmlConverter01\Test-05.docx

    Diane White

    123 Main St, Seattle, WA 98112

    206-555-1212

    janice@adventureworks.com

    “...exceptionally energetic and enthusiastic teacher...projects a charisma that captures the imagination of students...demonstrated excellent classroom management skill...”

    Simon Pearson
    former administrator

    “...business background in technology was supportive to the use of videos and computers in the class...She volunteered for cooperative opportunities in the media center and helped teachers to accommodate computers...I recommend her with the highest regard...”

    Aidan Delaney
    2nd Grade Teacher
    New York City Schools

    “...deeply involved in learning about the educational state-of-the-art, investigating research and designing instructional materials...I look forward to the time when Diane will bring her love of children, enthusiasm, initiative, and intelligence into her own classroom.”

    Monica Brink, Ed.D.

    “My ability to motivate students and share a love of learning fosters a successful classroom environment. ...I would welcome becoming part of ‘the village that raises the child’ in your district.”

    Jenny Lysaker

    Professional Profile

    Eager to bring elementary students into the twenty-first century using a unique combination of education experience coupled with ten years’ business background in computer systems management.

    Hold Masters Degree in Elementary Education and Bachelors Degree in Computer Science.

    Experienced in use of the Internet and educational software.

    Dedicated to enthusiastic and dynamic teaching as a means of creating and nurturing a lifelong love of knowledge in children.

    Education, Honors, and Certifications

    M.S. Elementary Education

    Elm College, Flushing, NY. 1995

    Bachelor of Science Computer Science

    Fir Tree University, Hempstead, NY. 1984

    Kappa Delta Pi Honor Society Member

    Provisional Certifications

    NY State Elementary Education. 1995

    NY State Business Education. 1995

    Key Qualifications

    Certified in Elementary (K-6) and Business Education

    Plan and instruct each subject area using wide variety of teaching aids, motivational and implementation strategies to engage students in active learning.

    Incorporate learning modality principles into classroom and individual instruction. Develop and conduct inter-grade activities. Utilize Heath automated math management system.

    Implement technological approaches to subject material. Research educational resources on the Internet. Assist with information retrieval.

    Experienced Computer Educator

    Designed and conducted various faculty and student workshops for training in word processing and spreadsheet software. Instructed corporate personnel in use of word processing, desktop publishing, and drafting programs for conversion from manual typesetting and drafting to computer assisted methods.

    Computer Skills

    Software (IBM and MAC environments): Microsoft Windows® and DOS, WordPerfect, Lotus123, Microsoft Word, PageMaker, AutoCAD, Books in Print, Baker & Taylor Links, Bibbase

    Working knowledge of the Internet

    System installations and debugging; terminal/printer operations

    Employment

    Professional Development in Education

    Substitute Teacher, K thru High School, April 1995 to present

    Graduate Advisor, Education Dept., October 1995 to present

    Elm College, Flushing, NY

    Workshop Presenter, November 1995

    First combined International Reading Association Regional Conference, Nashville, TN

    Information Services Assistant, May 1994 to August 1995

    Elm College, Flushing, NY

    Student Teacher, September to December 1994

    Fir Tree Elementary, Flushing, NY

    Computer Related Training Positions

    Workshop Presenter, February, 1995

    Maple High School, East Islip, NY

    Graduate Assistant, August 1993 to May 1994

    Elm College, Flushing, NY

    Software Engineer, 1989 to 1991

    Trey Research, Smithtown, NY

    Corporate Computer Systems Management

    Systems Manager, 1987 to 1989

    A. Datum Corporation, Bohemia, NY

    Software Quality Assurance Engineer, 1986 to 1987

    Fabrikam, Inc., Smithtown, NY

    Staff Administrator, Executive Department, 1984 to 1986

    The Telephone Company, Brooklyn, NY

    Student Director/Assistant, Computer Science Lab, 1981 to 1984

    Fir Tree University, Hempstead, NY

    Professional Affiliations

    International Reading Association

    Association for Supervision and Curriculum Development

    \ No newline at end of file + + + + + C:\Users\Eric\Documents\Open-Xml-PowerTools\OpenXmlPowerToolsExamples\HtmlConverter01\Test-05.docx + + + + +

    Diane White

    123 Main St, Seattle, WA 98112

    206-555-1212

    janice@adventureworks.com

    “...exceptionally energetic and enthusiastic teacher...projects a charisma that captures the imagination of students...demonstrated excellent classroom management skill...”

    Simon Pearson
    former administrator

    “...business background in technology was supportive to the use of videos and computers in the class...She volunteered for cooperative opportunities in the media center and helped teachers to accommodate computers...I recommend her with the highest regard...”

    Aidan Delaney
    2nd Grade Teacher
    New York City Schools

    “...deeply involved in learning about the educational state-of-the-art, investigating research and designing instructional materials...I look forward to the time when Diane will bring her love of children, enthusiasm, initiative, and intelligence into her own classroom.”

    Monica Brink, Ed.D.

    “My ability to motivate students and share a love of learning fosters a successful classroom environment. ...I would welcome becoming part of ‘the village that raises the child’ in your district.”

    Jenny Lysaker

    Professional Profile

    Eager to bring elementary students into the twenty-first century using a unique combination of education experience coupled with ten years’ business background in computer systems management.

    Hold Masters Degree in Elementary Education and Bachelors Degree in Computer Science.

    Experienced in use of the Internet and educational software.

    Dedicated to enthusiastic and dynamic teaching as a means of creating and nurturing a lifelong love of knowledge in children.

    Education, Honors, and Certifications

    M.S. Elementary Education

    Elm College, Flushing, NY. 1995

    Bachelor of Science Computer Science

    Fir Tree University, Hempstead, NY. 1984

    Kappa Delta Pi Honor Society Member

    Provisional Certifications

    NY State Elementary Education. 1995

    NY State Business Education. 1995

    Key Qualifications

    Certified in Elementary (K-6) and Business Education

    Plan and instruct each subject area using wide variety of teaching aids, motivational and implementation strategies to engage students in active learning.

    Incorporate learning modality principles into classroom and individual instruction. Develop and conduct inter-grade activities. Utilize Heath automated math management system.

    Implement technological approaches to subject material. Research educational resources on the Internet. Assist with information retrieval.

    Experienced Computer Educator

    Designed and conducted various faculty and student workshops for training in word processing and spreadsheet software. Instructed corporate personnel in use of word processing, desktop publishing, and drafting programs for conversion from manual typesetting and drafting to computer assisted methods.

    Computer Skills

    Software (IBM and MAC environments): Microsoft Windows® and DOS, WordPerfect, Lotus123, Microsoft Word, PageMaker, AutoCAD, Books in Print, Baker & Taylor Links, Bibbase

    Working knowledge of the Internet

    System installations and debugging; terminal/printer operations

    Employment

    Professional Development in Education

    Substitute Teacher, K thru High School, April 1995 to present

    Graduate Advisor, Education Dept., October 1995 to present

    Elm College, Flushing, NY

    Workshop Presenter, November 1995

    First combined International Reading Association Regional Conference, Nashville, TN

    Information Services Assistant, May 1994 to August 1995

    Elm College, Flushing, NY

    Student Teacher, September to December 1994

    Fir Tree Elementary, Flushing, NY

    Computer Related Training Positions

    Workshop Presenter, February, 1995

    Maple High School, East Islip, NY

    Graduate Assistant, August 1993 to May 1994

    Elm College, Flushing, NY

    Software Engineer, 1989 to 1991

    Trey Research, Smithtown, NY

    Corporate Computer Systems Management

    Systems Manager, 1987 to 1989

    A. Datum Corporation, Bohemia, NY

    Software Quality Assurance Engineer, 1986 to 1987

    Fabrikam, Inc., Smithtown, NY

    Staff Administrator, Executive Department, 1984 to 1986

    The Telephone Company, Brooklyn, NY

    Student Director/Assistant, Computer Science Lab, 1981 to 1984

    Fir Tree University, Hempstead, NY

    Professional Affiliations

    International Reading Association

    Association for Supervision and Curriculum Development

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-06.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-06.html index be03f635..61e5e4f1 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-06.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-06.html @@ -1,329 +1,381 @@ -

    Month 1

    Month 2

    Month 3

    Month 4

    Month 5

    Month 6

    Month 7

    Month 8

    Starting cash

    $20000.00

    $22000.00

    Cash In:

    Cash Sales Paid

    $2000.00

    $2000.00

    Receivables

    $4000.00

    $4000.00

    Total Cash In

    $6000.00

    $6000.00

    Cash Out:

    Rent

    $1000.00

    $1000.00

    Payroll

    $1000.00

    $1000.00

    Other

    $2000.00

    $2000.00

    Total Cash Out

    $4000.00

    $4000.00

    Ending Balance

    $22000.00

    $24000.00

    Change (cash flow)

    \ No newline at end of file + + + + + + + + + +

    Month 1

    Month 2

    Month 3

    Month 4

    Month 5

    Month 6

    Month 7

    Month 8

    Starting cash

    $20000.00

    $22000.00

    Cash In:

    Cash Sales Paid

    $2000.00

    $2000.00

    Receivables

    $4000.00

    $4000.00

    Total Cash In

    $6000.00

    $6000.00

    Cash Out:

    Rent

    $1000.00

    $1000.00

    Payroll

    $1000.00

    $1000.00

    Other

    $2000.00

    $2000.00

    Total Cash Out

    $4000.00

    $4000.00

    Ending Balance

    $22000.00

    $24000.00

    Change (cash flow)

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-07.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-07.html index 50f1ede6..3be002a0 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-07.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-07.html @@ -1,166 +1,204 @@ -ארצות הברית – ויקיפדיה

    ‏ארצות הברית‏

    ‏המונח "‏‎USA‎‏" מפנה לכאן. לערך העוסק בערוץ הטלוויזיה, ראו ‏‎USA‎‏ (ערוץ טלוויזיה).‏

    ‏ארצות הברית של אמריקה‏‏ ‏‏(באנגלית: ‏‎United States of America‎‏, או בראשי תיבות: ‏‎USA‎‏, בתרגום מילולי לעברית: "המדינות המאוחדות של אמריקה") היא פדרציה ורפובליקה-חוקתית המורכבת מ-50 מדינות וממחוז פדרלי. המדינה שוכנת ברוּבָּה במרכז יבשת אמריקה הצפונית, שבה נמצאות 49 מהמדינות וכן מחוז קולומביה שבו נמצאת העיר וושינגטון, בירת המדינה. ארצות הברית גובלת בקנדה בצפון ובמקסיקו בדרום. מדינת אלסקה שוכנת בצפון-מערב היבשת, כאשר קנדה נמצאת מזרחית לה ורוסיה נמצאת בצד מערב, מעבר למצר ברינג. המדינה ה- 50 היא הוואי, ארכיפלג השוכן באוקיינוס השקט.‏

    ‏שטחה של ארצות הברית הוא 9.83 מיליון קמ"ר, ואוכלוסייתה, נכון ליולי 2013 מונה כ-317,181,000 מיליון תושבים. ארצות הברית היא אחת מהמדינות המגוונות ביותר מבחינה אתנית, שנוצר כתוצאה מהגירה רחבת היקף ממדינות רבות. כלכלת ארצות הברית היא הכלכלה הלאומית הגדולה ביותר בעולם, והתוצר המקומי הגולמי שלה היה למעלה מ-14 טריליון דולר בשנת 2009.‏

    ‏ארצות הברית נחשבת כיום - לאחר התפרקותה של ברית המועצות - למעצמת העל היחידה בעולם, והיא בעלת חשיבות מכרעת בכלכלה, בביטחון וביחסי החוץ הבינלאומיים.‏

    ‏תוכן עניינים‏

    ‏1.‏‏אטימולוגיה‏

    ‏2.‏‏היסטוריה‏‏ ‏

    ‏2.1.‏‏התקופה הקדם קולוניאלית וראשית הקולוניזציה‏

    ‏2.2.‏‏השגת עצמאות וההתפשטות מערבה‏

    ‏2.3.‏‏מלחמת האזרחים והתפתחות התעשייה‏

    ‏2.4.‏‏מלחמת העולם הראשונה, השפל הגדול ומלחמת העולם השנייה‏

    ‏2.5.‏‏המלחמה הקרה וזכויות האזרח‏

    ‏2.6.‏‏מתום המלחמה הקרה ועד היום‏

    ‏3.‏‏פוליטיקה וממשל‏

    ‏4.‏‏כלכלה‏‏ ‏

    ‏4.1.‏‏הכנסה‏

    ‏5.‏‏שלטון מקומי‏

    ‏6.‏‏גאוגרפיה‏‏ ‏

    ‏6.1.‏‏פני הארץ‏

    ‏6.2.‏‏אקלים‏

    ‏6.3.‏‏נהרות‏

    ‏6.4.‏‏פאונה ופלורה‏

    ‏6.5.‏‏דינוזאורים‏

    ‏7.‏‏דמוגרפיה‏‏ ‏

    ‏7.1.‏‏דתות‏‏ ‏

    ‏7.1.1.‏‏יהדות ארצות הברית לפי מדינות‏

    ‏8.‏‏ערים‏

    ‏9.‏‏חינוך‏

    ‏10.‏‏צבא וביטחון‏

    ‏11.‏‏תרבות‏‏ ‏

    ‏11.1.‏‏ספרות, פילוסופיה ואמנות‏

    ‏11.2.‏‏קולינריה‏

    ‏11.3.‏‏ספורט‏

    ‏12.‏‏ראו גם‏

    ‏13.‏‏קישורים חיצוניים‏

    ‏14.‏‏הערות שוליים‏

    \ No newline at end of file + + + + + ארצות הברית – ויקיפדיה + + + + +

    ‏ארצות הברית‏

    ‏המונח "‏‎USA‎‏" מפנה לכאן. לערך העוסק בערוץ הטלוויזיה, ראו ‏‎USA‎‏ (ערוץ טלוויזיה).‏

    ‏ארצות הברית של אמריקה‏‏ ‏‏(באנגלית: ‏‎United States of America‎‏, או בראשי תיבות: ‏‎USA‎‏, בתרגום מילולי לעברית: "המדינות המאוחדות של אמריקה") היא פדרציה ורפובליקה-חוקתית המורכבת מ-50 מדינות וממחוז פדרלי. המדינה שוכנת ברוּבָּה במרכז יבשת אמריקה הצפונית, שבה נמצאות 49 מהמדינות וכן מחוז קולומביה שבו נמצאת העיר וושינגטון, בירת המדינה. ארצות הברית גובלת בקנדה בצפון ובמקסיקו בדרום. מדינת אלסקה שוכנת בצפון-מערב היבשת, כאשר קנדה נמצאת מזרחית לה ורוסיה נמצאת בצד מערב, מעבר למצר ברינג. המדינה ה- 50 היא הוואי, ארכיפלג השוכן באוקיינוס השקט.‏

    ‏שטחה של ארצות הברית הוא 9.83 מיליון קמ"ר, ואוכלוסייתה, נכון ליולי 2013 מונה כ-317,181,000 מיליון תושבים. ארצות הברית היא אחת מהמדינות המגוונות ביותר מבחינה אתנית, שנוצר כתוצאה מהגירה רחבת היקף ממדינות רבות. כלכלת ארצות הברית היא הכלכלה הלאומית הגדולה ביותר בעולם, והתוצר המקומי הגולמי שלה היה למעלה מ-14 טריליון דולר בשנת 2009.‏

    ‏ארצות הברית נחשבת כיום - לאחר התפרקותה של ברית המועצות - למעצמת העל היחידה בעולם, והיא בעלת חשיבות מכרעת בכלכלה, בביטחון וביחסי החוץ הבינלאומיים.‏

    ‏תוכן עניינים‏

    ‏1.‏‏אטימולוגיה‏

    ‏2.‏‏היסטוריה‏‏ ‏

    ‏2.1.‏‏התקופה הקדם קולוניאלית וראשית הקולוניזציה‏

    ‏2.2.‏‏השגת עצמאות וההתפשטות מערבה‏

    ‏2.3.‏‏מלחמת האזרחים והתפתחות התעשייה‏

    ‏2.4.‏‏מלחמת העולם הראשונה, השפל הגדול ומלחמת העולם השנייה‏

    ‏2.5.‏‏המלחמה הקרה וזכויות האזרח‏

    ‏2.6.‏‏מתום המלחמה הקרה ועד היום‏

    ‏3.‏‏פוליטיקה וממשל‏

    ‏4.‏‏כלכלה‏‏ ‏

    ‏4.1.‏‏הכנסה‏

    ‏5.‏‏שלטון מקומי‏

    ‏6.‏‏גאוגרפיה‏‏ ‏

    ‏6.1.‏‏פני הארץ‏

    ‏6.2.‏‏אקלים‏

    ‏6.3.‏‏נהרות‏

    ‏6.4.‏‏פאונה ופלורה‏

    ‏6.5.‏‏דינוזאורים‏

    ‏7.‏‏דמוגרפיה‏‏ ‏

    ‏7.1.‏‏דתות‏‏ ‏

    ‏7.1.1.‏‏יהדות ארצות הברית לפי מדינות‏

    ‏8.‏‏ערים‏

    ‏9.‏‏חינוך‏

    ‏10.‏‏צבא וביטחון‏

    ‏11.‏‏תרבות‏‏ ‏

    ‏11.1.‏‏ספרות, פילוסופיה ואמנות‏

    ‏11.2.‏‏קולינריה‏

    ‏11.3.‏‏ספורט‏

    ‏12.‏‏ראו גם‏

    ‏13.‏‏קישורים חיצוניים‏

    ‏14.‏‏הערות שוליים‏

    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-08.html b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-08.html index 49c5f868..2b5d18ee 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-08.html +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter01/Test-08.html @@ -1,2 +1,22 @@ -
    \ No newline at end of file + + + + + + + + + +
    + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.cs b/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.cs index 22695ff4..49ba7e61 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.cs +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.cs @@ -1,45 +1,38 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using DocumentFormat.OpenXml.Packaging; using System; -using System.Collections.Generic; -using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using OpenXmlPowerTools.HtmlToWml; -namespace OpenXmlPowerTools +namespace HtmlToWmlConverter02 { - class Program + internal class Program { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); - FileInfo templateDoc = new FileInfo("../../TemplateDocument.docx"); - FileInfo dataFile = new FileInfo(Path.Combine(tempDi.FullName, "Data.xml")); + var templateDoc = new FileInfo("../../TemplateDocument.docx"); + var dataFile = new FileInfo(Path.Combine(tempDi.FullName, "Data.xml")); // The following method generates a large data file with random data. // In a real world scenario, this is where you would query your data source and produce XML that will drive your document generation process. - int numberOfDocumentsToGenerate = 100; - XElement data = GenerateDataFromDataSource(dataFile, numberOfDocumentsToGenerate); + var numberOfDocumentsToGenerate = 100; + var data = GenerateDataFromDataSource(dataFile, numberOfDocumentsToGenerate); - WmlDocument wmlDoc = new WmlDocument(templateDoc.FullName); - int count = 1; + var wmlDoc = new WmlDocument(templateDoc.FullName); + var count = 1; foreach (var customer in data.Elements("Customer")) { - FileInfo assembledDoc = new FileInfo(Path.Combine(tempDi.FullName, string.Format("Letter-{0:0000}.docx", count++))); + var assembledDoc = new FileInfo(Path.Combine(tempDi.FullName, string.Format("Letter-{0:0000}.docx", count++))); Console.WriteLine("Generating {0}", assembledDoc.Name); - bool templateError; - WmlDocument wmlAssembledDoc = DocumentAssembler.AssembleDocument(wmlDoc, customer, out templateError); + var wmlAssembledDoc = DocumentAssembler.AssembleDocument(wmlDoc, customer, out var templateError); if (templateError) { Console.WriteLine("Errors in template."); @@ -55,7 +48,7 @@ static void Main(string[] args) } } - private static string[] s_productNames = new[] { + private static readonly string[] s_productNames = new[] { "Unicycle", "Bicycle", "Tricycle", @@ -67,8 +60,8 @@ static void Main(string[] args) private static XElement GenerateDataFromDataSource(FileInfo dataFi, int numberOfDocumentsToGenerate) { var customers = new XElement("Customers"); - Random r = new Random(); - for (int i = 0; i < numberOfDocumentsToGenerate; ++i) + var r = new Random(); + for (var i = 0; i < numberOfDocumentsToGenerate; ++i) { var customer = new XElement("Customer", new XElement("CustomerID", i + 1), @@ -76,8 +69,8 @@ private static XElement GenerateDataFromDataSource(FileInfo dataFi, int numberOf new XElement("HighValueCustomer", r.Next(2) == 0 ? "True" : "False"), new XElement("Orders")); var orders = customer.Element("Orders"); - int numberOfOrders = r.Next(10) + 1; - for (int j = 0; j < numberOfOrders; j++) + var numberOfOrders = r.Next(10) + 1; + for (var j = 0; j < numberOfOrders; j++) { var order = new XElement("Order", new XAttribute("Number", j + 1), @@ -95,119 +88,53 @@ private static XElement GenerateDataFromDataSource(FileInfo dataFi, int numberOf public static FileInfo ConvertToHtml(string file, string outputDirectory) { var fi = new FileInfo(file); - byte[] byteArray = File.ReadAllBytes(fi.FullName); - using (MemoryStream memoryStream = new MemoryStream()) + var byteArray = File.ReadAllBytes(fi.FullName); + using var memoryStream = new MemoryStream(); + memoryStream.Write(byteArray, 0, byteArray.Length); + using var wDoc = WordprocessingDocument.Open(memoryStream, true); + var destFileName = new FileInfo(fi.Name.Replace(".docx", ".html")); + if (outputDirectory != null && outputDirectory != string.Empty) { - memoryStream.Write(byteArray, 0, byteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) + var di = new DirectoryInfo(outputDirectory); + if (!di.Exists) { - var destFileName = new FileInfo(fi.Name.Replace(".docx", ".html")); - if (outputDirectory != null && outputDirectory != string.Empty) - { - DirectoryInfo di = new DirectoryInfo(outputDirectory); - if (!di.Exists) - { - throw new OpenXmlPowerToolsException("Output directory does not exist"); - } - destFileName = new FileInfo(Path.Combine(di.FullName, destFileName.Name)); - } - var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - int imageCounter = 0; - - var pageTitle = fi.FullName; - var part = wDoc.CoreFilePropertiesPart; - if (part != null) - { - pageTitle = (string)part.GetXDocument().Descendants(DC.title).FirstOrDefault() ?? fi.FullName; - } - - // TODO: Determine max-width from size of content area. - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings() - { - AdditionalCss = "body { margin: 1cm auto; max-width: 20cm; padding: 0; }", - PageTitle = pageTitle, - FabricateCssClasses = true, - CssClassPrefix = "pt-", - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - ImageHandler = imageInfo => - { - DirectoryInfo localDirInfo = new DirectoryInfo(imageDirectoryName); - if (!localDirInfo.Exists) - localDirInfo.Create(); - ++imageCounter; - string extension = imageInfo.ContentType.Split('/')[1].ToLower(); - ImageFormat imageFormat = null; - if (extension == "png") - imageFormat = ImageFormat.Png; - else if (extension == "gif") - imageFormat = ImageFormat.Gif; - else if (extension == "bmp") - imageFormat = ImageFormat.Bmp; - else if (extension == "jpeg") - imageFormat = ImageFormat.Jpeg; - else if (extension == "tiff") - { - // Convert tiff to gif. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "x-wmf") - { - extension = "wmf"; - imageFormat = ImageFormat.Wmf; - } - - // If the image format isn't one that we expect, ignore it, - // and don't return markup for the link. - if (imageFormat == null) - return null; + throw new OpenXmlPowerToolsException("Output directory does not exist"); + } + destFileName = new FileInfo(Path.Combine(di.FullName, destFileName.Name)); + } + var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - string imageFileName = imageDirectoryName + "/image" + - imageCounter.ToString() + "." + extension; - try - { - imageInfo.Bitmap.Save(imageFileName, imageFormat); - } - catch (System.Runtime.InteropServices.ExternalException) - { - return null; - } - string imageSource = localDirInfo.Name + "/image" + - imageCounter.ToString() + "." + extension; + var pageTitle = fi.FullName; + var part = wDoc.CoreFilePropertiesPart; + if (part != null) + { + pageTitle = (string)part.GetXDocument().Descendants(DC.title).FirstOrDefault() ?? fi.FullName; + } - XElement img = new XElement(Xhtml.img, - new XAttribute(NoNamespace.src, imageSource), - imageInfo.ImgStyleAttribute, - imageInfo.AltText != null ? - new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); - return img; - } - }; - XElement htmlElement = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); + // TODO: Determine max-width from size of content area. + var settings = new WmlToHtmlConverterSettings(pageTitle); + var htmlElement = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); - // Produce HTML document with declaration to tell the browser - // we are using HTML5. - var html = new XDocument( - new XDocumentType("html", null, null, null), - htmlElement); + // Produce HTML document with declaration to tell the browser + // we are using HTML5. + var html = new XDocument( + new XDocumentType("html", null, null, null), + htmlElement); - // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type - // XEntity. PtOpenXmlUtil.cs define the XEntity class. See - // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx - // for detailed explanation. - // - // If you further transform the XML tree returned by ConvertToHtmlTransform, you - // must do it correctly, or entities will not be serialized properly. + // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type + // XEntity. PtOpenXmlUtil.cs define the XEntity class. See + // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx + // for detailed explanation. + // + // If you further transform the XML tree returned by ConvertToHtmlTransform, you + // must do it correctly, or entities will not be serialized properly. - var htmlString = html.ToString(SaveOptions.DisableFormatting); - File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); + var htmlString = html.ToString(SaveOptions.DisableFormatting); + File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); - return destFileName; - } - } + return destFileName; } - + private static void ConvertToDocx(string file, string destinationDir) { var sourceHtmlFi = new FileInfo(file); @@ -215,16 +142,16 @@ private static void ConvertToDocx(string file, string destinationDir) var destDocxFi = new FileInfo(Path.Combine(destinationDir, sourceHtmlFi.Name.Replace(".html", "-ConvertedByHtmlToWml.docx"))); - XElement html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceHtmlFi); + var html = HtmlToWmlReadAsXElement.ReadAsXElement(sourceHtmlFi); - string usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); + var usedAuthorCss = HtmlToWmlConverter.CleanUpCss((string)html.Descendants().FirstOrDefault(d => d.Name.LocalName.ToLower() == "style")); - HtmlToWmlConverterSettings settings = HtmlToWmlConverter.GetDefaultSettings(); + var settings = HtmlToWmlConverter.GetDefaultSettings(); // image references in HTML files contain the path to the subdir that contains the images, so base URI is the name of the directory // that contains the HTML files settings.BaseUriForImages = sourceHtmlFi.DirectoryName; - WmlDocument doc = HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings); + var doc = HtmlToWmlConverter.ConvertHtmlToWml(defaultCss, usedAuthorCss, userCss, html, settings); doc.SaveAs(destDocxFi.FullName); } @@ -232,7 +159,7 @@ public class HtmlToWmlReadAsXElement { public static XElement ReadAsXElement(FileInfo sourceHtmlFi) { - string htmlString = File.ReadAllText(sourceHtmlFi.FullName); + var htmlString = File.ReadAllText(sourceHtmlFi.FullName); XElement html = null; try { @@ -260,9 +187,9 @@ public static XElement ReadAsXElement(FileInfo sourceHtmlFi) html = XElement.Parse(sb.ToString()); } #else - catch (XmlException e) + catch (XmlException) { - throw e; + throw; } #endif // HtmlToWmlConverter expects the HTML elements to be in no namespace, so convert all elements to no namespace. @@ -272,8 +199,7 @@ public static XElement ReadAsXElement(FileInfo sourceHtmlFi) private static object ConvertToNoNamespace(XNode node) { - XElement element = node as XElement; - if (element != null) + if (node is XElement element) { return new XElement(element.Name.LocalName, element.Attributes().Where(a => !a.IsNamespaceDeclaration), @@ -283,7 +209,7 @@ private static object ConvertToNoNamespace(XNode node) } } - static string defaultCss = + private static readonly string defaultCss = @"html, address, blockquote, body, dd, div, @@ -357,6 +283,6 @@ private static object ConvertToNoNamespace(XNode node) "; - static string userCss = @""; + private static readonly string userCss = @""; } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.csproj b/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.csproj +++ b/OpenXmlPowerToolsExamples/HtmlToWmlConverter02/HtmlToWmlConverter02.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.cs b/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.cs index 60455e23..5b366358 100644 --- a/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.cs +++ b/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.cs @@ -1,27 +1,12 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2014. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license -can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -***************************************************************************/ - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -class ListItemRetriever01 +internal class ListItemRetriever01 { private class XmlStackItem { @@ -29,54 +14,52 @@ private class XmlStackItem public int[] LevelNumbers; } - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); - using (WordprocessingDocument wDoc = - WordprocessingDocument.Open("../../NumberedListTest.docx", false)) - { - int abstractNumId = 0; - XElement xml = ConvertDocToXml(wDoc, abstractNumId); - Console.WriteLine(xml); - xml.Save(Path.Combine(tempDi.FullName, "Out.xml")); - } + using var wDoc = + WordprocessingDocument.Open("../../NumberedListTest.docx", false); + var abstractNumId = 0; + var xml = ConvertDocToXml(wDoc, abstractNumId); + Console.WriteLine(xml); + xml.Save(Path.Combine(tempDi.FullName, "Out.xml")); } private static XElement ConvertDocToXml(WordprocessingDocument wDoc, int abstractNumId) { - XDocument xd = wDoc.MainDocumentPart.GetXDocument(); + var xd = wDoc.MainDocumentPart.GetXDocument(); // First, call RetrieveListItem so that all paragraphs are initialized with ListItemInfo var firstParagraph = xd.Descendants(W.p).FirstOrDefault(); var listItem = ListItemRetriever.RetrieveListItem(wDoc, firstParagraph); - XElement xml = new XElement("Root"); + var xml = new XElement("Root"); var current = new Stack(); current.Push( new XmlStackItem() { Element = xml, - LevelNumbers = new int[] { }, + LevelNumbers = Array.Empty(), }); foreach (var paragraph in xd.Descendants(W.p)) { // The following does not take into account documents that have tracked revisions. // As necessary, call RevisionAccepter.AcceptRevisions before converting to XML. var text = paragraph.Descendants(W.t).Select(t => (string)t).StringConcatenate(); - ListItemRetriever.ListItemInfo lii = + var lii = paragraph.Annotation(); if (lii.IsListItem && lii.AbstractNumId == abstractNumId) { - ListItemRetriever.LevelNumbers levelNums = + var levelNums = paragraph.Annotation(); if (levelNums.LevelNumbersArray.Length == current.Peek().LevelNumbers.Length) { current.Pop(); var levelNumsForThisIndent = levelNums.LevelNumbersArray; - string levelText = levelNums + var levelText = levelNums .LevelNumbersArray .Select(l => l.ToString() + ".") .StringConcatenate() @@ -94,15 +77,15 @@ private static XElement ConvertDocToXml(WordprocessingDocument wDoc, int abstrac } else if (levelNums.LevelNumbersArray.Length > current.Peek().LevelNumbers.Length) { - for (int i = current.Peek().LevelNumbers.Length; - i < levelNums.LevelNumbersArray.Length; + for (var i = current.Peek().LevelNumbers.Length; + i < levelNums.LevelNumbersArray.Length; i++) { var levelNumsForThisIndent = levelNums .LevelNumbersArray .Take(i + 1) .ToArray(); - string levelText = levelNums + var levelText = levelNums .LevelNumbersArray .Select(l => l.ToString() + ".") .StringConcatenate() @@ -121,13 +104,16 @@ private static XElement ConvertDocToXml(WordprocessingDocument wDoc, int abstrac } else if (levelNums.LevelNumbersArray.Length < current.Peek().LevelNumbers.Length) { - for (int i = current.Peek().LevelNumbers.Length; - i > levelNums.LevelNumbersArray.Length; + for (var i = current.Peek().LevelNumbers.Length; + i > levelNums.LevelNumbersArray.Length; i--) + { current.Pop(); + } + current.Pop(); var levelNumsForThisIndent = levelNums.LevelNumbersArray; - string levelText = levelNums + var levelText = levelNums .LevelNumbersArray .Select(l => l.ToString() + ".") .StringConcatenate() @@ -151,4 +137,4 @@ private static XElement ConvertDocToXml(WordprocessingDocument wDoc, int abstrac } return xml; } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.csproj b/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.csproj +++ b/OpenXmlPowerToolsExamples/ListItemRetriever01/ListItemRetriever01.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.Designer.cs b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.Designer.cs deleted file mode 100644 index 146f5f66..00000000 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.Designer.cs +++ /dev/null @@ -1,244 +0,0 @@ -namespace MarkupSimplifierApp -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.cbRemoveContentControls = new System.Windows.Forms.CheckBox(); - this.cbRemoveSmartTags = new System.Windows.Forms.CheckBox(); - this.cbRemoveRsidInfo = new System.Windows.Forms.CheckBox(); - this.cbRemoveComments = new System.Windows.Forms.CheckBox(); - this.cbRemoveEndAndFootNotes = new System.Windows.Forms.CheckBox(); - this.cbReplaceTabsWithSpaces = new System.Windows.Forms.CheckBox(); - this.cbRemoveFieldCodes = new System.Windows.Forms.CheckBox(); - this.cbRemovePermissions = new System.Windows.Forms.CheckBox(); - this.cbRemoveProof = new System.Windows.Forms.CheckBox(); - this.cbRemoveSoftHyphens = new System.Windows.Forms.CheckBox(); - this.cbRemoveLastRenderedPageBreak = new System.Windows.Forms.CheckBox(); - this.cbRemoveBookmarks = new System.Windows.Forms.CheckBox(); - this.cbRemoveWebHidden = new System.Windows.Forms.CheckBox(); - this.cbNormalize = new System.Windows.Forms.CheckBox(); - this.btnApply = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // cbRemoveContentControls - // - this.cbRemoveContentControls.AutoSize = true; - this.cbRemoveContentControls.Location = new System.Drawing.Point(13, 13); - this.cbRemoveContentControls.Name = "cbRemoveContentControls"; - this.cbRemoveContentControls.Size = new System.Drawing.Size(147, 17); - this.cbRemoveContentControls.TabIndex = 0; - this.cbRemoveContentControls.Text = "Remove Content Controls"; - this.cbRemoveContentControls.UseVisualStyleBackColor = true; - // - // cbRemoveSmartTags - // - this.cbRemoveSmartTags.AutoSize = true; - this.cbRemoveSmartTags.Location = new System.Drawing.Point(13, 37); - this.cbRemoveSmartTags.Name = "cbRemoveSmartTags"; - this.cbRemoveSmartTags.Size = new System.Drawing.Size(123, 17); - this.cbRemoveSmartTags.TabIndex = 1; - this.cbRemoveSmartTags.Text = "Remove Smart Tags"; - this.cbRemoveSmartTags.UseVisualStyleBackColor = true; - // - // cbRemoveRsidInfo - // - this.cbRemoveRsidInfo.AutoSize = true; - this.cbRemoveRsidInfo.Location = new System.Drawing.Point(13, 61); - this.cbRemoveRsidInfo.Name = "cbRemoveRsidInfo"; - this.cbRemoveRsidInfo.Size = new System.Drawing.Size(111, 17); - this.cbRemoveRsidInfo.TabIndex = 2; - this.cbRemoveRsidInfo.Text = "Remove Rsid Info"; - this.cbRemoveRsidInfo.UseVisualStyleBackColor = true; - // - // cbRemoveComments - // - this.cbRemoveComments.AutoSize = true; - this.cbRemoveComments.Location = new System.Drawing.Point(13, 85); - this.cbRemoveComments.Name = "cbRemoveComments"; - this.cbRemoveComments.Size = new System.Drawing.Size(118, 17); - this.cbRemoveComments.TabIndex = 3; - this.cbRemoveComments.Text = "Remove Comments"; - this.cbRemoveComments.UseVisualStyleBackColor = true; - // - // cbRemoveEndAndFootNotes - // - this.cbRemoveEndAndFootNotes.AutoSize = true; - this.cbRemoveEndAndFootNotes.Location = new System.Drawing.Point(13, 109); - this.cbRemoveEndAndFootNotes.Name = "cbRemoveEndAndFootNotes"; - this.cbRemoveEndAndFootNotes.Size = new System.Drawing.Size(164, 17); - this.cbRemoveEndAndFootNotes.TabIndex = 4; - this.cbRemoveEndAndFootNotes.Text = "Remove End and Foot Notes"; - this.cbRemoveEndAndFootNotes.UseVisualStyleBackColor = true; - // - // cbReplaceTabsWithSpaces - // - this.cbReplaceTabsWithSpaces.AutoSize = true; - this.cbReplaceTabsWithSpaces.Location = new System.Drawing.Point(13, 133); - this.cbReplaceTabsWithSpaces.Name = "cbReplaceTabsWithSpaces"; - this.cbReplaceTabsWithSpaces.Size = new System.Drawing.Size(154, 17); - this.cbReplaceTabsWithSpaces.TabIndex = 5; - this.cbReplaceTabsWithSpaces.Text = "Replace Tabs with Spaces"; - this.cbReplaceTabsWithSpaces.UseVisualStyleBackColor = true; - // - // cbRemoveFieldCodes - // - this.cbRemoveFieldCodes.AutoSize = true; - this.cbRemoveFieldCodes.Location = new System.Drawing.Point(13, 157); - this.cbRemoveFieldCodes.Name = "cbRemoveFieldCodes"; - this.cbRemoveFieldCodes.Size = new System.Drawing.Size(124, 17); - this.cbRemoveFieldCodes.TabIndex = 6; - this.cbRemoveFieldCodes.Text = "Remove Field Codes"; - this.cbRemoveFieldCodes.UseVisualStyleBackColor = true; - // - // cbRemovePermissions - // - this.cbRemovePermissions.AutoSize = true; - this.cbRemovePermissions.Location = new System.Drawing.Point(13, 181); - this.cbRemovePermissions.Name = "cbRemovePermissions"; - this.cbRemovePermissions.Size = new System.Drawing.Size(124, 17); - this.cbRemovePermissions.TabIndex = 7; - this.cbRemovePermissions.Text = "Remove Permissions"; - this.cbRemovePermissions.UseVisualStyleBackColor = true; - // - // cbRemoveProof - // - this.cbRemoveProof.AutoSize = true; - this.cbRemoveProof.Location = new System.Drawing.Point(13, 205); - this.cbRemoveProof.Name = "cbRemoveProof"; - this.cbRemoveProof.Size = new System.Drawing.Size(94, 17); - this.cbRemoveProof.TabIndex = 8; - this.cbRemoveProof.Text = "Remove Proof"; - this.cbRemoveProof.UseVisualStyleBackColor = true; - // - // cbRemoveSoftHyphens - // - this.cbRemoveSoftHyphens.AutoSize = true; - this.cbRemoveSoftHyphens.Location = new System.Drawing.Point(13, 229); - this.cbRemoveSoftHyphens.Name = "cbRemoveSoftHyphens"; - this.cbRemoveSoftHyphens.Size = new System.Drawing.Size(133, 17); - this.cbRemoveSoftHyphens.TabIndex = 9; - this.cbRemoveSoftHyphens.Text = "Remove Soft Hyphens"; - this.cbRemoveSoftHyphens.UseVisualStyleBackColor = true; - // - // cbRemoveLastRenderedPageBreak - // - this.cbRemoveLastRenderedPageBreak.AutoSize = true; - this.cbRemoveLastRenderedPageBreak.Location = new System.Drawing.Point(13, 253); - this.cbRemoveLastRenderedPageBreak.Name = "cbRemoveLastRenderedPageBreak"; - this.cbRemoveLastRenderedPageBreak.Size = new System.Drawing.Size(198, 17); - this.cbRemoveLastRenderedPageBreak.TabIndex = 10; - this.cbRemoveLastRenderedPageBreak.Text = "Remove Last Rendered Page Break"; - this.cbRemoveLastRenderedPageBreak.UseVisualStyleBackColor = true; - // - // cbRemoveBookmarks - // - this.cbRemoveBookmarks.AutoSize = true; - this.cbRemoveBookmarks.Location = new System.Drawing.Point(13, 277); - this.cbRemoveBookmarks.Name = "cbRemoveBookmarks"; - this.cbRemoveBookmarks.Size = new System.Drawing.Size(122, 17); - this.cbRemoveBookmarks.TabIndex = 11; - this.cbRemoveBookmarks.Text = "Remove Bookmarks"; - this.cbRemoveBookmarks.UseVisualStyleBackColor = true; - // - // cbRemoveWebHidden - // - this.cbRemoveWebHidden.AutoSize = true; - this.cbRemoveWebHidden.Location = new System.Drawing.Point(13, 301); - this.cbRemoveWebHidden.Name = "cbRemoveWebHidden"; - this.cbRemoveWebHidden.Size = new System.Drawing.Size(129, 17); - this.cbRemoveWebHidden.TabIndex = 12; - this.cbRemoveWebHidden.Text = "Remove Web Hidden"; - this.cbRemoveWebHidden.UseVisualStyleBackColor = true; - // - // cbNormalize - // - this.cbNormalize.AutoSize = true; - this.cbNormalize.Location = new System.Drawing.Point(13, 325); - this.cbNormalize.Name = "cbNormalize"; - this.cbNormalize.Size = new System.Drawing.Size(97, 17); - this.cbNormalize.TabIndex = 13; - this.cbNormalize.Text = "Normalize XML"; - this.cbNormalize.UseVisualStyleBackColor = true; - // - // btnApply - // - this.btnApply.Location = new System.Drawing.Point(256, 301); - this.btnApply.Name = "btnApply"; - this.btnApply.Size = new System.Drawing.Size(103, 41); - this.btnApply.TabIndex = 14; - this.btnApply.Text = "Apply to Documents"; - this.btnApply.UseVisualStyleBackColor = true; - this.btnApply.Click += new System.EventHandler(this.btnApply_Click); - // - // Form1 - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(378, 354); - this.Controls.Add(this.btnApply); - this.Controls.Add(this.cbNormalize); - this.Controls.Add(this.cbRemoveWebHidden); - this.Controls.Add(this.cbRemoveBookmarks); - this.Controls.Add(this.cbRemoveLastRenderedPageBreak); - this.Controls.Add(this.cbRemoveSoftHyphens); - this.Controls.Add(this.cbRemoveProof); - this.Controls.Add(this.cbRemovePermissions); - this.Controls.Add(this.cbRemoveFieldCodes); - this.Controls.Add(this.cbReplaceTabsWithSpaces); - this.Controls.Add(this.cbRemoveEndAndFootNotes); - this.Controls.Add(this.cbRemoveComments); - this.Controls.Add(this.cbRemoveRsidInfo); - this.Controls.Add(this.cbRemoveSmartTags); - this.Controls.Add(this.cbRemoveContentControls); - this.Name = "Form1"; - this.Text = "Form1"; - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.CheckBox cbRemoveContentControls; - private System.Windows.Forms.CheckBox cbRemoveSmartTags; - private System.Windows.Forms.CheckBox cbRemoveRsidInfo; - private System.Windows.Forms.CheckBox cbRemoveComments; - private System.Windows.Forms.CheckBox cbRemoveEndAndFootNotes; - private System.Windows.Forms.CheckBox cbReplaceTabsWithSpaces; - private System.Windows.Forms.CheckBox cbRemoveFieldCodes; - private System.Windows.Forms.CheckBox cbRemovePermissions; - private System.Windows.Forms.CheckBox cbRemoveProof; - private System.Windows.Forms.CheckBox cbRemoveSoftHyphens; - private System.Windows.Forms.CheckBox cbRemoveLastRenderedPageBreak; - private System.Windows.Forms.CheckBox cbRemoveBookmarks; - private System.Windows.Forms.CheckBox cbRemoveWebHidden; - private System.Windows.Forms.CheckBox cbNormalize; - private System.Windows.Forms.Button btnApply; - } -} - diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.cs b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.cs deleted file mode 100644 index 3969f09e..00000000 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Windows.Forms; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; - -namespace MarkupSimplifierApp -{ - public partial class Form1 : Form - { - public Form1() - { - InitializeComponent(); - } - - private void btnApply_Click(object sender, EventArgs e) - { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.Multiselect = true; - DialogResult dr = ofd.ShowDialog(); - foreach (var item in ofd.FileNames) - { - using (WordprocessingDocument doc = - WordprocessingDocument.Open(item, true)) - { - SimplifyMarkupSettings settings = new SimplifyMarkupSettings - { - RemoveContentControls = cbRemoveContentControls.Checked, - RemoveSmartTags = cbRemoveSmartTags.Checked, - RemoveRsidInfo = cbRemoveRsidInfo.Checked, - RemoveComments = cbRemoveComments.Checked, - RemoveEndAndFootNotes = cbRemoveEndAndFootNotes.Checked, - ReplaceTabsWithSpaces = cbReplaceTabsWithSpaces.Checked, - RemoveFieldCodes = cbRemoveFieldCodes.Checked, - RemovePermissions = cbRemovePermissions.Checked, - RemoveProof = cbRemoveProof.Checked, - RemoveSoftHyphens = cbRemoveSoftHyphens.Checked, - RemoveLastRenderedPageBreak = cbRemoveLastRenderedPageBreak.Checked, - RemoveBookmarks = cbRemoveBookmarks.Checked, - RemoveWebHidden = cbRemoveWebHidden.Checked, - NormalizeXml = cbNormalize.Checked, - }; - OpenXmlPowerTools.MarkupSimplifier.SimplifyMarkup(doc, settings); - } - } - } - } -} diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.resx b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.resx deleted file mode 100644 index 1af7de15..00000000 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Form1.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/MarkupSimplifierApp.csproj b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/MarkupSimplifierApp.csproj index 90ae7ebe..ae9472fd 100644 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/MarkupSimplifierApp.csproj +++ b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/MarkupSimplifierApp.csproj @@ -1,25 +1,34 @@ - + + Exe - net461 + net8.0 + net8.0 + 8.0 + true - - + - - + - - Form - - - Form + + True + True + Settings.settings - \ No newline at end of file + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Note-ExampleOutput-files-are-in-bin-debug.txt b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Note-ExampleOutput-files-are-in-bin-debug.txt deleted file mode 100644 index b8881c1c..00000000 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Note-ExampleOutput-files-are-in-bin-debug.txt +++ /dev/null @@ -1,3 +0,0 @@ -Example output files are in a DateTime stamped directory in ./bin/debug. The directory name is ExampleOutput-yy-mm-dd-hhmmss. - -If you are building in release mode, they will, of course, be in ./bin/release. \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Program.cs b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Program.cs index c453fd4f..ad182910 100644 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Program.cs +++ b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Program.cs @@ -1,24 +1,43 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; +using MarkupSimplifierApp.Properties; using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows.Forms; namespace MarkupSimplifierApp { - static class Program + internal class Program { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() + private static void Main(string[] args) { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new Form1()); + if (args.Length == 0) + { + Console.WriteLine("Example output files are in a DateTime stamped directory in ./bin/debug. The directory name is ExampleOutput-yy-mm-dd-hhmmss."); + Console.WriteLine("If you are building in release mode, they will, of course, be in ./bin/release."); + Console.WriteLine("MarkupSimplifierApp.exe 1.docx 2.docx"); + } + + foreach (var item in args) + { + using var doc = WordprocessingDocument.Open(item, true); + var settings = new SimplifyMarkupSettings + { + RemoveContentControls = Settings.Default.RemoveContentControls, + RemoveSmartTags = Settings.Default.RemoveSmartTags, + RemoveRsidInfo = Settings.Default.RemoveRsidInfo, + RemoveComments = Settings.Default.RemoveComments, + RemoveEndAndFootNotes = Settings.Default.RemoveEndAndFootNotes, + ReplaceTabsWithSpaces = Settings.Default.ReplaceTabsWithSpaces, + RemoveFieldCodes = Settings.Default.RemoveFieldCodes, + RemovePermissions = Settings.Default.RemovePermissions, + RemoveProof = Settings.Default.RemoveProof, + RemoveSoftHyphens = Settings.Default.RemoveSoftHyphens, + RemoveLastRenderedPageBreak = Settings.Default.RemoveLastRenderedPageBreak, + RemoveBookmarks = Settings.Default.RemoveBookmarks, + RemoveWebHidden = Settings.Default.RemoveWebHidden, + NormalizeXml = Settings.Default.NormalizeXml, + }; + MarkupSimplifier.SimplifyMarkup(doc, settings); + } } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Resources.Designer.cs b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Resources.Designer.cs deleted file mode 100644 index 5feca88c..00000000 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Resources.Designer.cs +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34209 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace MarkupSimplifierApp.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MarkupSimplifierApp.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Resources.resx b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Resources.resx deleted file mode 100644 index af7dbebb..00000000 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Settings.Designer.cs b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Settings.Designer.cs index a8100ea7..af197296 100644 --- a/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Settings.Designer.cs +++ b/OpenXmlPowerToolsExamples/MarkupSimplifierApp/Properties/Settings.Designer.cs @@ -1,30 +1,194 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18033 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace MarkupSimplifierApp.Properties -{ - - +namespace MarkupSimplifierApp.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveContentControls { + get { + return ((bool)(this["RemoveContentControls"])); + } + set { + this["RemoveContentControls"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveSmartTags { + get { + return ((bool)(this["RemoveSmartTags"])); + } + set { + this["RemoveSmartTags"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveRsidInfo { + get { + return ((bool)(this["RemoveRsidInfo"])); + } + set { + this["RemoveRsidInfo"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveComments { + get { + return ((bool)(this["RemoveComments"])); + } + set { + this["RemoveComments"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveEndAndFootNotes { + get { + return ((bool)(this["RemoveEndAndFootNotes"])); + } + set { + this["RemoveEndAndFootNotes"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ReplaceTabsWithSpaces { + get { + return ((bool)(this["ReplaceTabsWithSpaces"])); + } + set { + this["ReplaceTabsWithSpaces"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveFieldCodes { + get { + return ((bool)(this["RemoveFieldCodes"])); + } + set { + this["RemoveFieldCodes"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemovePermissions { + get { + return ((bool)(this["RemovePermissions"])); + } + set { + this["RemovePermissions"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveProof { + get { + return ((bool)(this["RemoveProof"])); + } + set { + this["RemoveProof"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveSoftHyphens { + get { + return ((bool)(this["RemoveSoftHyphens"])); + } + set { + this["RemoveSoftHyphens"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveLastRenderedPageBreak { + get { + return ((bool)(this["RemoveLastRenderedPageBreak"])); + } + set { + this["RemoveLastRenderedPageBreak"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveBookmarks { + get { + return ((bool)(this["RemoveBookmarks"])); + } + set { + this["RemoveBookmarks"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RemoveWebHidden { + get { + return ((bool)(this["RemoveWebHidden"])); + } + set { + this["RemoveWebHidden"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool NormalizeXml { + get { + return ((bool)(this["NormalizeXml"])); + } + set { + this["NormalizeXml"] = value; + } + } } } diff --git a/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.cs b/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.cs index 5071183c..ba848f66 100644 --- a/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.cs +++ b/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.cs @@ -1,63 +1,63 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -namespace OpenXmlPowerTools +namespace MetricsGetter01 { - class MetricsGetter01 + internal class MetricsGetter01 { - static void Main(string[] args) + private static void Main() { - MetricsGetterSettings settings = null; - FileInfo fi = null; - - fi = new FileInfo("../../ContentControls.docx"); - settings = new MetricsGetterSettings(); - settings.IncludeTextInContentControls = false; + var fi = new FileInfo("../../ContentControls.docx"); + var settings = new MetricsGetterSettings + { + IncludeTextInContentControls = false + }; Console.WriteLine("============== No text from content controls =============="); Console.WriteLine(fi.FullName); Console.WriteLine(MetricsGetter.GetMetrics(fi.FullName, settings)); Console.WriteLine(); fi = new FileInfo("../../ContentControls.docx"); - settings = new MetricsGetterSettings(); - settings.IncludeTextInContentControls = true; + settings = new MetricsGetterSettings + { + IncludeTextInContentControls = true + }; Console.WriteLine("============== With text from content controls =============="); Console.WriteLine(fi.FullName); Console.WriteLine(MetricsGetter.GetMetrics(fi.FullName, settings)); Console.WriteLine(); fi = new FileInfo("../../TrackedRevisions.docx"); - settings = new MetricsGetterSettings(); - settings.IncludeTextInContentControls = true; + settings = new MetricsGetterSettings + { + IncludeTextInContentControls = true + }; Console.WriteLine("============== Tracked Revisions =============="); Console.WriteLine(fi.FullName); Console.WriteLine(MetricsGetter.GetMetrics(fi.FullName, settings)); Console.WriteLine(); fi = new FileInfo("../../Styles.docx"); - settings = new MetricsGetterSettings(); - settings.IncludeTextInContentControls = false; + settings = new MetricsGetterSettings + { + IncludeTextInContentControls = false + }; Console.WriteLine("============== Style Hierarchy =============="); Console.WriteLine(fi.FullName); Console.WriteLine(MetricsGetter.GetMetrics(fi.FullName, settings)); Console.WriteLine(); fi = new FileInfo("../../Tables.xlsx"); - settings = new MetricsGetterSettings(); - settings.IncludeTextInContentControls = false; - settings.IncludeXlsxTableCellData = true; + settings = new MetricsGetterSettings + { + IncludeTextInContentControls = false, + IncludeXlsxTableCellData = true + }; Console.WriteLine("============== Spreadsheet Tables =============="); Console.WriteLine(fi.FullName); Console.WriteLine(MetricsGetter.GetMetrics(fi.FullName, settings)); Console.WriteLine(); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.csproj b/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.csproj +++ b/OpenXmlPowerToolsExamples/MetricsGetter01/MetricsGetter01.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.cs b/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.cs index 8b0ec416..9e21022a 100644 --- a/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.cs +++ b/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.cs @@ -1,22 +1,19 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; namespace OpenXmlRegex01 { public class OpenXmlRegexExample { - public static void Main(string[] args) + public static void Main() { - DateTime n = DateTime.Now; + var n = DateTime.Now; var tempDi = new DirectoryInfo( $"ExampleOutput-{n.Year - 2000:00}-{n.Month:00}-{n.Day:00}-{n.Hour:00}{n.Minute:00}{n.Second:00}"); tempDi.Create(); @@ -24,14 +21,14 @@ public static void Main(string[] args) var sourceDoc = new FileInfo("../../TestDocument.docx"); var newDoc = new FileInfo(Path.Combine(tempDi.FullName, "Modified.docx")); File.Copy(sourceDoc.FullName, newDoc.FullName); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(newDoc.FullName, true)) + using (var wDoc = WordprocessingDocument.Open(newDoc.FullName, true)) { - XDocument xDoc = wDoc.MainDocumentPart.GetXDocument(); + var xDoc = wDoc.MainDocumentPart.GetXDocument(); // Match content (paragraph 1) - IEnumerable content = xDoc.Descendants(W.p).Take(1); + var content = xDoc.Descendants(W.p).Take(1); var regex = new Regex("Video"); - int count = OpenXmlRegex.Match(content, regex); + var count = OpenXmlRegex.Match(content, regex); Console.WriteLine("Example #1 Count: {0}", count); // Match content, case insensitive (paragraph 1) @@ -177,7 +174,7 @@ public static void Main(string[] args) Console.WriteLine("Example #23 Replaced: {0}", count); // Remove soft hyphens (paragraph 22) - List paras = xDoc.Descendants(W.p).Skip(21).Take(1).ToList(); + var paras = xDoc.Descendants(W.p).Skip(21).Take(1).ToList(); count = OpenXmlRegex.Replace(paras, new Regex($"{UnicodeMapper.SoftHyphen}"), "", null); count += OpenXmlRegex.Replace(paras, new Regex("use"), "no longer use", null); Console.WriteLine("Example #24 Replaced: {0}", count); @@ -198,10 +195,10 @@ public static void Main(string[] args) // spider (same value with different font) will be represented by U+E001. // If spider had been assigned first, spider would be U+F021 and pencil // would be U+E001. - char oldPhone = UnicodeMapper.SymToChar("Wingdings", 40); - char newPhone = UnicodeMapper.SymToChar("Wingdings", 41); - char pencil = UnicodeMapper.SymToChar("Wingdings", 0x21); - char spider = UnicodeMapper.SymToChar("Webdings", 0x21); + var oldPhone = UnicodeMapper.SymToChar("Wingdings", 40); + var newPhone = UnicodeMapper.SymToChar("Wingdings", 41); + var pencil = UnicodeMapper.SymToChar("Wingdings", 0x21); + var spider = UnicodeMapper.SymToChar("Webdings", 0x21); // Replace or comment on symbols (paragraph 23) paras = xDoc.Descendants(W.p).Skip(22).Take(1).ToList(); @@ -216,26 +213,24 @@ public static void Main(string[] args) var sourcePres = new FileInfo("../../TestPresentation.pptx"); var newPres = new FileInfo(Path.Combine(tempDi.FullName, "Modified.pptx")); File.Copy(sourcePres.FullName, newPres.FullName); - using (PresentationDocument pDoc = PresentationDocument.Open(newPres.FullName, true)) + using var pDoc = PresentationDocument.Open(newPres.FullName, true); + foreach (var slidePart in pDoc.PresentationPart.SlideParts) { - foreach (SlidePart slidePart in pDoc.PresentationPart.SlideParts) - { - XDocument xDoc = slidePart.GetXDocument(); - - // Replace content - IEnumerable content = xDoc.Descendants(A.p); - var regex = new Regex("Hello"); - int count = OpenXmlRegex.Replace(content, regex, "H e l l o", null); - Console.WriteLine("Example #18 Replaced: {0}", count); - - // If you absolutely want to preserve compatibility with PowerPoint 2007, then you will need to strip the xml:space="preserve" attribute throughout. - // This is an issue for PowerPoint only, not Word, and for 2007 only. - // The side-effect of this is that if a run has space at the beginning or end of it, the space will be stripped upon loading, and content/layout will be affected. - xDoc.Descendants().Attributes(XNamespace.Xml + "space").Remove(); - - slidePart.PutXDocument(); - } + var xDoc = slidePart.GetXDocument(); + + // Replace content + var content = xDoc.Descendants(A.p); + var regex = new Regex("Hello"); + var count = OpenXmlRegex.Replace(content, regex, "H e l l o", null); + Console.WriteLine("Example #18 Replaced: {0}", count); + + // If you absolutely want to preserve compatibility with PowerPoint 2007, then you will need to strip the xml:space="preserve" attribute throughout. + // This is an issue for PowerPoint only, not Word, and for 2007 only. + // The side-effect of this is that if a run has space at the beginning or end of it, the space will be stripped upon loading, and content/layout will be affected. + xDoc.Descendants().Attributes(XNamespace.Xml + "space").Remove(); + + slidePart.PutXDocument(); } } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.csproj b/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.csproj index e188d465..100b0d14 100644 --- a/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.csproj +++ b/OpenXmlPowerToolsExamples/OpenXmlRegex01/OpenXmlRegex01.csproj @@ -1,15 +1,13 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/OpenXmlRegex02/OpenXmlRegex02.csproj b/OpenXmlPowerToolsExamples/OpenXmlRegex02/OpenXmlRegex02.csproj index d1fe6d31..a74d8e76 100644 --- a/OpenXmlPowerToolsExamples/OpenXmlRegex02/OpenXmlRegex02.csproj +++ b/OpenXmlPowerToolsExamples/OpenXmlRegex02/OpenXmlRegex02.csproj @@ -1,11 +1,10 @@ - - - Exe - net45 - - - - - - + + + Exe + net8.0 + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/PivotTables01/PivotTables.cs b/OpenXmlPowerToolsExamples/PivotTables01/PivotTables.cs index 352980c8..9225f8e8 100644 --- a/OpenXmlPowerToolsExamples/PivotTables01/PivotTables.cs +++ b/OpenXmlPowerToolsExamples/PivotTables01/PivotTables.cs @@ -1,54 +1,47 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; -using System.Xml.Linq; using System.IO; -using System.Timers; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -namespace ExamplePivotTables +namespace PivotTables01 { - class PivotTableExample + internal class PivotTableExample { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); // Update an existing pivot table - FileInfo qs = new FileInfo("../../QuarterlySales.xlsx"); - FileInfo qsu = new FileInfo(Path.Combine(tempDi.FullName, "QuarterlyPivot.xlsx")); + var qs = new FileInfo("../../QuarterlySales.xlsx"); + var qsu = new FileInfo(Path.Combine(tempDi.FullName, "QuarterlyPivot.xlsx")); - int row = 1; - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument( - SmlDocument.FromFileName(qs.FullName))) + var row = 1; + using (var streamDoc = new OpenXmlMemoryStreamDocument( + OpenXmlPowerToolsDocument.FromFileName(qs.FullName))) { - using (SpreadsheetDocument doc = streamDoc.GetSpreadsheetDocument()) + using (var doc = streamDoc.GetSpreadsheetDocument()) { - WorksheetPart sheet = WorksheetAccessor.GetWorksheet(doc, "Range"); - using (StreamReader source = new StreamReader("../../PivotData.txt")) + var sheet = WorksheetAccessor.GetWorksheet(doc, "Range"); + using (var source = new StreamReader("../../PivotData.txt")) { while (!source.EndOfStream) { - string line = source.ReadLine(); + var line = source.ReadLine(); if (line.Length > 3) { - string[] fields = line.Split(','); - int column = 1; - foreach (string item in fields) + var fields = line.Split(','); + var column = 1; + foreach (var item in fields) { - double num; - if (double.TryParse(item, out num)) - WorksheetAccessor.SetCellValue(doc, sheet, row, column++, num); + if (double.TryParse(item, out var num)) + { + WorksheetAccessor.SetCellValue(sheet, row, column++, num); + } else - WorksheetAccessor.SetCellValue(doc, sheet, row, column++, item); + { + WorksheetAccessor.SetCellValue(sheet, row, column++, item); + } } } row++; @@ -63,306 +56,22 @@ static void Main(string[] args) // Create from scratch row = 1; - int maxColumn = 1; - using (OpenXmlMemoryStreamDocument streamDoc = OpenXmlMemoryStreamDocument.CreateSpreadsheetDocument()) + var maxColumn = 1; + using (var streamDoc = OpenXmlMemoryStreamDocument.CreateSpreadsheetDocument()) { - using (SpreadsheetDocument doc = streamDoc.GetSpreadsheetDocument()) + using (var doc = streamDoc.GetSpreadsheetDocument()) { WorksheetAccessor.CreateDefaultStyles(doc); - WorksheetPart sheet = WorksheetAccessor.AddWorksheet(doc, "Range"); - MemorySpreadsheet ms = new MemorySpreadsheet(); + var sheet = WorksheetAccessor.AddWorksheet(doc, "Range"); + var ms = new MemorySpreadsheet(); -#if false - int font0 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 1), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font2 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 18, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 3), - Name = "Cambria", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Major - }); - int font3 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 15, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 3), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font4 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 13, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 3), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font5 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 11, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 3), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font6 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FF006100"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font7 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FF9C0006"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font8 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FF9C6500"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font9 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FF3F3F76"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font10 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FF3F3F3F"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font11 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FFFA7D00"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font12 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FFFA7D00"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font13 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 11, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 0), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font14 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FFFF0000"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font15 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Italic = true, - Size = 11, - Color = new WorksheetAccessor.ColorInfo("FF7F7F7F"), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font16 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Bold = true, - Size = 11, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 1), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - int font17 = WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font - { - Size = 11, - Color = new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 0), - Name = "Calibri", - Family = 2, - Scheme = WorksheetAccessor.Font.SchemeType.Minor - }); - - int fill0 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.None, null, null)); - int fill1 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Gray125, null, null)); - int fill2 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo("FFC6EFCE"))); - int fill3 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo("FFFFC7CE"))); - int fill4 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo("FFFFEB9C"))); - int fill5 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo("FFFFCC99"))); - int fill6 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo("FFF2F2F2"))); - int fill7 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo("FFA5A5A5"))); - int fill8 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo("FFFFFFCC"))); - int fill9 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 4))); - int fill10 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(4, 0.79998168889431442))); - int fill11 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(4, 0.59999389629810485))); - int fill12 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(4, 0.39997558519241921))); - int fill13 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 5))); - int fill14 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(5, 0.79998168889431442))); - int fill15 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(5, 0.59999389629810485))); - int fill16 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(5, 0.39997558519241921))); - int fill17 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 6))); - int fill18 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(6, 0.79998168889431442))); - int fill19 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(6, 0.59999389629810485))); - int fill20 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(6, 0.39997558519241921))); - int fill21 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 7))); - int fill22 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(7, 0.79998168889431442))); - int fill23 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(7, 0.59999389629810485))); - int fill24 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(7, 0.39997558519241921))); - int fill25 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 8))); - int fill26 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(8, 0.79998168889431442))); - int fill27 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(8, 0.59999389629810485))); - int fill28 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(8, 0.39997558519241921))); - int fill29 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - null, new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 9))); - int fill30 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(9, 0.79998168889431442))); - int fill31 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(9, 0.59999389629810485))); - int fill32 = WorksheetAccessor.GetFillIndex(doc, new WorksheetAccessor.PatternFill(WorksheetAccessor.PatternFill.PatternType.Solid, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Indexed, 65), - new WorksheetAccessor.ColorInfo(9, 0.39997558519241921))); - - int border1 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thick, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 4)) - }); - int border2 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thick, new WorksheetAccessor.ColorInfo(4, 0.499984740745262)) - }); - int border3 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Medium, new WorksheetAccessor.ColorInfo(4, 0.39997558519241921)) - }); - int border4 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Left = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF7F7F7F")), - Right = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF7F7F7F")), - Top = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF7F7F7F")), - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF7F7F7F")) - }); - int border5 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Left = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF3F3F3F")), - Right = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF3F3F3F")), - Top = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF3F3F3F")), - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FF3F3F3F")) - }); - int border6 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Double, new WorksheetAccessor.ColorInfo("FFFF8001")) - }); - int border7 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Left = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Double, new WorksheetAccessor.ColorInfo("FF3F3F3F")), - Right = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Double, new WorksheetAccessor.ColorInfo("FF3F3F3F")), - Top = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Double, new WorksheetAccessor.ColorInfo("FF3F3F3F")), - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Double, new WorksheetAccessor.ColorInfo("FF3F3F3F")) - }); - int border8 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Left = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FFB2B2B2")), - Right = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FFB2B2B2")), - Top = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FFB2B2B2")), - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, new WorksheetAccessor.ColorInfo("FFB2B2B2")) - }); - int border9 = WorksheetAccessor.GetBorderIndex(doc, new WorksheetAccessor.Border - { - Top = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Thin, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 4)), - Bottom = new WorksheetAccessor.BorderLine(WorksheetAccessor.BorderLine.LineStyle.Double, - new WorksheetAccessor.ColorInfo(WorksheetAccessor.ColorInfo.ColorType.Theme, 4)) - }); -#endif - - int southIndex = WorksheetAccessor.GetStyleIndex(doc, 0, 8, 1, 2, + var southIndex = WorksheetAccessor.GetStyleIndex(doc, 0, 8, 1, 2, new WorksheetAccessor.CellAlignment { HorizontalAlignment = WorksheetAccessor.CellAlignment.Horizontal.Center }, true, false); - WorksheetAccessor.GradientFill gradient = new WorksheetAccessor.GradientFill(90); + var gradient = new WorksheetAccessor.GradientFill(90); gradient.AddStop(new WorksheetAccessor.GradientStop(0, new WorksheetAccessor.ColorInfo("FF92D050"))); gradient.AddStop(new WorksheetAccessor.GradientStop(1, new WorksheetAccessor.ColorInfo("FF0070C0"))); - int northIndex = WorksheetAccessor.GetStyleIndex(doc, 0, + var northIndex = WorksheetAccessor.GetStyleIndex(doc, 0, WorksheetAccessor.GetFontIndex(doc, new WorksheetAccessor.Font { Italic = true, @@ -380,80 +89,90 @@ static void Main(string[] args) }), null, false, false); WorksheetAccessor.CheckNumberFormat(doc, 100, "_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)"); - int amountIndex = WorksheetAccessor.GetStyleIndex(doc, 100, 0, 0, 0, null, false, false); + var amountIndex = WorksheetAccessor.GetStyleIndex(doc, 100, 0, 0, 0, null, false, false); - using (StreamReader source = new StreamReader("../../PivotData.txt")) + using (var source = new StreamReader("../../PivotData.txt")) { while (!source.EndOfStream) { - string line = source.ReadLine(); + var line = source.ReadLine(); if (line.Length > 3) { - string[] fields = line.Split(','); - int column = 1; - foreach (string item in fields) + var fields = line.Split(','); + var column = 1; + foreach (var item in fields) { - double num; - if (double.TryParse(item, out num)) + if (double.TryParse(item, out var num)) { if (column == 6) + { ms.SetCellValue(row, column++, num, amountIndex); + } else + { ms.SetCellValue(row, column++, num); + } } else if (item == "Accessories") + { ms.SetCellValue(row, column++, item, WorksheetAccessor.GetStyleIndex(doc, "Good")); + } else if (item == "South") + { ms.SetCellValue(row, column++, item, southIndex); + } else if (item == "North") + { ms.SetCellValue(row, column++, item, northIndex); + } else + { ms.SetCellValue(row, column++, item); + } } maxColumn = column - 1; } row++; } } - WorksheetAccessor.SetSheetContents(doc, sheet, ms); + WorksheetAccessor.SetSheetContents(sheet, ms); WorksheetAccessor.SetRange(doc, "Sales", "Range", 1, 1, row - 1, maxColumn); - WorksheetPart pivot = WorksheetAccessor.AddWorksheet(doc, "Pivot"); + var pivot = WorksheetAccessor.AddWorksheet(doc, "Pivot"); WorksheetAccessor.CreatePivotTable(doc, "Sales", pivot); // Configure pivot table rows, columns, data and filters - WorksheetAccessor.AddPivotAxis(doc, pivot, "Year", WorksheetAccessor.PivotAxis.Column); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Quarter", WorksheetAccessor.PivotAxis.Column); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Category", WorksheetAccessor.PivotAxis.Row); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Product", WorksheetAccessor.PivotAxis.Row); + WorksheetAccessor.AddPivotAxis(pivot, "Year", WorksheetAccessor.PivotAxis.Column); + WorksheetAccessor.AddPivotAxis(pivot, "Quarter", WorksheetAccessor.PivotAxis.Column); + WorksheetAccessor.AddPivotAxis(pivot, "Category", WorksheetAccessor.PivotAxis.Row); + WorksheetAccessor.AddPivotAxis(pivot, "Product", WorksheetAccessor.PivotAxis.Row); WorksheetAccessor.AddDataValue(doc, pivot, "Amount"); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Region", WorksheetAccessor.PivotAxis.Page); + WorksheetAccessor.AddPivotAxis(pivot, "Region", WorksheetAccessor.PivotAxis.Page); } streamDoc.GetModifiedSmlDocument().SaveAs(Path.Combine(tempDi.FullName, "NewPivot.xlsx")); } - // Add pivot table to existing spreadsheet // Demonstrate multiple data fields - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument( - SmlDocument.FromFileName("../../QuarterlyUnitSales.xlsx"))) + using (var streamDoc = new OpenXmlMemoryStreamDocument( + OpenXmlPowerToolsDocument.FromFileName("../../QuarterlyUnitSales.xlsx"))) { - using (SpreadsheetDocument doc = streamDoc.GetSpreadsheetDocument()) + using (var doc = streamDoc.GetSpreadsheetDocument()) { - WorksheetPart pivot = WorksheetAccessor.AddWorksheet(doc, "Pivot"); + var pivot = WorksheetAccessor.AddWorksheet(doc, "Pivot"); WorksheetAccessor.CreatePivotTable(doc, "Sales", pivot); // Configure pivot table rows, columns, data and filters - WorksheetAccessor.AddPivotAxis(doc, pivot, "Year", WorksheetAccessor.PivotAxis.Column); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Quarter", WorksheetAccessor.PivotAxis.Column); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Category", WorksheetAccessor.PivotAxis.Row); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Product", WorksheetAccessor.PivotAxis.Row); + WorksheetAccessor.AddPivotAxis(pivot, "Year", WorksheetAccessor.PivotAxis.Column); + WorksheetAccessor.AddPivotAxis(pivot, "Quarter", WorksheetAccessor.PivotAxis.Column); + WorksheetAccessor.AddPivotAxis(pivot, "Category", WorksheetAccessor.PivotAxis.Row); + WorksheetAccessor.AddPivotAxis(pivot, "Product", WorksheetAccessor.PivotAxis.Row); WorksheetAccessor.AddDataValue(doc, pivot, "Total"); WorksheetAccessor.AddDataValue(doc, pivot, "Quantity"); WorksheetAccessor.AddDataValue(doc, pivot, "Unit Price"); - WorksheetAccessor.AddPivotAxis(doc, pivot, "Region", WorksheetAccessor.PivotAxis.Page); + WorksheetAccessor.AddPivotAxis(pivot, "Region", WorksheetAccessor.PivotAxis.Page); } streamDoc.GetModifiedSmlDocument().SaveAs(Path.Combine(tempDi.FullName, "QuarterlyUnitSalesWithPivot.xlsx")); } } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/PivotTables01/PivotTables01.csproj b/OpenXmlPowerToolsExamples/PivotTables01/PivotTables01.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/PivotTables01/PivotTables01.csproj +++ b/OpenXmlPowerToolsExamples/PivotTables01/PivotTables01.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Companies.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Companies.pptx deleted file mode 100644 index 641af8d8..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Companies.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso One.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso One.pptx deleted file mode 100644 index 4f898773..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso One.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso Three.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso Three.pptx deleted file mode 100644 index 5f8b9629..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso Three.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso Two.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso Two.pptx deleted file mode 100644 index 06432bf0..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso Two.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso.pptx deleted file mode 100644 index 752cf6e7..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Contoso.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Customer Content.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Customer Content.pptx deleted file mode 100644 index 02baedbc..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Customer Content.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Note-ExampleOutput-files-are-in-bin-debug.txt b/OpenXmlPowerToolsExamples/PresentationBuilder01/Note-ExampleOutput-files-are-in-bin-debug.txt deleted file mode 100644 index b8881c1c..00000000 --- a/OpenXmlPowerToolsExamples/PresentationBuilder01/Note-ExampleOutput-files-are-in-bin-debug.txt +++ /dev/null @@ -1,3 +0,0 @@ -Example output files are in a DateTime stamped directory in ./bin/debug. The directory name is ExampleOutput-yy-mm-dd-hhmmss. - -If you are building in release mode, they will, of course, be in ./bin/release. \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation One.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation One.pptx deleted file mode 100644 index 04975622..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation One.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation Three.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation Three.pptx deleted file mode 100644 index 4a70f956..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation Three.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation Two.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation Two.pptx deleted file mode 100644 index d0f7e822..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder01/Presentation Two.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/PresentationBuilder01.cs b/OpenXmlPowerToolsExamples/PresentationBuilder01/PresentationBuilder01.cs deleted file mode 100644 index e3615f25..00000000 --- a/OpenXmlPowerToolsExamples/PresentationBuilder01/PresentationBuilder01.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using OpenXmlPowerTools; - -namespace ExamplePresentatonBuilder01 -{ - class ExamplePresentationBuilder01 - { - static void Main(string[] args) - { - var n = DateTime.Now; - var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); - tempDi.Create(); - - string source1 = "../../Contoso.pptx"; - string source2 = "../../Companies.pptx"; - string source3 = "../../Customer Content.pptx"; - string source4 = "../../Presentation One.pptx"; - string source5 = "../../Presentation Two.pptx"; - string source6 = "../../Presentation Three.pptx"; - string contoso1 = "../../Contoso One.pptx"; - string contoso2 = "../../Contoso Two.pptx"; - string contoso3 = "../../Contoso Three.pptx"; - List sources = null; - - var sourceDoc = new PmlDocument(source1); - sources = new List() - { - new SlideSource(sourceDoc, 0, 1, false), // Title - new SlideSource(sourceDoc, 1, 1, false), // First intro (of 3) - new SlideSource(sourceDoc, 4, 2, false), // Sales bios - new SlideSource(sourceDoc, 9, 3, false), // Content slides - new SlideSource(sourceDoc, 13, 1, false), // Closing summary - }; - PresentationBuilder.BuildPresentation(sources, Path.Combine(tempDi.FullName, "Out1.pptx")); - - sources = new List() - { - new SlideSource(new PmlDocument(source2), 2, 1, true), // Choose company - new SlideSource(new PmlDocument(source3), false), // Content - }; - PresentationBuilder.BuildPresentation(sources, Path.Combine(tempDi.FullName, "Out2.pptx")); - - sources = new List() - { - new SlideSource(new PmlDocument(source4), true), - new SlideSource(new PmlDocument(source5), true), - new SlideSource(new PmlDocument(source6), true), - }; - PresentationBuilder.BuildPresentation(sources, Path.Combine(tempDi.FullName, "Out3.pptx")); - - sources = new List() - { - new SlideSource(new PmlDocument(contoso1), true), - new SlideSource(new PmlDocument(contoso2), true), - new SlideSource(new PmlDocument(contoso3), true), - }; - PresentationBuilder.BuildPresentation(sources, Path.Combine(tempDi.FullName, "Out4.pptx")); - } - } -} diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder01/PresentationBuilder01.csproj b/OpenXmlPowerToolsExamples/PresentationBuilder01/PresentationBuilder01.csproj deleted file mode 100644 index e188d465..00000000 --- a/OpenXmlPowerToolsExamples/PresentationBuilder01/PresentationBuilder01.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - - \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder02/HiddenPresentation.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder02/HiddenPresentation.pptx deleted file mode 100644 index b37a76ba..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder02/HiddenPresentation.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder02/Note-ExampleOutput-files-are-in-bin-debug.txt b/OpenXmlPowerToolsExamples/PresentationBuilder02/Note-ExampleOutput-files-are-in-bin-debug.txt deleted file mode 100644 index b8881c1c..00000000 --- a/OpenXmlPowerToolsExamples/PresentationBuilder02/Note-ExampleOutput-files-are-in-bin-debug.txt +++ /dev/null @@ -1,3 +0,0 @@ -Example output files are in a DateTime stamped directory in ./bin/debug. The directory name is ExampleOutput-yy-mm-dd-hhmmss. - -If you are building in release mode, they will, of course, be in ./bin/release. \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder02/Presentation1.pptx b/OpenXmlPowerToolsExamples/PresentationBuilder02/Presentation1.pptx deleted file mode 100644 index 12e7d785..00000000 Binary files a/OpenXmlPowerToolsExamples/PresentationBuilder02/Presentation1.pptx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder02/PresentationBuilder02.cs b/OpenXmlPowerToolsExamples/PresentationBuilder02/PresentationBuilder02.cs deleted file mode 100644 index a676f7ed..00000000 --- a/OpenXmlPowerToolsExamples/PresentationBuilder02/PresentationBuilder02.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; - -namespace PresentationBuilder02 -{ - class PresentationBuilder02 - { - static void Main(string[] args) - { - var n = DateTime.Now; - var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); - tempDi.Create(); - - string presentation = "../../Presentation1.pptx"; - string hiddenPresentation = "../../HiddenPresentation.pptx"; - - // First, load both presentations into byte arrays, simulating retrieving presentations from some source - // such as a SharePoint server - var baPresentation = File.ReadAllBytes(presentation); - var baHiddenPresentation = File.ReadAllBytes(hiddenPresentation); - - // Next, replace "thee" with "the" in the main presentation - var pmlMainPresentation = new PmlDocument("Main.pptx", baPresentation); - PmlDocument modifiedMainPresentation = null; - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(pmlMainPresentation)) - { - using (PresentationDocument document = streamDoc.GetPresentationDocument()) - { - var pXDoc = document.PresentationPart.GetXDocument(); - foreach (var slideId in pXDoc.Root.Elements(P.sldIdLst).Elements(P.sldId)) - { - var slideRelId = (string)slideId.Attribute(R.id); - var slidePart = document.PresentationPart.GetPartById(slideRelId); - var slideXDoc = slidePart.GetXDocument(); - var paragraphs = slideXDoc.Descendants(A.p).ToList(); - OpenXmlRegex.Replace(paragraphs, new Regex("thee"), "the", null); - slidePart.PutXDocument(); - } - } - modifiedMainPresentation = streamDoc.GetModifiedPmlDocument(); - } - - // Combine the two presentations into a single presentation - var slideSources = new List() { - new SlideSource(modifiedMainPresentation, 0, 1, true), - new SlideSource(new PmlDocument("Hidden.pptx", baHiddenPresentation), true), - new SlideSource(modifiedMainPresentation, 1, true), - }; - PmlDocument combinedPresentation = PresentationBuilder.BuildPresentation(slideSources); - - // Replace <# TRADEMARK #> with AdventureWorks (c) - PmlDocument modifiedCombinedPresentation = null; - using (OpenXmlMemoryStreamDocument streamDoc = new OpenXmlMemoryStreamDocument(combinedPresentation)) - { - using (PresentationDocument document = streamDoc.GetPresentationDocument()) - { - var pXDoc = document.PresentationPart.GetXDocument(); - foreach (var slideId in pXDoc.Root.Elements(P.sldIdLst).Elements(P.sldId).Skip(1).Take(1)) - { - var slideRelId = (string)slideId.Attribute(R.id); - var slidePart = document.PresentationPart.GetPartById(slideRelId); - var slideXDoc = slidePart.GetXDocument(); - var paragraphs = slideXDoc.Descendants(A.p).ToList(); - OpenXmlRegex.Replace(paragraphs, new Regex("<# TRADEMARK #>"), "AdventureWorks (c)", null); - slidePart.PutXDocument(); - } - } - modifiedCombinedPresentation = streamDoc.GetModifiedPmlDocument(); - } - - // we now have a PmlDocument (which is essentially a byte array) that can be saved as necessary. - modifiedCombinedPresentation.SaveAs(Path.Combine(tempDi.FullName, "Modified.pptx")); - } - } -} diff --git a/OpenXmlPowerToolsExamples/PresentationBuilder02/PresentationBuilder02.csproj b/OpenXmlPowerToolsExamples/PresentationBuilder02/PresentationBuilder02.csproj deleted file mode 100644 index e188d465..00000000 --- a/OpenXmlPowerToolsExamples/PresentationBuilder02/PresentationBuilder02.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - - \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.cs b/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.cs index 8be0bff6..e3a96a32 100644 --- a/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.cs +++ b/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.cs @@ -1,30 +1,27 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -class TestTocAdder +internal class TestTocAdder { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); - DirectoryInfo di2 = new DirectoryInfo("../../"); + var di2 = new DirectoryInfo("../../"); foreach (var file in di2.GetFiles("*.docx")) + { file.CopyTo(Path.Combine(tempDi.FullName, file.Name)); + } - List filesToProcess = new List(); + var filesToProcess = new List(); // Inserts a basic TOC before the first paragraph of the document - using (WordprocessingDocument wdoc = + using (var wdoc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test01.docx"), true)) { ReferenceAdder.AddToc(wdoc, "/w:document/w:body/w:p[1]", @@ -32,7 +29,7 @@ static void Main(string[] args) } // Inserts a TOC after the title of the document - using (WordprocessingDocument wdoc = + using (var wdoc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test02.docx"), true)) { ReferenceAdder.AddToc(wdoc, "/w:document/w:body/w:p[2]", @@ -40,7 +37,7 @@ static void Main(string[] args) } // Inserts a TOC with a different title - using (WordprocessingDocument wdoc = + using (var wdoc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test03.docx"), true)) { ReferenceAdder.AddToc(wdoc, "/w:document/w:body/w:p[1]", @@ -48,7 +45,7 @@ static void Main(string[] args) } // Inserts a TOC that includes headings through level 4 - using (WordprocessingDocument wdoc = + using (var wdoc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test04.docx"), true)) { ReferenceAdder.AddToc(wdoc, "/w:document/w:body/w:p[1]", @@ -56,7 +53,7 @@ static void Main(string[] args) } // Inserts a table of figures - using (WordprocessingDocument wdoc = + using (var wdoc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test05.docx"), true)) { ReferenceAdder.AddTof(wdoc, "/w:document/w:body/w:p[2]", @@ -65,7 +62,7 @@ static void Main(string[] args) // Inserts a basic TOC before the first paragraph of the document. // Test06.docx does not include a StylesWithEffects part. - using (WordprocessingDocument wdoc = + using (var wdoc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test06.docx"), true)) { ReferenceAdder.AddToc(wdoc, "/w:document/w:body/w:p[1]", @@ -73,11 +70,11 @@ static void Main(string[] args) } // Inserts a TOA before the first paragraph of the document. - using (WordprocessingDocument wdoc = + using (var wdoc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test07.docx"), true)) { ReferenceAdder.AddToa(wdoc, "/w:document/w:body/w:p[2]", @"TOA \h \c ""1"" \p", null); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.csproj b/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.csproj +++ b/OpenXmlPowerToolsExamples/ReferenceAdder01/ReferenceAdder01.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.cs b/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.cs index 311d41a5..b859bd3a 100644 --- a/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.cs +++ b/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.cs @@ -1,26 +1,20 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using OpenXmlPowerTools; -namespace RevisionAccepterExample +namespace RevisionAccepter01 { - class RevisionAccepterExample + internal class RevisionAccepterExample { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); // Accept all revisions, save result as a new document - WmlDocument result = RevisionAccepter.AcceptRevisions(new WmlDocument("../../Source1.docx")); + var result = RevisionAccepter.AcceptRevisions(new WmlDocument("../../Source1.docx")); result.SaveAs(Path.Combine(tempDi.FullName, "Out1.docx")); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.csproj b/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.csproj +++ b/OpenXmlPowerToolsExamples/RevisionAccepter01/RevisionAccepter01.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.cs b/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.cs index 4bc93ced..6c7da424 100644 --- a/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.cs +++ b/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.cs @@ -1,26 +1,17 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -namespace OpenXmlPowerTools +namespace SmlDataRetriever01 { - class SmlDataRetriever01 + internal class SmlDataRetriever01 { - static void Main(string[] args) + private static void Main() { - FileInfo fi = null; - fi = new FileInfo("../../SampleSpreadsheet.xlsx"); + var fi = new FileInfo("../../SampleSpreadsheet.xlsx"); // Retrieve range from Sheet1 - XElement data = SmlDataRetriever.RetrieveRange(fi.FullName, "Sheet1", "A1:C3"); + var data = SmlDataRetriever.RetrieveRange(fi.FullName, "Sheet1", "A1:C3"); Console.WriteLine(data); // Retrieve entire sheet @@ -32,4 +23,4 @@ static void Main(string[] args) Console.WriteLine(data); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.csproj b/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.csproj +++ b/OpenXmlPowerToolsExamples/SmlDataRetriever01/SmlDataRetriever01.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.cs b/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.cs index dda2903e..ffc66d04 100644 --- a/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.cs +++ b/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.cs @@ -1,27 +1,18 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -namespace SpreadsheetWriterExample +namespace SpreadsheetWriter01 { - class Program + internal class Program { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); - WorkbookDfn wb = new WorkbookDfn + var wb = new WorkbookDfn { Worksheets = new WorksheetDfn[] { @@ -96,4 +87,4 @@ static void Main(string[] args) SpreadsheetWriter.Write(Path.Combine(tempDi.FullName, "Test1.xlsx"), wb); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.csproj b/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.csproj index e188d465..781a1464 100644 --- a/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.csproj +++ b/OpenXmlPowerToolsExamples/SpreadsheetWriter01/SpreadsheetWriter01.csproj @@ -1,15 +1,13 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.cs b/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.cs index 75390252..f3d6acbb 100644 --- a/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.cs +++ b/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.cs @@ -1,27 +1,18 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -namespace SpreadsheetWriterExample +namespace SpreadsheetWriter02 { - class Program + internal class Program { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); - WorkbookDfn wb = new WorkbookDfn + var wb = new WorkbookDfn { Worksheets = new WorksheetDfn[] { @@ -97,7 +88,7 @@ static void Main(string[] args) }, new CellDfn { CellDataType = CellDataType.Number, - Value = (int)100, + Value = 100, }, } }, @@ -153,7 +144,7 @@ static void Main(string[] args) }, new CellDfn { CellDataType = CellDataType.Number, - Value = Int64.MaxValue, + Value = long.MaxValue, }, } }, @@ -181,7 +172,7 @@ static void Main(string[] args) }, new CellDfn { CellDataType = CellDataType.Number, - Value = (double)123.45, + Value = 123.45, }, } }, @@ -224,4 +215,4 @@ static void Main(string[] args) SpreadsheetWriter.Write(Path.Combine(tempDi.FullName, "Test2.xlsx"), wb); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.csproj b/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.csproj index e188d465..e9065c2d 100644 --- a/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.csproj +++ b/OpenXmlPowerToolsExamples/SpreadsheetWriter02/SpreadsheetWriter02.csproj @@ -1,15 +1,13 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.cs b/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.cs index dc924a6f..57c1cd8f 100644 --- a/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.cs +++ b/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.cs @@ -1,39 +1,33 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -class TestPmlTextReplacer +internal class TestPmlTextReplacer { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); File.Copy("../../Test01.pptx", Path.Combine(tempDi.FullName, "Test01out.pptx")); - using (PresentationDocument pDoc = + using (var pDoc = PresentationDocument.Open(Path.Combine(tempDi.FullName, "Test01out.pptx"), true)) { TextReplacer.SearchAndReplace(pDoc, "Hello", "Goodbye", true); } File.Copy("../../Test02.pptx", Path.Combine(tempDi.FullName, "Test02out.pptx")); - using (PresentationDocument pDoc = + using (var pDoc = PresentationDocument.Open(Path.Combine(tempDi.FullName, "Test02out.pptx"), true)) { TextReplacer.SearchAndReplace(pDoc, "Hello", "Goodbye", true); } File.Copy("../../Test03.pptx", Path.Combine(tempDi.FullName, "Test03out.pptx")); - using (PresentationDocument pDoc = + using (var pDoc = PresentationDocument.Open(Path.Combine(tempDi.FullName, "Test03out.pptx"), true)) { TextReplacer.SearchAndReplace(pDoc, "Hello", "Goodbye", false); } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.csproj b/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.csproj index e188d465..781a1464 100644 --- a/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.csproj +++ b/OpenXmlPowerToolsExamples/TextReplacer01/TextReplacer01.csproj @@ -1,15 +1,13 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.cs b/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.cs index 7071816b..55bca9e7 100644 --- a/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.cs +++ b/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.cs @@ -1,55 +1,70 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - +using Codeuctivity.OpenXmlPowerTools; +using DocumentFormat.OpenXml.Packaging; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -namespace OpenXmlPowerTools +namespace TextReplacer02 { - class Program + internal class Program { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); tempDi.Create(); - DirectoryInfo di2 = new DirectoryInfo("../../"); + var di2 = new DirectoryInfo("../../"); foreach (var file in di2.GetFiles("*.docx")) + { file.CopyTo(Path.Combine(tempDi.FullName, file.Name)); + } - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test01.docx"), true)) + using (var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test01.docx"), true)) + { TextReplacer.SearchAndReplace(doc, "the", "this", false); + } + try { - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test02.docx"), true)) - TextReplacer.SearchAndReplace(doc, "the", "this", false); + using var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test02.docx"), true); + TextReplacer.SearchAndReplace(doc, "the", "this", false); } catch (Exception) { } try { - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test03.docx"), true)) - TextReplacer.SearchAndReplace(doc, "the", "this", false); + using var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test03.docx"), true); + TextReplacer.SearchAndReplace(doc, "the", "this", false); } catch (Exception) { } - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test04.docx"), true)) + using (var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test04.docx"), true)) + { TextReplacer.SearchAndReplace(doc, "the", "this", true); - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test05.docx"), true)) + } + + using (var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test05.docx"), true)) + { TextReplacer.SearchAndReplace(doc, "is on", "is above", true); - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test06.docx"), true)) + } + + using (var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test06.docx"), true)) + { TextReplacer.SearchAndReplace(doc, "the", "this", false); - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test07.docx"), true)) + } + + using (var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test07.docx"), true)) + { TextReplacer.SearchAndReplace(doc, "the", "this", true); - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test08.docx"), true)) + } + + using (var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test08.docx"), true)) + { TextReplacer.SearchAndReplace(doc, "the", "this", true); - using (WordprocessingDocument doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test09.docx"), true)) + } + + using (var doc = WordprocessingDocument.Open(Path.Combine(tempDi.FullName, "Test09.docx"), true)) + { TextReplacer.SearchAndReplace(doc, "===== Replace this text =====", "***zzz***", true); + } } } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.csproj b/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.csproj +++ b/OpenXmlPowerToolsExamples/TextReplacer02/TextReplacer02.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WmlComparer01/Source1.docx b/OpenXmlPowerToolsExamples/WmlComparer01/Source1.docx deleted file mode 100644 index 66ec2f8b..00000000 Binary files a/OpenXmlPowerToolsExamples/WmlComparer01/Source1.docx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/WmlComparer01/Source2.docx b/OpenXmlPowerToolsExamples/WmlComparer01/Source2.docx deleted file mode 100644 index 9334a8f4..00000000 Binary files a/OpenXmlPowerToolsExamples/WmlComparer01/Source2.docx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/WmlComparer01/WmlComparer01.cs b/OpenXmlPowerToolsExamples/WmlComparer01/WmlComparer01.cs deleted file mode 100644 index db4f3526..00000000 --- a/OpenXmlPowerToolsExamples/WmlComparer01/WmlComparer01.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; - -namespace OpenXmlPowerTools -{ - class WmlComparer01 - { - static void Main(string[] args) - { - var n = DateTime.Now; - var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); - tempDi.Create(); - - WmlComparerSettings settings = new WmlComparerSettings(); - WmlDocument result = WmlComparer.Compare( - new WmlDocument("../../Source1.docx"), - new WmlDocument("../../Source2.docx"), - settings); - result.SaveAs(Path.Combine(tempDi.FullName, "Compared.docx")); - - var revisions = WmlComparer.GetRevisions(result, settings); - foreach (var rev in revisions) - { - Console.WriteLine("Author: " + rev.Author); - Console.WriteLine("Revision type: " + rev.RevisionType); - Console.WriteLine("Revision text: " + rev.Text); - Console.WriteLine(); - } - } - } -} diff --git a/OpenXmlPowerToolsExamples/WmlComparer01/WmlComparer01.csproj b/OpenXmlPowerToolsExamples/WmlComparer01/WmlComparer01.csproj deleted file mode 100644 index e188d465..00000000 --- a/OpenXmlPowerToolsExamples/WmlComparer01/WmlComparer01.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - - \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WmlComparer02/Original.docx b/OpenXmlPowerToolsExamples/WmlComparer02/Original.docx deleted file mode 100644 index 29bfaffc..00000000 Binary files a/OpenXmlPowerToolsExamples/WmlComparer02/Original.docx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/WmlComparer02/RevisedByBob.docx b/OpenXmlPowerToolsExamples/WmlComparer02/RevisedByBob.docx deleted file mode 100644 index 3796163a..00000000 Binary files a/OpenXmlPowerToolsExamples/WmlComparer02/RevisedByBob.docx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/WmlComparer02/RevisedByMary.docx b/OpenXmlPowerToolsExamples/WmlComparer02/RevisedByMary.docx deleted file mode 100644 index 69a5a592..00000000 Binary files a/OpenXmlPowerToolsExamples/WmlComparer02/RevisedByMary.docx and /dev/null differ diff --git a/OpenXmlPowerToolsExamples/WmlComparer02/WmlComparer02.cs b/OpenXmlPowerToolsExamples/WmlComparer02/WmlComparer02.cs deleted file mode 100644 index 198f12dc..00000000 --- a/OpenXmlPowerToolsExamples/WmlComparer02/WmlComparer02.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; - -namespace OpenXmlPowerTools -{ - class WmlComparer02 - { - static void Main(string[] args) - { - var n = DateTime.Now; - var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); - tempDi.Create(); - - WmlDocument originalWml = new WmlDocument("../../Original.docx"); - List revisedDocumentInfoList = new List() - { - new WmlRevisedDocumentInfo() - { - RevisedDocument = new WmlDocument("../../RevisedByBob.docx"), - Revisor = "Bob", - Color = Color.LightBlue, - }, - new WmlRevisedDocumentInfo() - { - RevisedDocument = new WmlDocument("../../RevisedByMary.docx"), - Revisor = "Mary", - Color = Color.LightYellow, - }, - }; - WmlComparerSettings settings = new WmlComparerSettings(); - WmlDocument consolidatedWml = WmlComparer.Consolidate( - originalWml, - revisedDocumentInfoList, - settings); - consolidatedWml.SaveAs(Path.Combine(tempDi.FullName, "Consolidated.docx")); - } - } -} diff --git a/OpenXmlPowerToolsExamples/WmlComparer02/WmlComparer02.csproj b/OpenXmlPowerToolsExamples/WmlComparer02/WmlComparer02.csproj deleted file mode 100644 index e188d465..00000000 --- a/OpenXmlPowerToolsExamples/WmlComparer02/WmlComparer02.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - - \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/CustomImageHandler.cs b/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/CustomImageHandler.cs new file mode 100644 index 00000000..a41e8cb7 --- /dev/null +++ b/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/CustomImageHandler.cs @@ -0,0 +1,43 @@ +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using System.IO; +using System.Xml.Linq; + +internal class CustomImageHandler : IImageHandler +{ + public CustomImageHandler(string imageDirectoryName) + { + ImageDirectoryName = imageDirectoryName; + ImageCounter = 0; + } + + public string ImageDirectoryName { get; } + public int ImageCounter { get; private set; } + + public XElement TransformImage(ImageInfo imageInfo) + { + var localDirInfo = new DirectoryInfo(ImageDirectoryName); + if (!localDirInfo.Exists) + { + localDirInfo.Create(); + } + + ++ImageCounter; + var extension = imageInfo.ContentType.Split('/')[1].ToLower(); + + var imageFileName = ImageDirectoryName + "/image" + ImageCounter.ToString() + "." + extension; + try + { + using var fileStream = new FileStream(imageFileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite); + imageInfo.Image.CopyTo(fileStream); + } + catch (System.Runtime.InteropServices.ExternalException) + { + return null; + } + var imageSource = localDirInfo.Name + "/image" + ImageCounter.ToString() + "." + extension; + + var img = new XElement(Xhtml.img, new XAttribute(NoNamespace.src, imageSource), imageInfo.ImgStyleAttribute, imageInfo.AltText != null ? new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); + return img; + } +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.cs b/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.cs index 1b3f4d03..16f63a50 100644 --- a/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.cs +++ b/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.cs @@ -1,30 +1,15 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2010. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license -can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -***************************************************************************/ - +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using DocumentFormat.OpenXml.Packaging; using System; -using System.Drawing.Imaging; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -class WmlToHtmlConverterHelper +internal class WmlToHtmlConverterHelper { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); @@ -34,7 +19,7 @@ static void Main(string[] args) * This example loads each document into a byte array, then into a memory stream, so that the document can be opened for writing without * modifying the source document. */ - foreach (var file in Directory.GetFiles("../../", "*.docx")) + foreach (var file in Directory.GetFiles("../../../", "*.docx")) { ConvertToHtml(file, tempDi.FullName); } @@ -42,116 +27,42 @@ static void Main(string[] args) public static void ConvertToHtml(string file, string outputDirectory) { - var fi = new FileInfo(file); - Console.WriteLine(fi.Name); - byte[] byteArray = File.ReadAllBytes(fi.FullName); - using (MemoryStream memoryStream = new MemoryStream()) + var fileInfo = new FileInfo(file); + Console.WriteLine(fileInfo.Name); + var byteArray = File.ReadAllBytes(fileInfo.FullName); + using var memoryStream = new MemoryStream(); + memoryStream.Write(byteArray, 0, byteArray.Length); + using var wDoc = WordprocessingDocument.Open(memoryStream, true); + var destFileName = new FileInfo(fileInfo.Name.Replace(".docx", ".html")); + if (outputDirectory != null && !string.IsNullOrEmpty(outputDirectory)) { - memoryStream.Write(byteArray, 0, byteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) + var directoryInfo = new DirectoryInfo(outputDirectory); + if (!directoryInfo.Exists) { - var destFileName = new FileInfo(fi.Name.Replace(".docx", ".html")); - if (outputDirectory != null && outputDirectory != string.Empty) - { - DirectoryInfo di = new DirectoryInfo(outputDirectory); - if (!di.Exists) - { - throw new OpenXmlPowerToolsException("Output directory does not exist"); - } - destFileName = new FileInfo(Path.Combine(di.FullName, destFileName.Name)); - } - var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - int imageCounter = 0; - - var pageTitle = fi.FullName; - var part = wDoc.CoreFilePropertiesPart; - if (part != null) - { - pageTitle = (string)part.GetXDocument().Descendants(DC.title).FirstOrDefault() ?? fi.FullName; - } - - // TODO: Determine max-width from size of content area. - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings() - { - AdditionalCss = "body { margin: 1cm auto; max-width: 20cm; padding: 0; }", - PageTitle = pageTitle, - FabricateCssClasses = true, - CssClassPrefix = "pt-", - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - ImageHandler = imageInfo => - { - DirectoryInfo localDirInfo = new DirectoryInfo(imageDirectoryName); - if (!localDirInfo.Exists) - localDirInfo.Create(); - ++imageCounter; - string extension = imageInfo.ContentType.Split('/')[1].ToLower(); - ImageFormat imageFormat = null; - if (extension == "png") - imageFormat = ImageFormat.Png; - else if (extension == "gif") - imageFormat = ImageFormat.Gif; - else if (extension == "bmp") - imageFormat = ImageFormat.Bmp; - else if (extension == "jpeg") - imageFormat = ImageFormat.Jpeg; - else if (extension == "tiff") - { - // Convert tiff to gif. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "x-wmf") - { - extension = "wmf"; - imageFormat = ImageFormat.Wmf; - } + throw new OpenXmlPowerToolsException("Output directory does not exist"); + } + destFileName = new FileInfo(Path.Combine(directoryInfo.FullName, destFileName.Name)); + } + var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - // If the image format isn't one that we expect, ignore it, - // and don't return markup for the link. - if (imageFormat == null) - return null; + var pageTitle = fileInfo.FullName; + var part = wDoc.CoreFilePropertiesPart; + if (part != null) + { + pageTitle = (string)part.GetXDocument().Descendants(DC.title).FirstOrDefault() ?? fileInfo.FullName; + } - string imageFileName = imageDirectoryName + "/image" + - imageCounter.ToString() + "." + extension; - try - { - imageInfo.Bitmap.Save(imageFileName, imageFormat); - } - catch (System.Runtime.InteropServices.ExternalException) - { - return null; - } - string imageSource = localDirInfo.Name + "/image" + - imageCounter.ToString() + "." + extension; + // TODO: Determine max-width from size of content area. + var settings = new WmlToHtmlConverterSettings(pageTitle, new CustomImageHandler(imageDirectoryName), new TextDummyHandler(), new SymbolHandler(), new BreakHandler(), new FontHandler(), true); - XElement img = new XElement(Xhtml.img, - new XAttribute(NoNamespace.src, imageSource), - imageInfo.ImgStyleAttribute, - imageInfo.AltText != null ? - new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); - return img; - } - }; - XElement htmlElement = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); + var htmlElement = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); - // Produce HTML document with declaration to tell the browser - // we are using HTML5. - var html = new XDocument( - new XDocumentType("html", null, null, null), - htmlElement); + // Produce HTML document with declaration to tell the browser we are using HTML5. + var html = new XDocument(new XDocumentType("html", null, null, null), htmlElement); - // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type - // XEntity. PtOpenXmlUtil.cs define the XEntity class. See - // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx - // for detailed explanation. - // - // If you further transform the XML tree returned by ConvertToHtmlTransform, you - // must do it correctly, or entities will not be serialized properly. + // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type XEntity. PtOpenXmlUtil.cs define the XEntity class. See http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx for detailed explanation. If you further transform the XML tree returned by ConvertToHtmlTransform, you must do it correctly, or entities will not be serialized properly. - var htmlString = html.ToString(SaveOptions.DisableFormatting); - File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); - } - } + var htmlString = html.ToString(SaveOptions.DisableFormatting); + File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.csproj b/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.csproj index e188d465..97ff35fb 100644 --- a/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.csproj +++ b/OpenXmlPowerToolsExamples/WmlToHtmlConverter01/WmlToHtmlConverter01.csproj @@ -1,15 +1,13 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.cs b/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.cs index 429540c2..1bc2e0a2 100644 --- a/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.cs +++ b/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.cs @@ -1,31 +1,15 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -/*************************************************************************** - -Copyright (c) Microsoft Corporation 2010. - -This code is licensed using the Microsoft Public License (Ms-PL). The text of the license -can be found here: - -http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx - -***************************************************************************/ - +using Codeuctivity.OpenXmlPowerTools; +using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter; +using DocumentFormat.OpenXml.Packaging; using System; -using System.Drawing.Imaging; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using System.Xml.Linq; -using DocumentFormat.OpenXml.Packaging; -using OpenXmlPowerTools; -using System.Collections.Generic; -class WmlToHtmlConverterHelper +internal class WmlToHtmlConverterHelper { - static void Main(string[] args) + private static void Main() { var n = DateTime.Now; var tempDi = new DirectoryInfo(string.Format("ExampleOutput-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", n.Year - 2000, n.Month, n.Day, n.Hour, n.Minute, n.Second)); @@ -35,7 +19,7 @@ static void Main(string[] args) * This example loads each document into a byte array, then into a memory stream, so that the document can be opened for writing without * modifying the source document. */ - foreach (var file in Directory.GetFiles("../../", "*.docx")) + foreach (var file in Directory.GetFiles("../../../", "*.docx")) { ConvertToHtml(file, tempDi.FullName); } @@ -45,119 +29,45 @@ public static void ConvertToHtml(string file, string outputDirectory) { var fi = new FileInfo(file); Console.WriteLine(fi.Name); - byte[] byteArray = File.ReadAllBytes(fi.FullName); - using (MemoryStream memoryStream = new MemoryStream()) + var byteArray = File.ReadAllBytes(fi.FullName); + using var memoryStream = new MemoryStream(); + memoryStream.Write(byteArray, 0, byteArray.Length); + using var wDoc = WordprocessingDocument.Open(memoryStream, true); + var destFileName = new FileInfo(fi.Name.Replace(".docx", ".html")); + if (outputDirectory != null && !string.IsNullOrEmpty(outputDirectory)) { - memoryStream.Write(byteArray, 0, byteArray.Length); - using (WordprocessingDocument wDoc = WordprocessingDocument.Open(memoryStream, true)) + var di = new DirectoryInfo(outputDirectory); + if (!di.Exists) { - var destFileName = new FileInfo(fi.Name.Replace(".docx", ".html")); - if (outputDirectory != null && outputDirectory != string.Empty) - { - DirectoryInfo di = new DirectoryInfo(outputDirectory); - if (!di.Exists) - { - throw new OpenXmlPowerToolsException("Output directory does not exist"); - } - destFileName = new FileInfo(Path.Combine(di.FullName, destFileName.Name)); - } - var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - int imageCounter = 0; - - var pageTitle = fi.FullName; - var part = wDoc.CoreFilePropertiesPart; - if (part != null) - { - pageTitle = (string)part.GetXDocument().Descendants(DC.title).FirstOrDefault() ?? fi.FullName; - } - - // TODO: Determine max-width from size of content area. - WmlToHtmlConverterSettings settings = new WmlToHtmlConverterSettings() - { - AdditionalCss = "body { margin: 1cm auto; max-width: 20cm; padding: 0; }", - PageTitle = pageTitle, - FabricateCssClasses = true, - CssClassPrefix = "pt-", - RestrictToSupportedLanguages = false, - RestrictToSupportedNumberingFormats = false, - ImageHandler = imageInfo => - { - ++imageCounter; - string extension = imageInfo.ContentType.Split('/')[1].ToLower(); - ImageFormat imageFormat = null; - if (extension == "png") - imageFormat = ImageFormat.Png; - else if (extension == "gif") - imageFormat = ImageFormat.Gif; - else if (extension == "bmp") - imageFormat = ImageFormat.Bmp; - else if (extension == "jpeg") - imageFormat = ImageFormat.Jpeg; - else if (extension == "tiff") - { - // Convert tiff to gif. - extension = "gif"; - imageFormat = ImageFormat.Gif; - } - else if (extension == "x-wmf") - { - extension = "wmf"; - imageFormat = ImageFormat.Wmf; - } - - // If the image format isn't one that we expect, ignore it, - // and don't return markup for the link. - if (imageFormat == null) - return null; - - string base64 = null; - try - { - using (MemoryStream ms = new MemoryStream()) - { - imageInfo.Bitmap.Save(ms, imageFormat); - var ba = ms.ToArray(); - base64 = System.Convert.ToBase64String(ba); - } - } - catch (System.Runtime.InteropServices.ExternalException) - { - return null; - } - - ImageFormat format = imageInfo.Bitmap.RawFormat; - ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders().First(c => c.FormatID == format.Guid); - string mimeType = codec.MimeType; + throw new OpenXmlPowerToolsException("Output directory does not exist"); + } + destFileName = new FileInfo(Path.Combine(di.FullName, destFileName.Name)); + } + var imageDirectoryName = destFileName.FullName.Substring(0, destFileName.FullName.Length - 5) + "_files"; - string imageSource = string.Format("data:{0};base64,{1}", mimeType, base64); + var pageTitle = fi.FullName; + var part = wDoc.CoreFilePropertiesPart; + if (part != null) + { + pageTitle = (string)part.GetXDocument().Descendants(DC.title).FirstOrDefault() ?? fi.FullName; + } - XElement img = new XElement(Xhtml.img, - new XAttribute(NoNamespace.src, imageSource), - imageInfo.ImgStyleAttribute, - imageInfo.AltText != null ? - new XAttribute(NoNamespace.alt, imageInfo.AltText) : null); - return img; - } - }; - XElement htmlElement = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); + // TODO: Determine max-width from size of content area. + var settings = new WmlToHtmlConverterSettings(pageTitle); + var htmlElement = WmlToHtmlConverter.ConvertToHtml(wDoc, settings); - // Produce HTML document with declaration to tell the browser - // we are using HTML5. - var html = new XDocument( - new XDocumentType("html", null, null, null), - htmlElement); + // Produce HTML document with declaration to tell the browser we are using HTML5. + var html = new XDocument(new XDocumentType("html", null, null, null), htmlElement); - // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type - // XEntity. PtOpenXmlUtil.cs define the XEntity class. See - // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx - // for detailed explanation. - // - // If you further transform the XML tree returned by ConvertToHtmlTransform, you - // must do it correctly, or entities will not be serialized properly. + // Note: the xhtml returned by ConvertToHtmlTransform contains objects of type + // XEntity. PtOpenXmlUtil.cs define the XEntity class. See + // http://blogs.msdn.com/ericwhite/archive/2010/01/21/writing-entity-references-using-linq-to-xml.aspx + // for detailed explanation. + // + // If you further transform the XML tree returned by ConvertToHtmlTransform, you + // must do it correctly, or entities will not be serialized properly. - var htmlString = html.ToString(SaveOptions.DisableFormatting); - File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); - } - } + var htmlString = html.ToString(SaveOptions.DisableFormatting); + File.WriteAllText(destFileName.FullName, htmlString, Encoding.UTF8); } -} +} \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.csproj b/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.csproj index e188d465..d6fcc3e9 100644 --- a/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.csproj +++ b/OpenXmlPowerToolsExamples/WmlToHtmlConverter02/WmlToHtmlConverter02.csproj @@ -1,15 +1,12 @@ - - - Exe - net45;net46;netcoreapp2.0 - - - - - - - - - - + + + Exe + net8.0 + 8.0 + true + + + + + \ No newline at end of file diff --git a/OpenXmlPowerToolsExamples/WordAutomationUtilities/WordAutomationUtilities.csproj b/OpenXmlPowerToolsExamples/WordAutomationUtilities/WordAutomationUtilities.csproj index d1fe6d31..a74d8e76 100644 --- a/OpenXmlPowerToolsExamples/WordAutomationUtilities/WordAutomationUtilities.csproj +++ b/OpenXmlPowerToolsExamples/WordAutomationUtilities/WordAutomationUtilities.csproj @@ -1,11 +1,10 @@ - - - Exe - net45 - - - - - - + + + Exe + net8.0 + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 007489ac..d12935be 100644 --- a/README.md +++ b/README.md @@ -1,196 +1,52 @@ -NuGet Feed for CI build: https://ci.appveyor.com/nuget/open-xml-powertools +# OpenXmlPowerTools -News -==== -Welcome, Open-Xml-PowerTools users. As you may have learned from the repo at OfficeDev/Open-Xml-PowerTools, -Microsoft is going to archive that repo, and will not be maintaining that repo in the future. +[![.NET build and test](https://github.com/Codeuctivity/OpenXmlPowerTools/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Codeuctivity/OpenXmlPowerTools/actions/workflows/dotnet.yml) [![Nuget](https://img.shields.io/nuget/v/Codeuctivity.OpenXmlPowerTools.svg)](https://www.nuget.org/packages/Codeuctivity.OpenXmlPowerTools/) -Eric White +## Focus of this fork -Open-XML-PowerTools -=================== -The Open XML PowerTools provides guidance and example code for programming with Open XML -Documents (DOCX, XLSX, and PPTX). It is based on, and extends the functionality -of the [Open XML SDK](https://github.com/OfficeDev/Open-XML-SDK). +- Linux, Windows and MacOs support was added by this fork +- Conversion of DOCX to HTML/CSS + +## Known missing features - Conversion of DOCX to HTML/CSS + +- [floating settings of images are ignored](https://stackoverflow.com/questions/70277539/how-to-handle-floating-images-in-openxml-and-convert-to-html-equivalent/73639409#73639409) +- [W.oMath](https://github.com/Codeuctivity/OpenXmlToHtml/issues/74) +- many more + + +## Example - Convert DOCX to HTML + +``` csharp +var sourceDocxFileContent = File.ReadAllBytes("./source.docx"); +using var memoryStream = new MemoryStream(); +await memoryStream.WriteAsync(sourceDocxFileContent, 0, sourceDocxFileContent.Length); +using var wordProcessingDocument = WordprocessingDocument.Open(memoryStream, true); +var settings = new WmlToHtmlConverterSettings("htmlPageTitle"); +var html = WmlToHtmlConverter.ConvertToHtml(wordProcessingDocument, settings); +var htmlString = html.ToString(SaveOptions.DisableFormatting); +File.WriteAllText("./target.html", htmlString, Encoding.UTF8); +``` + +### Other features -It supports scenarios such as: - Splitting DOCX/PPTX files into multiple files. -- Combining multiple DOCX/PPTX files into a single file. +- Combining multiple DOCX files into a single file. - Populating content in template DOCX files with data from XML. -- High-fidelity conversion of DOCX to HTML/CSS. -- High-fidelity conversion of HTML/CSS to DOCX. +- Conversion of HTML/CSS to DOCX. - Searching and replacing content in DOCX/PPTX using regular expressions. - Managing tracked-revisions, including detecting tracked revisions, and accepting tracked revisions. - Updating Charts in DOCX/PPTX files, including updating cached data, as well as the embedded XLSX. -- Comparing two DOCX files, producing a DOCX with revision tracking markup, and enabling retrieving a list of revisions. - Retrieving metrics from DOCX files, including the hierarchy of styles used, the languages used, and the fonts used. - Writing XLSX files using far simpler code than directly writing the markup, including a streaming approach that enables writing XLSX files with millions of rows. - Extracting data (along with formatting) from spreadsheets. -Copyright (c) Microsoft Corporation 2012-2017 -Portions Copyright (c) Eric White Inc 2018-2019 - -Licensed under the MIT License. -See License in the project root for license information. - -News -==== -New Release! Version 4.6.0 - -This version has a completely re-written WmlComparer.cs, which now supports nested tables and text boxes. WmlComparer.cs is a module that compares two DOCX files and -produces a DOCX with revision tracking markup. It enables retrieving a list of revisions. - -Open-Xml-PowerTools Content -=========================== +## SkiaSharp migration -There is a lot of content about Open-Xml-PowerTools at the [Open-Xml-PowerTools Resource Center at OpenXmlDeveloper.org](http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx) +Earlier releases used the ImageSharp library for tasks such as decoding images in the WordprocessingML to HTML converter and validating rendering output in tests. ImageSharp's powerful API came with a more restrictive licensing model that could require commercial agreements, which proved limiting for downstream projects. -See: -- [DocumentBuilder Resource Center](http://www.ericwhite.com/blog/documentbuilder-developer-center/) -- [PresentationBuilder Resource Center](http://www.ericwhite.com/blog/presentationbuilder-developer-center/) -- [WmlToHtmlConverter Resource Center](http://www.ericwhite.com/blog/wmltohtmlconverter-developer-center/) -- [DocumentAssembler Resource Center](http://www.ericwhite.com/blog/documentassembler-developer-center/) +The project now uses SkiaSharp to handle these responsibilities. SkiaSharp, distributed under the permissive MIT license, provides cross-platform bindings to the Skia graphics engine. By leveraging SkiaSharp's `SKCodec` and `SKImage` APIs for image transformation and `SKColor` for color parsing, the codebase avoids licensing friction while retaining rich imaging capabilities. -Build Instructions -================== - -**Prerequisites:** - -- Visual Studio 2017 Update 5 or .NET CLI toolchain - -**Build** - - With Visual Studio: - -- Open `OpenXmlPowerTools.sln` in Visual Studio -- Rebuild the project -- Build the solution. To validate the build, open the Test Explorer. Click Run All. -- To run an example, set the example as the startup project, and press F5. - -With .NET CLI toolchain: +## Development - Run `dotnet build OpenXmlPowerTools.sln` - -Change Log -========== - -Version 4.6 : November 16, 2020 -- Various small bug fixes - -Version 4.5 : January 21, 2020 -- Various changes and fixes to DocumentBuilder - -Version 4.3 : June 13, 2016 -- New WmlComparer module - -Version 4.2 : December 11, 2015 -- New SmlDataRetriever module -- New SmlCellFormatter module - -Version 4.1.3 : November 2, 2015 -- DocumentAssembler: Fix bug associated with duplicate bookmarks. -- DocumentAssembler: Enable processing of content controls / metadata in footer rows. -- DocumentAssembler: Avoid processing content controls used for purposes other than the DocumentAssembler template, including page numbers in footers, etc. - -Version 4.1.2 : October 31, 2015 -- HtmlToWmlConverter: Handle unknown elements by recursively processing descendants - -Version 4.1.1 : October 21, 2015 -- Fix to AddTypes.ps1 to compile WmlToHtmlConverter.cs instead of HtmlConverter.cs -- Fix to MettricsGetter.ps1 to correctly report whether a document contains tracked revisions -- Added some unit tests for PresentationBuilder - -Version 4.1.0 : September 27, 2015 -- New HtmlToWmlConverter module -- HtmlConverter generates non breaking spaces as #00a0 unicode charater, not   entity. - -Version 4.0.0 : August 6, 2015 -- New DocumentAssember module -- New SpreadsheetWriter module -- New Cmdlet: Complete-DocxTemplateFromXml -- Fix DocumentBuilder: deal with headers / footers more rationally -- Enhance DocumentBuilder: add option to discard headers / footers from section (but keep layout of section) -- Fix RevisionAccepter: deal with w:moveTo immediately before a table -- New test document library in the TestFiles directory -- XUnit tests -- Cleaned up build system -- Build using the open source Open-Xml-SDK and the new System.IO.Packaging by default -- Back port to .NET 3.5 -- Rename the PowerShell module to Open-Xml-PowerTools - -Version 3.1.11 : June 30, 2015 -- Updated projects and solutions to build with the open source Open XML SDK and new System.IO.Packaging - -Version 3.1.10 : June 14, 2015 -- Changed Out-Xlsx Cmdlet to C# implementation -- Fix Add-DocxText - -Version 3.1.09 : April 20, 2015 -- Fix OpenXmlRegex: PowerPoint 2007 and xml:space issues, causing 2007 to not open PPTX's - -Version 3.1.08 : March 13, 2015 -- Added Out-Xlsx Cmdlet - -Version 3.1.07 : February 9, 2015 -- Added Merge-Pptx Cmdlet -- Added New-Pptx Cmdlet -- Added New-PmlDocument -- Fixed help for Merge-Docx -- Don't throw duplicate attribute exception when running FormattingAssembler.AssembleFormatting - twice on same document. - -Version 3.1.06 : February 7, 2015 -- Added Expand-DocxFormatting Cmdlet -- Cmdlets do not keep a handle to the current directory, preventing deletion of the directory. -- Added additional tests to Test-OxPtCmdlets - -Version 3.1.05 : January 29, 2015 -- Added GetListItemText_zh_CN.cs -- Fixed GetListItemText_fr_FR.cs -- Partially fixed GetListItemText_ru_RU.cs -- Fixed GetListItemText_Default.cs -- Added better support in ListItemRetriever.cs -- Added FileUtils class in PtUtil.cs - -Version 3.1.04 : December 17, 2014 -- Added Get-DocxMetrics Cmdlet -- Added New-WmlDocument Cmdlet -- Added MetricsGetter.cs module -- Added MettricsGetter01.cs module, along with sample documents -- Reworked Add-DocxText, new style of using it with New-WmlDocument - -Version 3.1.03 : December 9, 2014 -- Added ChartUpdater.cs module -- Added ChartUpdater01.cs module, along with sample documents -- Added Test-OxPtCmdlets Cmdlet - -Version 3.1.02 : December 1, 2014 -- Added Add-DocxText Cmdlet - -Version 3.1.01 : November 23, 2014 -- Added Convert-DocxToHtml Cmdlet -- Added Chinese and Hebrew sample documents -- Cmdlets in this release - Clear-DocxTrackedRevision - Convert-DocxToHtml - ConvertFrom-Base64 - ConvertFrom-FlatOpc - ConvertTo-Base64 - ConvertTo-FlatOpc - Get-OpenXmlValidationErrors - Merge-Docx - New-Docx - Test-OpenXmlValid - -Version 3.1.00 : November 13, 2014 -- Changed installation process - no longer requires compilation using Visual Studio -- Added ConvertTo-FlatOpc Cmdlet -- Added ConvertFrom-FlatOpc Cmdlet -- Changed parameters for Test-OpenXmlValid, Get-OpenXmlValidationErrors -- Removed the unnecessary 1/2 second sleep when doing Word automation in the New-Docx Cmdlet - -Version 3.0.00 : October 29, 2014 -- New release of cmdlets that are written as 'Advanced Functions' instead of in C#. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/TestFiles/DB017-ApplyHeaderAndFooterToAllDocs-Landscape-SingleColumn.docx b/TestFiles/DB017-ApplyHeaderAndFooterToAllDocs-Landscape-SingleColumn.docx new file mode 100644 index 00000000..2f481f15 Binary files /dev/null and b/TestFiles/DB017-ApplyHeaderAndFooterToAllDocs-Landscape-SingleColumn.docx differ diff --git a/TestFiles/DB017-ApplyHeaderAndFooterToAllDocs-Portrait-TwoColumns.docx b/TestFiles/DB017-ApplyHeaderAndFooterToAllDocs-Portrait-TwoColumns.docx new file mode 100644 index 00000000..5af1995e Binary files /dev/null and b/TestFiles/DB017-ApplyHeaderAndFooterToAllDocs-Portrait-TwoColumns.docx differ diff --git a/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png b/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png new file mode 100644 index 00000000..894e819d Binary files /dev/null and b/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png differ diff --git a/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png.diff.win.png b/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..5b466ed2 Binary files /dev/null and b/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png.diff.win20250308224318.png b/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png.diff.win20250308224318.png new file mode 100644 index 00000000..71cc0fc6 Binary files /dev/null and b/TestFiles/HC001-5DayTourPlanTemplate.docxExpectation.png.diff.win20250308224318.png differ diff --git a/TestFiles/HC002-Hebrew-01.docxExpectation.png b/TestFiles/HC002-Hebrew-01.docxExpectation.png new file mode 100644 index 00000000..09105840 Binary files /dev/null and b/TestFiles/HC002-Hebrew-01.docxExpectation.png differ diff --git a/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win.png b/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..4e92c38a Binary files /dev/null and b/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win20250308224343.png b/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win20250308224343.png new file mode 100644 index 00000000..d3f34d53 Binary files /dev/null and b/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win20250308224343.png differ diff --git a/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win20250920081949.png b/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win20250920081949.png new file mode 100644 index 00000000..917dda93 Binary files /dev/null and b/TestFiles/HC002-Hebrew-01.docxExpectation.png.diff.win20250920081949.png differ diff --git a/TestFiles/HC003-Hebrew-02.docxExpectation.png b/TestFiles/HC003-Hebrew-02.docxExpectation.png new file mode 100644 index 00000000..ce46aa2c Binary files /dev/null and b/TestFiles/HC003-Hebrew-02.docxExpectation.png differ diff --git a/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win.png b/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..4feefc62 Binary files /dev/null and b/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win20250308224353.png b/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win20250308224353.png new file mode 100644 index 00000000..27e2ab28 Binary files /dev/null and b/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win20250308224353.png differ diff --git a/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win20250920081752.png b/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win20250920081752.png new file mode 100644 index 00000000..858e2935 Binary files /dev/null and b/TestFiles/HC003-Hebrew-02.docxExpectation.png.diff.win20250920081752.png differ diff --git a/TestFiles/HC004-ResumeTemplate.docxExpectation.png b/TestFiles/HC004-ResumeTemplate.docxExpectation.png new file mode 100644 index 00000000..a49cd57b Binary files /dev/null and b/TestFiles/HC004-ResumeTemplate.docxExpectation.png differ diff --git a/TestFiles/HC004-ResumeTemplate.docxExpectation.png.diff.win.png b/TestFiles/HC004-ResumeTemplate.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..3082fa37 Binary files /dev/null and b/TestFiles/HC004-ResumeTemplate.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC004-ResumeTemplate.docxExpectation.png.diff.win20250308224334.png b/TestFiles/HC004-ResumeTemplate.docxExpectation.png.diff.win20250308224334.png new file mode 100644 index 00000000..3b9e111a Binary files /dev/null and b/TestFiles/HC004-ResumeTemplate.docxExpectation.png.diff.win20250308224334.png differ diff --git a/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png b/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png new file mode 100644 index 00000000..515bdacc Binary files /dev/null and b/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png differ diff --git a/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png.diff.win.png b/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..6f63ab2b Binary files /dev/null and b/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png.diff.win20250308224407.png b/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png.diff.win20250308224407.png new file mode 100644 index 00000000..b27b3fc3 Binary files /dev/null and b/TestFiles/HC005-TaskPlanTemplate.docxExpectation.png.diff.win20250308224407.png differ diff --git a/TestFiles/HC006-Test-01.docxExpectation.png b/TestFiles/HC006-Test-01.docxExpectation.png new file mode 100644 index 00000000..fe4055be Binary files /dev/null and b/TestFiles/HC006-Test-01.docxExpectation.png differ diff --git a/TestFiles/HC006-Test-01.docxExpectation.png.diff.win.png b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..0848fda3 Binary files /dev/null and b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250308224330.png b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250308224330.png new file mode 100644 index 00000000..eec0d6da Binary files /dev/null and b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250308224330.png differ diff --git a/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920081803.png b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920081803.png new file mode 100644 index 00000000..dfab261d Binary files /dev/null and b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920081803.png differ diff --git a/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920082004.png b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920082004.png new file mode 100644 index 00000000..dfab261d Binary files /dev/null and b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920082004.png differ diff --git a/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920101006.png b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920101006.png new file mode 100644 index 00000000..7fa7ca95 Binary files /dev/null and b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920101006.png differ diff --git a/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920101057.png b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920101057.png new file mode 100644 index 00000000..7fa7ca95 Binary files /dev/null and b/TestFiles/HC006-Test-01.docxExpectation.png.diff.win20250920101057.png differ diff --git a/TestFiles/HC007-Test-02.docxExpectation.png b/TestFiles/HC007-Test-02.docxExpectation.png new file mode 100644 index 00000000..092fc3e8 Binary files /dev/null and b/TestFiles/HC007-Test-02.docxExpectation.png differ diff --git a/TestFiles/HC007-Test-02.docxExpectation.png.diff.win.png b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..3b57e6ed Binary files /dev/null and b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250308224304.png b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250308224304.png new file mode 100644 index 00000000..655eacd5 Binary files /dev/null and b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250308224304.png differ diff --git a/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250920081816.png b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250920081816.png new file mode 100644 index 00000000..bbec3924 Binary files /dev/null and b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250920081816.png differ diff --git a/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250920101008.png b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250920101008.png new file mode 100644 index 00000000..2edc77e6 Binary files /dev/null and b/TestFiles/HC007-Test-02.docxExpectation.png.diff.win20250920101008.png differ diff --git a/TestFiles/HC008-Test-03.docxExpectation.png b/TestFiles/HC008-Test-03.docxExpectation.png new file mode 100644 index 00000000..eec81096 Binary files /dev/null and b/TestFiles/HC008-Test-03.docxExpectation.png differ diff --git a/TestFiles/HC008-Test-03.docxExpectation.png.diff.win.png b/TestFiles/HC008-Test-03.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..2cd3afea Binary files /dev/null and b/TestFiles/HC008-Test-03.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC008-Test-03.docxExpectation.png.diff.win20250308223845.png b/TestFiles/HC008-Test-03.docxExpectation.png.diff.win20250308223845.png new file mode 100644 index 00000000..dc2c0285 Binary files /dev/null and b/TestFiles/HC008-Test-03.docxExpectation.png.diff.win20250308223845.png differ diff --git a/TestFiles/HC009-Test-04.docxExpectation.png b/TestFiles/HC009-Test-04.docxExpectation.png new file mode 100644 index 00000000..81b19c7d Binary files /dev/null and b/TestFiles/HC009-Test-04.docxExpectation.png differ diff --git a/TestFiles/HC009-Test-04.docxExpectation.png.diff.win.png b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..3cc68a7a Binary files /dev/null and b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250308224315.png b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250308224315.png new file mode 100644 index 00000000..33f71228 Binary files /dev/null and b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250308224315.png differ diff --git a/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250920081757.png b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250920081757.png new file mode 100644 index 00000000..c18cb562 Binary files /dev/null and b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250920081757.png differ diff --git a/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250920101001.png b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250920101001.png new file mode 100644 index 00000000..bb81421b Binary files /dev/null and b/TestFiles/HC009-Test-04.docxExpectation.png.diff.win20250920101001.png differ diff --git a/TestFiles/HC010-Test-05.docxExpectation.png b/TestFiles/HC010-Test-05.docxExpectation.png new file mode 100644 index 00000000..1044a784 Binary files /dev/null and b/TestFiles/HC010-Test-05.docxExpectation.png differ diff --git a/TestFiles/HC010-Test-05.docxExpectation.png.diff.win.png b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..a91a9709 Binary files /dev/null and b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250308224409.png b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250308224409.png new file mode 100644 index 00000000..30617c1f Binary files /dev/null and b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250308224409.png differ diff --git a/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250920081932.png b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250920081932.png new file mode 100644 index 00000000..d2861235 Binary files /dev/null and b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250920081932.png differ diff --git a/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250920101022.png b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250920101022.png new file mode 100644 index 00000000..dd03486e Binary files /dev/null and b/TestFiles/HC010-Test-05.docxExpectation.png.diff.win20250920101022.png differ diff --git a/TestFiles/HC011-Test-06.docxExpectation.png b/TestFiles/HC011-Test-06.docxExpectation.png new file mode 100644 index 00000000..7e6e54be Binary files /dev/null and b/TestFiles/HC011-Test-06.docxExpectation.png differ diff --git a/TestFiles/HC011-Test-06.docxExpectation.png.diff.win.png b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..9cc157c0 Binary files /dev/null and b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250308224339.png b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250308224339.png new file mode 100644 index 00000000..20d911ef Binary files /dev/null and b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250308224339.png differ diff --git a/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250920081859.png b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250920081859.png new file mode 100644 index 00000000..9a825e9d Binary files /dev/null and b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250920081859.png differ diff --git a/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250920101020.png b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250920101020.png new file mode 100644 index 00000000..e9ba4fc6 Binary files /dev/null and b/TestFiles/HC011-Test-06.docxExpectation.png.diff.win20250920101020.png differ diff --git a/TestFiles/HC012-Test-07.docxExpectation.png b/TestFiles/HC012-Test-07.docxExpectation.png new file mode 100644 index 00000000..7a367918 Binary files /dev/null and b/TestFiles/HC012-Test-07.docxExpectation.png differ diff --git a/TestFiles/HC012-Test-07.docxExpectation.png.diff.win.png b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..f809a3e1 Binary files /dev/null and b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250308224348.png b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250308224348.png new file mode 100644 index 00000000..9d42a14d Binary files /dev/null and b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250308224348.png differ diff --git a/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250920081738.png b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250920081738.png new file mode 100644 index 00000000..0b6b9c7b Binary files /dev/null and b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250920081738.png differ diff --git a/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250920100958.png b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250920100958.png new file mode 100644 index 00000000..a278cfb7 Binary files /dev/null and b/TestFiles/HC012-Test-07.docxExpectation.png.diff.win20250920100958.png differ diff --git a/TestFiles/HC013-Test-08.docxExpectation.png b/TestFiles/HC013-Test-08.docxExpectation.png new file mode 100644 index 00000000..c5350203 Binary files /dev/null and b/TestFiles/HC013-Test-08.docxExpectation.png differ diff --git a/TestFiles/HC014-RTL-Table-01.docxExpectation.png b/TestFiles/HC014-RTL-Table-01.docxExpectation.png new file mode 100644 index 00000000..6d1194f2 Binary files /dev/null and b/TestFiles/HC014-RTL-Table-01.docxExpectation.png differ diff --git a/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win.png b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..4eb9d63e Binary files /dev/null and b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250308224243.png b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250308224243.png new file mode 100644 index 00000000..87cf8f4d Binary files /dev/null and b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250308224243.png differ diff --git a/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250308224721.png b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250308224721.png new file mode 100644 index 00000000..87cf8f4d Binary files /dev/null and b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250308224721.png differ diff --git a/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250920081937.png b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250920081937.png new file mode 100644 index 00000000..aec71b03 Binary files /dev/null and b/TestFiles/HC014-RTL-Table-01.docxExpectation.png.diff.win20250920081937.png differ diff --git a/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png b/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png new file mode 100644 index 00000000..01b51c18 Binary files /dev/null and b/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png differ diff --git a/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png.diff.win.png b/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..00d3ff62 Binary files /dev/null and b/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png.diff.win20250308224341.png b/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png.diff.win20250308224341.png new file mode 100644 index 00000000..ecac4f11 Binary files /dev/null and b/TestFiles/HC015-Vertical-Spacing-atLeast.docxExpectation.png.diff.win20250308224341.png differ diff --git a/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png b/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png new file mode 100644 index 00000000..53cd64fd Binary files /dev/null and b/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png differ diff --git a/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png.diff.win.png b/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..fbb8dad9 Binary files /dev/null and b/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png.diff.win20250308224336.png b/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png.diff.win20250308224336.png new file mode 100644 index 00000000..7ad151cb Binary files /dev/null and b/TestFiles/HC016-Horizontal-Spacing-firstLine.docxExpectation.png.diff.win20250308224336.png differ diff --git a/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png b/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png new file mode 100644 index 00000000..c02cfd55 Binary files /dev/null and b/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png differ diff --git a/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png.diff.win.png b/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..ded83fa2 Binary files /dev/null and b/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png.diff.win20250308224404.png b/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png.diff.win20250308224404.png new file mode 100644 index 00000000..57390e15 Binary files /dev/null and b/TestFiles/HC017-Vertical-Alignment-Cell-01.docxExpectation.png.diff.win20250308224404.png differ diff --git a/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png b/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png new file mode 100644 index 00000000..afe353ba Binary files /dev/null and b/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png differ diff --git a/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png.diff.win.png b/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..0ffa7892 Binary files /dev/null and b/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png.diff.win20250308224400.png b/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png.diff.win20250308224400.png new file mode 100644 index 00000000..dd3419bc Binary files /dev/null and b/TestFiles/HC018-Vertical-Alignment-Para-01.docxExpectation.png.diff.win20250308224400.png differ diff --git a/TestFiles/HC019-Hidden-Run.docxExpectation.png b/TestFiles/HC019-Hidden-Run.docxExpectation.png new file mode 100644 index 00000000..17e15b25 Binary files /dev/null and b/TestFiles/HC019-Hidden-Run.docxExpectation.png differ diff --git a/TestFiles/HC019-Hidden-Run.docxExpectation.png.diff.win.png b/TestFiles/HC019-Hidden-Run.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..0e57a620 Binary files /dev/null and b/TestFiles/HC019-Hidden-Run.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC019-Hidden-Run.docxExpectation.png.diff.win20250308224352.png b/TestFiles/HC019-Hidden-Run.docxExpectation.png.diff.win20250308224352.png new file mode 100644 index 00000000..a9455346 Binary files /dev/null and b/TestFiles/HC019-Hidden-Run.docxExpectation.png.diff.win20250308224352.png differ diff --git a/TestFiles/HC020-Small-Caps.docxExpectation.png b/TestFiles/HC020-Small-Caps.docxExpectation.png new file mode 100644 index 00000000..3542007e Binary files /dev/null and b/TestFiles/HC020-Small-Caps.docxExpectation.png differ diff --git a/TestFiles/HC020-Small-Caps.docxExpectation.png.diff.win.png b/TestFiles/HC020-Small-Caps.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..66c471c6 Binary files /dev/null and b/TestFiles/HC020-Small-Caps.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC020-Small-Caps.docxExpectation.png.diff.win20250308224327.png b/TestFiles/HC020-Small-Caps.docxExpectation.png.diff.win20250308224327.png new file mode 100644 index 00000000..7ff61f8f Binary files /dev/null and b/TestFiles/HC020-Small-Caps.docxExpectation.png.diff.win20250308224327.png differ diff --git a/TestFiles/HC021-Symbols.docxExpectation.png b/TestFiles/HC021-Symbols.docxExpectation.png new file mode 100644 index 00000000..99c07688 Binary files /dev/null and b/TestFiles/HC021-Symbols.docxExpectation.png differ diff --git a/TestFiles/HC021-Symbols.docxExpectation.png.diff.win.png b/TestFiles/HC021-Symbols.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..e6456974 Binary files /dev/null and b/TestFiles/HC021-Symbols.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC021-Symbols.docxExpectation.png.diff.win20250308224311.png b/TestFiles/HC021-Symbols.docxExpectation.png.diff.win20250308224311.png new file mode 100644 index 00000000..05738731 Binary files /dev/null and b/TestFiles/HC021-Symbols.docxExpectation.png.diff.win20250308224311.png differ diff --git a/TestFiles/HC022-Table-Of-Contents.docxExpectation.png b/TestFiles/HC022-Table-Of-Contents.docxExpectation.png new file mode 100644 index 00000000..ddabf94b Binary files /dev/null and b/TestFiles/HC022-Table-Of-Contents.docxExpectation.png differ diff --git a/TestFiles/HC022-Table-Of-Contents.docxExpectation.png.diff.win.png b/TestFiles/HC022-Table-Of-Contents.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..ca57a162 Binary files /dev/null and b/TestFiles/HC022-Table-Of-Contents.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC022-Table-Of-Contents.docxExpectation.png.diff.win20250308224308.png b/TestFiles/HC022-Table-Of-Contents.docxExpectation.png.diff.win20250308224308.png new file mode 100644 index 00000000..1aaa2cf9 Binary files /dev/null and b/TestFiles/HC022-Table-Of-Contents.docxExpectation.png.diff.win20250308224308.png differ diff --git a/TestFiles/HC023-Hyperlink.docx b/TestFiles/HC023-Hyperlink.docx index ba2b2449..09c554fc 100644 Binary files a/TestFiles/HC023-Hyperlink.docx and b/TestFiles/HC023-Hyperlink.docx differ diff --git a/TestFiles/HC023-Hyperlink.docxExpectation.png b/TestFiles/HC023-Hyperlink.docxExpectation.png new file mode 100644 index 00000000..77de469e Binary files /dev/null and b/TestFiles/HC023-Hyperlink.docxExpectation.png differ diff --git a/TestFiles/HC023-Hyperlink.docxExpectation.png.diff.win.png b/TestFiles/HC023-Hyperlink.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..a8da1c80 Binary files /dev/null and b/TestFiles/HC023-Hyperlink.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC023-Hyperlink.docxExpectation.png.diff.win20250308224323.png b/TestFiles/HC023-Hyperlink.docxExpectation.png.diff.win20250308224323.png new file mode 100644 index 00000000..6db5e900 Binary files /dev/null and b/TestFiles/HC023-Hyperlink.docxExpectation.png.diff.win20250308224323.png differ diff --git a/TestFiles/HC024-Tabs-01.docxExpectation.png b/TestFiles/HC024-Tabs-01.docxExpectation.png new file mode 100644 index 00000000..0ae9b294 Binary files /dev/null and b/TestFiles/HC024-Tabs-01.docxExpectation.png differ diff --git a/TestFiles/HC024-Tabs-01.docxExpectation.png.diff.win.png b/TestFiles/HC024-Tabs-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..380270f8 Binary files /dev/null and b/TestFiles/HC024-Tabs-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC024-Tabs-01.docxExpectation.png.diff.win20250308224402.png b/TestFiles/HC024-Tabs-01.docxExpectation.png.diff.win20250308224402.png new file mode 100644 index 00000000..952fb0ae Binary files /dev/null and b/TestFiles/HC024-Tabs-01.docxExpectation.png.diff.win20250308224402.png differ diff --git a/TestFiles/HC025-Tabs-02.docxExpectation.png b/TestFiles/HC025-Tabs-02.docxExpectation.png new file mode 100644 index 00000000..0ae9b294 Binary files /dev/null and b/TestFiles/HC025-Tabs-02.docxExpectation.png differ diff --git a/TestFiles/HC025-Tabs-02.docxExpectation.png.diff.win.png b/TestFiles/HC025-Tabs-02.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..380270f8 Binary files /dev/null and b/TestFiles/HC025-Tabs-02.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC025-Tabs-02.docxExpectation.png.diff.win20250308224310.png b/TestFiles/HC025-Tabs-02.docxExpectation.png.diff.win20250308224310.png new file mode 100644 index 00000000..952fb0ae Binary files /dev/null and b/TestFiles/HC025-Tabs-02.docxExpectation.png.diff.win20250308224310.png differ diff --git a/TestFiles/HC026-Tabs-03.docxExpectation.png b/TestFiles/HC026-Tabs-03.docxExpectation.png new file mode 100644 index 00000000..d7740d41 Binary files /dev/null and b/TestFiles/HC026-Tabs-03.docxExpectation.png differ diff --git a/TestFiles/HC026-Tabs-03.docxExpectation.png.diff.win.png b/TestFiles/HC026-Tabs-03.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..d961c143 Binary files /dev/null and b/TestFiles/HC026-Tabs-03.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC026-Tabs-03.docxExpectation.png.diff.win20250308224302.png b/TestFiles/HC026-Tabs-03.docxExpectation.png.diff.win20250308224302.png new file mode 100644 index 00000000..bc9a2ced Binary files /dev/null and b/TestFiles/HC026-Tabs-03.docxExpectation.png.diff.win20250308224302.png differ diff --git a/TestFiles/HC027-Tabs-04.docxExpectation.png b/TestFiles/HC027-Tabs-04.docxExpectation.png new file mode 100644 index 00000000..a0ed38f0 Binary files /dev/null and b/TestFiles/HC027-Tabs-04.docxExpectation.png differ diff --git a/TestFiles/HC027-Tabs-04.docxExpectation.png.diff.win.png b/TestFiles/HC027-Tabs-04.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..40ccf267 Binary files /dev/null and b/TestFiles/HC027-Tabs-04.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC027-Tabs-04.docxExpectation.png.diff.win20250308224320.png b/TestFiles/HC027-Tabs-04.docxExpectation.png.diff.win20250308224320.png new file mode 100644 index 00000000..12447278 Binary files /dev/null and b/TestFiles/HC027-Tabs-04.docxExpectation.png.diff.win20250308224320.png differ diff --git a/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png b/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png new file mode 100644 index 00000000..b320d79e Binary files /dev/null and b/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png differ diff --git a/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png.diff.win.png b/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..47323dd4 Binary files /dev/null and b/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png.diff.win20250308224259.png b/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png.diff.win20250308224259.png new file mode 100644 index 00000000..65fd0673 Binary files /dev/null and b/TestFiles/HC028-No-Break-Hyphen.docxExpectation.png.diff.win20250308224259.png differ diff --git a/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png b/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png new file mode 100644 index 00000000..4249f19e Binary files /dev/null and b/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png differ diff --git a/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png.diff.win.png b/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..3d593ac7 Binary files /dev/null and b/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png.diff.win20250308224346.png b/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png.diff.win20250308224346.png new file mode 100644 index 00000000..ace6872f Binary files /dev/null and b/TestFiles/HC029-Table-Merged-Cells.docxExpectation.png.diff.win20250308224346.png differ diff --git a/TestFiles/HC030-Content-Controls.docxExpectation.png b/TestFiles/HC030-Content-Controls.docxExpectation.png new file mode 100644 index 00000000..ad64baed Binary files /dev/null and b/TestFiles/HC030-Content-Controls.docxExpectation.png differ diff --git a/TestFiles/HC030-Content-Controls.docxExpectation.png.diff.win.png b/TestFiles/HC030-Content-Controls.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..c42c30fc Binary files /dev/null and b/TestFiles/HC030-Content-Controls.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC030-Content-Controls.docxExpectation.png.diff.win20250308224256.png b/TestFiles/HC030-Content-Controls.docxExpectation.png.diff.win20250308224256.png new file mode 100644 index 00000000..3e3088b0 Binary files /dev/null and b/TestFiles/HC030-Content-Controls.docxExpectation.png.diff.win20250308224256.png differ diff --git a/TestFiles/HC031-Complicated-Document.docxExpectation.png b/TestFiles/HC031-Complicated-Document.docxExpectation.png new file mode 100644 index 00000000..6e4200e4 Binary files /dev/null and b/TestFiles/HC031-Complicated-Document.docxExpectation.png differ diff --git a/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win.png b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..38f64a5a Binary files /dev/null and b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250308224414.png b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250308224414.png new file mode 100644 index 00000000..dd148005 Binary files /dev/null and b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250308224414.png differ diff --git a/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250308224741.png b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250308224741.png new file mode 100644 index 00000000..dd148005 Binary files /dev/null and b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250308224741.png differ diff --git a/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250920081956.png b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250920081956.png new file mode 100644 index 00000000..22d36a7b Binary files /dev/null and b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250920081956.png differ diff --git a/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250920101033.png b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250920101033.png new file mode 100644 index 00000000..c64d413a Binary files /dev/null and b/TestFiles/HC031-Complicated-Document.docxExpectation.png.diff.win20250920101033.png differ diff --git a/TestFiles/HC032-Named-Color.docxExpectation.png b/TestFiles/HC032-Named-Color.docxExpectation.png new file mode 100644 index 00000000..e5a321a7 Binary files /dev/null and b/TestFiles/HC032-Named-Color.docxExpectation.png differ diff --git a/TestFiles/HC032-Named-Color.docxExpectation.png.diff.win.png b/TestFiles/HC032-Named-Color.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..1f0f9d28 Binary files /dev/null and b/TestFiles/HC032-Named-Color.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC032-Named-Color.docxExpectation.png.diff.win20250308224337.png b/TestFiles/HC032-Named-Color.docxExpectation.png.diff.win20250308224337.png new file mode 100644 index 00000000..895056fa Binary files /dev/null and b/TestFiles/HC032-Named-Color.docxExpectation.png.diff.win20250308224337.png differ diff --git a/TestFiles/HC033-Run-With-Border.docxExpectation.png b/TestFiles/HC033-Run-With-Border.docxExpectation.png new file mode 100644 index 00000000..7a7f0719 Binary files /dev/null and b/TestFiles/HC033-Run-With-Border.docxExpectation.png differ diff --git a/TestFiles/HC033-Run-With-Border.docxExpectation.png.diff.win.png b/TestFiles/HC033-Run-With-Border.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..b3b5e888 Binary files /dev/null and b/TestFiles/HC033-Run-With-Border.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC033-Run-With-Border.docxExpectation.png.diff.win20250308224253.png b/TestFiles/HC033-Run-With-Border.docxExpectation.png.diff.win20250308224253.png new file mode 100644 index 00000000..f49492b4 Binary files /dev/null and b/TestFiles/HC033-Run-With-Border.docxExpectation.png.diff.win20250308224253.png differ diff --git a/TestFiles/HC034-Run-With-Position.docxExpectation.png b/TestFiles/HC034-Run-With-Position.docxExpectation.png new file mode 100644 index 00000000..7bc5ccec Binary files /dev/null and b/TestFiles/HC034-Run-With-Position.docxExpectation.png differ diff --git a/TestFiles/HC034-Run-With-Position.docxExpectation.png.diff.win.png b/TestFiles/HC034-Run-With-Position.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..24484647 Binary files /dev/null and b/TestFiles/HC034-Run-With-Position.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC034-Run-With-Position.docxExpectation.png.diff.win20250308224254.png b/TestFiles/HC034-Run-With-Position.docxExpectation.png.diff.win20250308224254.png new file mode 100644 index 00000000..d74309e8 Binary files /dev/null and b/TestFiles/HC034-Run-With-Position.docxExpectation.png.diff.win20250308224254.png differ diff --git a/TestFiles/HC035-Strike-Through.docxExpectation.png b/TestFiles/HC035-Strike-Through.docxExpectation.png new file mode 100644 index 00000000..cdcd2877 Binary files /dev/null and b/TestFiles/HC035-Strike-Through.docxExpectation.png differ diff --git a/TestFiles/HC035-Strike-Through.docxExpectation.png.diff.win.png b/TestFiles/HC035-Strike-Through.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..ba50c33b Binary files /dev/null and b/TestFiles/HC035-Strike-Through.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC035-Strike-Through.docxExpectation.png.diff.win20250308224249.png b/TestFiles/HC035-Strike-Through.docxExpectation.png.diff.win20250308224249.png new file mode 100644 index 00000000..dbd5afa3 Binary files /dev/null and b/TestFiles/HC035-Strike-Through.docxExpectation.png.diff.win20250308224249.png differ diff --git a/TestFiles/HC036-Super-Script.docxExpectation.png b/TestFiles/HC036-Super-Script.docxExpectation.png new file mode 100644 index 00000000..66a278a4 Binary files /dev/null and b/TestFiles/HC036-Super-Script.docxExpectation.png differ diff --git a/TestFiles/HC036-Super-Script.docxExpectation.png.diff.win.png b/TestFiles/HC036-Super-Script.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..84ed8d20 Binary files /dev/null and b/TestFiles/HC036-Super-Script.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC036-Super-Script.docxExpectation.png.diff.win20250308224332.png b/TestFiles/HC036-Super-Script.docxExpectation.png.diff.win20250308224332.png new file mode 100644 index 00000000..c0d4b5b6 Binary files /dev/null and b/TestFiles/HC036-Super-Script.docxExpectation.png.diff.win20250308224332.png differ diff --git a/TestFiles/HC037-Sub-Script.docxExpectation.png b/TestFiles/HC037-Sub-Script.docxExpectation.png new file mode 100644 index 00000000..43b300e5 Binary files /dev/null and b/TestFiles/HC037-Sub-Script.docxExpectation.png differ diff --git a/TestFiles/HC037-Sub-Script.docxExpectation.png.diff.win.png b/TestFiles/HC037-Sub-Script.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..ed189edf Binary files /dev/null and b/TestFiles/HC037-Sub-Script.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC037-Sub-Script.docxExpectation.png.diff.win20250308224405.png b/TestFiles/HC037-Sub-Script.docxExpectation.png.diff.win20250308224405.png new file mode 100644 index 00000000..e64ea532 Binary files /dev/null and b/TestFiles/HC037-Sub-Script.docxExpectation.png.diff.win20250308224405.png differ diff --git a/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png b/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png new file mode 100644 index 00000000..ed434747 Binary files /dev/null and b/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png differ diff --git a/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png.diff.win.png b/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..786547cb Binary files /dev/null and b/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png.diff.win20250308224430.png b/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png.diff.win20250308224430.png new file mode 100644 index 00000000..935f555c Binary files /dev/null and b/TestFiles/HC038-Conflicting-Border-Weight.docxExpectation.png.diff.win20250308224430.png differ diff --git a/TestFiles/HC039-Bold.docxExpectation.png b/TestFiles/HC039-Bold.docxExpectation.png new file mode 100644 index 00000000..96bea0eb Binary files /dev/null and b/TestFiles/HC039-Bold.docxExpectation.png differ diff --git a/TestFiles/HC039-Bold.docxExpectation.png.diff.win.png b/TestFiles/HC039-Bold.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..ecd7303c Binary files /dev/null and b/TestFiles/HC039-Bold.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC039-Bold.docxExpectation.png.diff.win20250308224358.png b/TestFiles/HC039-Bold.docxExpectation.png.diff.win20250308224358.png new file mode 100644 index 00000000..40feae5c Binary files /dev/null and b/TestFiles/HC039-Bold.docxExpectation.png.diff.win20250308224358.png differ diff --git a/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png b/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png new file mode 100644 index 00000000..fff80c5a Binary files /dev/null and b/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png differ diff --git a/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png.diff.win.png b/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..d0054dec Binary files /dev/null and b/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png.diff.win20250308224251.png b/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png.diff.win20250308224251.png new file mode 100644 index 00000000..075457d9 Binary files /dev/null and b/TestFiles/HC040-Hyperlink-Fieldcode-01.docxExpectation.png.diff.win20250308224251.png differ diff --git a/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png b/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png new file mode 100644 index 00000000..d208850a Binary files /dev/null and b/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png differ diff --git a/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png.diff.win.png b/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..9624ede4 Binary files /dev/null and b/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png.diff.win20250308224350.png b/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png.diff.win20250308224350.png new file mode 100644 index 00000000..f80e8142 Binary files /dev/null and b/TestFiles/HC041-Hyperlink-Fieldcode-02.docxExpectation.png.diff.win20250308224350.png differ diff --git a/TestFiles/HC042-Image-Png.docxExpectation.png b/TestFiles/HC042-Image-Png.docxExpectation.png new file mode 100644 index 00000000..fd1bde50 Binary files /dev/null and b/TestFiles/HC042-Image-Png.docxExpectation.png differ diff --git a/TestFiles/HC042-Image-Png.docxExpectation.png.diff.win.png b/TestFiles/HC042-Image-Png.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..875487bc Binary files /dev/null and b/TestFiles/HC042-Image-Png.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC042-Image-Png.docxExpectation.png.diff.win20250308224357.png b/TestFiles/HC042-Image-Png.docxExpectation.png.diff.win20250308224357.png new file mode 100644 index 00000000..e1dcf63c Binary files /dev/null and b/TestFiles/HC042-Image-Png.docxExpectation.png.diff.win20250308224357.png differ diff --git a/TestFiles/HC043-Chart.docxExpectation.png b/TestFiles/HC043-Chart.docxExpectation.png new file mode 100644 index 00000000..c5350203 Binary files /dev/null and b/TestFiles/HC043-Chart.docxExpectation.png differ diff --git a/TestFiles/HC044-Embedded-Workbook.docxExpectation.png b/TestFiles/HC044-Embedded-Workbook.docxExpectation.png new file mode 100644 index 00000000..c5350203 Binary files /dev/null and b/TestFiles/HC044-Embedded-Workbook.docxExpectation.png differ diff --git a/TestFiles/HC045-Italic.docxExpectation.png b/TestFiles/HC045-Italic.docxExpectation.png new file mode 100644 index 00000000..59f7e830 Binary files /dev/null and b/TestFiles/HC045-Italic.docxExpectation.png differ diff --git a/TestFiles/HC045-Italic.docxExpectation.png.diff.win.png b/TestFiles/HC045-Italic.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..fabe1c09 Binary files /dev/null and b/TestFiles/HC045-Italic.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250308224425.png b/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250308224425.png new file mode 100644 index 00000000..81ce7ec1 Binary files /dev/null and b/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250308224425.png differ diff --git a/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250920081941.png b/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250920081941.png new file mode 100644 index 00000000..fb2fe1c3 Binary files /dev/null and b/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250920081941.png differ diff --git a/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250920101027.png b/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250920101027.png new file mode 100644 index 00000000..287b5c92 Binary files /dev/null and b/TestFiles/HC045-Italic.docxExpectation.png.diff.win20250920101027.png differ diff --git a/TestFiles/HC046-BoldAndItalic.docxExpectation.png b/TestFiles/HC046-BoldAndItalic.docxExpectation.png new file mode 100644 index 00000000..868292e9 Binary files /dev/null and b/TestFiles/HC046-BoldAndItalic.docxExpectation.png differ diff --git a/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win.png b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..e9b13b00 Binary files /dev/null and b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250308224313.png b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250308224313.png new file mode 100644 index 00000000..7a08bff5 Binary files /dev/null and b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250308224313.png differ diff --git a/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250920081834.png b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250920081834.png new file mode 100644 index 00000000..fae761be Binary files /dev/null and b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250920081834.png differ diff --git a/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250920101016.png b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250920101016.png new file mode 100644 index 00000000..0727b6a1 Binary files /dev/null and b/TestFiles/HC046-BoldAndItalic.docxExpectation.png.diff.win20250920101016.png differ diff --git a/TestFiles/HC047-No-Section.docxExpectation.png b/TestFiles/HC047-No-Section.docxExpectation.png new file mode 100644 index 00000000..707aa372 Binary files /dev/null and b/TestFiles/HC047-No-Section.docxExpectation.png differ diff --git a/TestFiles/HC047-No-Section.docxExpectation.png.diff.win.png b/TestFiles/HC047-No-Section.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..90afaafa Binary files /dev/null and b/TestFiles/HC047-No-Section.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC047-No-Section.docxExpectation.png.diff.win20250308224322.png b/TestFiles/HC047-No-Section.docxExpectation.png.diff.win20250308224322.png new file mode 100644 index 00000000..ae02f29a Binary files /dev/null and b/TestFiles/HC047-No-Section.docxExpectation.png.diff.win20250308224322.png differ diff --git a/TestFiles/HC048-Excerpt.docxExpectation.png b/TestFiles/HC048-Excerpt.docxExpectation.png new file mode 100644 index 00000000..6c0d0123 Binary files /dev/null and b/TestFiles/HC048-Excerpt.docxExpectation.png differ diff --git a/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win.png b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..1c26e341 Binary files /dev/null and b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250308224420.png b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250308224420.png new file mode 100644 index 00000000..b405cbc3 Binary files /dev/null and b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250308224420.png differ diff --git a/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250308224747.png b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250308224747.png new file mode 100644 index 00000000..b405cbc3 Binary files /dev/null and b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250308224747.png differ diff --git a/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250920081829.png b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250920081829.png new file mode 100644 index 00000000..e28d43e8 Binary files /dev/null and b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250920081829.png differ diff --git a/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250920101011.png b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250920101011.png new file mode 100644 index 00000000..5a3eeb35 Binary files /dev/null and b/TestFiles/HC048-Excerpt.docxExpectation.png.diff.win20250920101011.png differ diff --git a/TestFiles/HC049-Borders.docxExpectation.png b/TestFiles/HC049-Borders.docxExpectation.png new file mode 100644 index 00000000..872b0751 Binary files /dev/null and b/TestFiles/HC049-Borders.docxExpectation.png differ diff --git a/TestFiles/HC049-Borders.docxExpectation.png.diff.win.png b/TestFiles/HC049-Borders.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..f361efb1 Binary files /dev/null and b/TestFiles/HC049-Borders.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC049-Borders.docxExpectation.png.diff.win20250308224247.png b/TestFiles/HC049-Borders.docxExpectation.png.diff.win20250308224247.png new file mode 100644 index 00000000..010a712b Binary files /dev/null and b/TestFiles/HC049-Borders.docxExpectation.png.diff.win20250308224247.png differ diff --git a/TestFiles/HC050-Shaded-Text-01.docxExpectation.png b/TestFiles/HC050-Shaded-Text-01.docxExpectation.png new file mode 100644 index 00000000..15586bfd Binary files /dev/null and b/TestFiles/HC050-Shaded-Text-01.docxExpectation.png differ diff --git a/TestFiles/HC050-Shaded-Text-01.docxExpectation.png.diff.win.png b/TestFiles/HC050-Shaded-Text-01.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..24e74bff Binary files /dev/null and b/TestFiles/HC050-Shaded-Text-01.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC050-Shaded-Text-01.docxExpectation.png.diff.win20250308224258.png b/TestFiles/HC050-Shaded-Text-01.docxExpectation.png.diff.win20250308224258.png new file mode 100644 index 00000000..b9bfe98b Binary files /dev/null and b/TestFiles/HC050-Shaded-Text-01.docxExpectation.png.diff.win20250308224258.png differ diff --git a/TestFiles/HC051-Shaded-Text-02.docxExpectation.png b/TestFiles/HC051-Shaded-Text-02.docxExpectation.png new file mode 100644 index 00000000..2bc2c3a1 Binary files /dev/null and b/TestFiles/HC051-Shaded-Text-02.docxExpectation.png differ diff --git a/TestFiles/HC051-Shaded-Text-02.docxExpectation.png.diff.win.png b/TestFiles/HC051-Shaded-Text-02.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..637509fa Binary files /dev/null and b/TestFiles/HC051-Shaded-Text-02.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC051-Shaded-Text-02.docxExpectation.png.diff.win20250308224306.png b/TestFiles/HC051-Shaded-Text-02.docxExpectation.png.diff.win20250308224306.png new file mode 100644 index 00000000..2d0332ae Binary files /dev/null and b/TestFiles/HC051-Shaded-Text-02.docxExpectation.png.diff.win20250308224306.png differ diff --git a/TestFiles/HC052-SmartArt.docx b/TestFiles/HC052-SmartArt.docx index 09599ddb..dfe6a480 100644 Binary files a/TestFiles/HC052-SmartArt.docx and b/TestFiles/HC052-SmartArt.docx differ diff --git a/TestFiles/HC052-SmartArt.docxExpectation.png b/TestFiles/HC052-SmartArt.docxExpectation.png new file mode 100644 index 00000000..02ffcaef Binary files /dev/null and b/TestFiles/HC052-SmartArt.docxExpectation.png differ diff --git a/TestFiles/HC052-SmartArt.docxExpectation.png.diff.win.png b/TestFiles/HC052-SmartArt.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..2a38c405 Binary files /dev/null and b/TestFiles/HC052-SmartArt.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC052-SmartArt.docxExpectation.png.diff.win20250308224428.png b/TestFiles/HC052-SmartArt.docxExpectation.png.diff.win20250308224428.png new file mode 100644 index 00000000..a30910fc Binary files /dev/null and b/TestFiles/HC052-SmartArt.docxExpectation.png.diff.win20250308224428.png differ diff --git a/TestFiles/HC053-Headings.docx b/TestFiles/HC053-Headings.docx new file mode 100644 index 00000000..b3ef33e5 Binary files /dev/null and b/TestFiles/HC053-Headings.docx differ diff --git a/TestFiles/HC053-Headings.docxExpectation.png b/TestFiles/HC053-Headings.docxExpectation.png new file mode 100644 index 00000000..2ae29341 Binary files /dev/null and b/TestFiles/HC053-Headings.docxExpectation.png differ diff --git a/TestFiles/HC053-Headings.docxExpectation.png.diff.win.png b/TestFiles/HC053-Headings.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..acc16881 Binary files /dev/null and b/TestFiles/HC053-Headings.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250308224325.png b/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250308224325.png new file mode 100644 index 00000000..c58aeee3 Binary files /dev/null and b/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250308224325.png differ diff --git a/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250920081845.png b/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250920081845.png new file mode 100644 index 00000000..8beea3f1 Binary files /dev/null and b/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250920081845.png differ diff --git a/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250920101018.png b/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250920101018.png new file mode 100644 index 00000000..6e9f8642 Binary files /dev/null and b/TestFiles/HC053-Headings.docxExpectation.png.diff.win20250920101018.png differ diff --git a/TestFiles/HC055-GoogleDocsExport.docx b/TestFiles/HC055-GoogleDocsExport.docx new file mode 100644 index 00000000..8c28c871 Binary files /dev/null and b/TestFiles/HC055-GoogleDocsExport.docx differ diff --git a/TestFiles/HC055-GoogleDocsExport.docxExpectation.png b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png new file mode 100644 index 00000000..350ce45e Binary files /dev/null and b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png differ diff --git a/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win.png b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..0fe29b4c Binary files /dev/null and b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250308224412.png b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250308224412.png new file mode 100644 index 00000000..483578d6 Binary files /dev/null and b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250308224412.png differ diff --git a/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250920081935.png b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250920081935.png new file mode 100644 index 00000000..8363d52c Binary files /dev/null and b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250920081935.png differ diff --git a/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250920101025.png b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250920101025.png new file mode 100644 index 00000000..7fd2d84f Binary files /dev/null and b/TestFiles/HC055-GoogleDocsExport.docxExpectation.png.diff.win20250920101025.png differ diff --git a/TestFiles/HC060-Image-with-Hyperlink.docxExpectation.png b/TestFiles/HC060-Image-with-Hyperlink.docxExpectation.png new file mode 100644 index 00000000..84f05ed1 Binary files /dev/null and b/TestFiles/HC060-Image-with-Hyperlink.docxExpectation.png differ diff --git a/TestFiles/HC060-Image-with-Hyperlink.docxExpectation.png.diff.win.png b/TestFiles/HC060-Image-with-Hyperlink.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..7b17fc13 Binary files /dev/null and b/TestFiles/HC060-Image-with-Hyperlink.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png new file mode 100644 index 00000000..0b31d528 Binary files /dev/null and b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png differ diff --git a/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win.png b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..4cd7bed0 Binary files /dev/null and b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250308224426.png b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250308224426.png new file mode 100644 index 00000000..f7c1cdfe Binary files /dev/null and b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250308224426.png differ diff --git a/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250920081945.png b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250920081945.png new file mode 100644 index 00000000..8c0d8a86 Binary files /dev/null and b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250920081945.png differ diff --git a/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250920101029.png b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250920101029.png new file mode 100644 index 00000000..b646cf15 Binary files /dev/null and b/TestFiles/HC061-Hyperlink-in-Field.docxExpectation.png.diff.win20250920101029.png differ diff --git a/TestFiles/SH001-Table.xlsx b/TestFiles/SH001-Table.xlsx index 139a02c2..e93209ea 100644 Binary files a/TestFiles/SH001-Table.xlsx and b/TestFiles/SH001-Table.xlsx differ diff --git a/TestFiles/SH002-TwoTablesTwoSheets.xlsx b/TestFiles/SH002-TwoTablesTwoSheets.xlsx index a26e02f9..bc21d38e 100644 Binary files a/TestFiles/SH002-TwoTablesTwoSheets.xlsx and b/TestFiles/SH002-TwoTablesTwoSheets.xlsx differ diff --git a/TestFiles/SH003-TableWithDateInFirstColumn.xlsx b/TestFiles/SH003-TableWithDateInFirstColumn.xlsx index 3e99ac65..f4346143 100644 Binary files a/TestFiles/SH003-TableWithDateInFirstColumn.xlsx and b/TestFiles/SH003-TableWithDateInFirstColumn.xlsx differ diff --git a/TestFiles/SH004-TableAtOffsetLocation.xlsx b/TestFiles/SH004-TableAtOffsetLocation.xlsx index 7e04bc71..ccfe06a2 100644 Binary files a/TestFiles/SH004-TableAtOffsetLocation.xlsx and b/TestFiles/SH004-TableAtOffsetLocation.xlsx differ diff --git a/TestFiles/SH005-Table-With-SharedStrings.xlsx b/TestFiles/SH005-Table-With-SharedStrings.xlsx index 0ad1fe50..5dd6a85f 100644 Binary files a/TestFiles/SH005-Table-With-SharedStrings.xlsx and b/TestFiles/SH005-Table-With-SharedStrings.xlsx differ diff --git a/TestFiles/SH006-Table-No-SharedStrings.xlsx b/TestFiles/SH006-Table-No-SharedStrings.xlsx index ecf16a41..017b118a 100644 Binary files a/TestFiles/SH006-Table-No-SharedStrings.xlsx and b/TestFiles/SH006-Table-No-SharedStrings.xlsx differ diff --git a/TestFiles/SH007-One-Cell-Table.xlsx b/TestFiles/SH007-One-Cell-Table.xlsx index 7aaf9c67..b0234576 100644 Binary files a/TestFiles/SH007-One-Cell-Table.xlsx and b/TestFiles/SH007-One-Cell-Table.xlsx differ diff --git a/TestFiles/SH008-Table-With-Tall-Row.xlsx b/TestFiles/SH008-Table-With-Tall-Row.xlsx index 03cfbb9c..b60606b9 100644 Binary files a/TestFiles/SH008-Table-With-Tall-Row.xlsx and b/TestFiles/SH008-Table-With-Tall-Row.xlsx differ diff --git a/TestFiles/SH009-Table-With-Wide-Column.xlsx b/TestFiles/SH009-Table-With-Wide-Column.xlsx index 8faf19f5..9a3e4a4a 100644 Binary files a/TestFiles/SH009-Table-With-Wide-Column.xlsx and b/TestFiles/SH009-Table-With-Wide-Column.xlsx differ diff --git a/TestFiles/SH101-SimpleFormats.xlsx b/TestFiles/SH101-SimpleFormats.xlsx index 70f7ebb0..d5ba2951 100644 Binary files a/TestFiles/SH101-SimpleFormats.xlsx and b/TestFiles/SH101-SimpleFormats.xlsx differ diff --git a/TestFiles/SH102-9-x-9.xlsx b/TestFiles/SH102-9-x-9.xlsx index 049afeb2..e35b1dcc 100644 Binary files a/TestFiles/SH102-9-x-9.xlsx and b/TestFiles/SH102-9-x-9.xlsx differ diff --git a/TestFiles/SH103-No-SharedString.xlsx b/TestFiles/SH103-No-SharedString.xlsx index 4178be7a..abdc76e2 100644 Binary files a/TestFiles/SH103-No-SharedString.xlsx and b/TestFiles/SH103-No-SharedString.xlsx differ diff --git a/TestFiles/SH104-With-SharedString.xlsx b/TestFiles/SH104-With-SharedString.xlsx index ef09990d..825d4709 100644 Binary files a/TestFiles/SH104-With-SharedString.xlsx and b/TestFiles/SH104-With-SharedString.xlsx differ diff --git a/TestFiles/SH105-No-SharedString.xlsx b/TestFiles/SH105-No-SharedString.xlsx index 779a0b40..a70047e9 100644 Binary files a/TestFiles/SH105-No-SharedString.xlsx and b/TestFiles/SH105-No-SharedString.xlsx differ diff --git a/TestFiles/SH106-9-x-9-Formatted.xlsx b/TestFiles/SH106-9-x-9-Formatted.xlsx index 0e28e315..ff0b6c85 100644 Binary files a/TestFiles/SH106-9-x-9-Formatted.xlsx and b/TestFiles/SH106-9-x-9-Formatted.xlsx differ diff --git a/TestFiles/SH107-9-x-9-Formatted-Table.xlsx b/TestFiles/SH107-9-x-9-Formatted-Table.xlsx index c6dbd340..22583be7 100644 Binary files a/TestFiles/SH107-9-x-9-Formatted-Table.xlsx and b/TestFiles/SH107-9-x-9-Formatted-Table.xlsx differ diff --git a/TestFiles/SH108-SimpleFormattedCell.xlsx b/TestFiles/SH108-SimpleFormattedCell.xlsx index 78924e22..6f3d08a9 100644 Binary files a/TestFiles/SH108-SimpleFormattedCell.xlsx and b/TestFiles/SH108-SimpleFormattedCell.xlsx differ diff --git a/TestFiles/SH109-CellWithBorder.xlsx b/TestFiles/SH109-CellWithBorder.xlsx index 986ab1ae..2e734056 100644 Binary files a/TestFiles/SH109-CellWithBorder.xlsx and b/TestFiles/SH109-CellWithBorder.xlsx differ diff --git a/TestFiles/SH110-CellWithMasterStyle.xlsx b/TestFiles/SH110-CellWithMasterStyle.xlsx index 11110359..99a69c0a 100644 Binary files a/TestFiles/SH110-CellWithMasterStyle.xlsx and b/TestFiles/SH110-CellWithMasterStyle.xlsx differ diff --git a/TestFiles/SH111-ChangedDefaultColumnWidth.xlsx b/TestFiles/SH111-ChangedDefaultColumnWidth.xlsx index bfa0b03e..ac88d145 100644 Binary files a/TestFiles/SH111-ChangedDefaultColumnWidth.xlsx and b/TestFiles/SH111-ChangedDefaultColumnWidth.xlsx differ diff --git a/TestFiles/SH112-NotVertMergedCell.xlsx b/TestFiles/SH112-NotVertMergedCell.xlsx index e2c19819..dfef9d8a 100644 Binary files a/TestFiles/SH112-NotVertMergedCell.xlsx and b/TestFiles/SH112-NotVertMergedCell.xlsx differ diff --git a/TestFiles/SH113-VertMergedCell.xlsx b/TestFiles/SH113-VertMergedCell.xlsx index ae200e27..53d735d9 100644 Binary files a/TestFiles/SH113-VertMergedCell.xlsx and b/TestFiles/SH113-VertMergedCell.xlsx differ diff --git a/TestFiles/SH114-Centered-Cell.xlsx b/TestFiles/SH114-Centered-Cell.xlsx index e092b5dd..794c2f83 100644 Binary files a/TestFiles/SH114-Centered-Cell.xlsx and b/TestFiles/SH114-Centered-Cell.xlsx differ diff --git a/TestFiles/SH115-DigitsToRight.xlsx b/TestFiles/SH115-DigitsToRight.xlsx index fcefedbf..0dc3fd40 100644 Binary files a/TestFiles/SH115-DigitsToRight.xlsx and b/TestFiles/SH115-DigitsToRight.xlsx differ diff --git a/TestFiles/SH116-FmtNumId-1.xlsx b/TestFiles/SH116-FmtNumId-1.xlsx index 5370d8c1..542b8532 100644 Binary files a/TestFiles/SH116-FmtNumId-1.xlsx and b/TestFiles/SH116-FmtNumId-1.xlsx differ diff --git a/TestFiles/SH117-FmtNumId-2.xlsx b/TestFiles/SH117-FmtNumId-2.xlsx index 4650658b..6e59413b 100644 Binary files a/TestFiles/SH117-FmtNumId-2.xlsx and b/TestFiles/SH117-FmtNumId-2.xlsx differ diff --git a/TestFiles/SH118-FmtNumId-3.xlsx b/TestFiles/SH118-FmtNumId-3.xlsx index 143d897b..3fd696d0 100644 Binary files a/TestFiles/SH118-FmtNumId-3.xlsx and b/TestFiles/SH118-FmtNumId-3.xlsx differ diff --git a/TestFiles/SH119-FmtNumId-4.xlsx b/TestFiles/SH119-FmtNumId-4.xlsx index 4454a2ce..8fb7a5bd 100644 Binary files a/TestFiles/SH119-FmtNumId-4.xlsx and b/TestFiles/SH119-FmtNumId-4.xlsx differ diff --git a/TestFiles/SH120-FmtNumId-9.xlsx b/TestFiles/SH120-FmtNumId-9.xlsx index a6bbd715..2f4cddee 100644 Binary files a/TestFiles/SH120-FmtNumId-9.xlsx and b/TestFiles/SH120-FmtNumId-9.xlsx differ diff --git a/TestFiles/SH121-FmtNumId-11.xlsx b/TestFiles/SH121-FmtNumId-11.xlsx index 3717ebd3..ddab4a9c 100644 Binary files a/TestFiles/SH121-FmtNumId-11.xlsx and b/TestFiles/SH121-FmtNumId-11.xlsx differ diff --git a/TestFiles/SH122-FmtNumId-12.xlsx b/TestFiles/SH122-FmtNumId-12.xlsx index bc632c67..c0ed5c61 100644 Binary files a/TestFiles/SH122-FmtNumId-12.xlsx and b/TestFiles/SH122-FmtNumId-12.xlsx differ diff --git a/TestFiles/SH123-FmtNumId-14.xlsx b/TestFiles/SH123-FmtNumId-14.xlsx index 5b9a9b03..968274c4 100644 Binary files a/TestFiles/SH123-FmtNumId-14.xlsx and b/TestFiles/SH123-FmtNumId-14.xlsx differ diff --git a/TestFiles/SH124-FmtNumId-15.xlsx b/TestFiles/SH124-FmtNumId-15.xlsx index 5b59fc6a..11243c67 100644 Binary files a/TestFiles/SH124-FmtNumId-15.xlsx and b/TestFiles/SH124-FmtNumId-15.xlsx differ diff --git a/TestFiles/SH125-FmtNumId-16.xlsx b/TestFiles/SH125-FmtNumId-16.xlsx index 895e4e32..377263f3 100644 Binary files a/TestFiles/SH125-FmtNumId-16.xlsx and b/TestFiles/SH125-FmtNumId-16.xlsx differ diff --git a/TestFiles/SH126-FmtNumId-17.xlsx b/TestFiles/SH126-FmtNumId-17.xlsx index 97913f5f..0adc04e1 100644 Binary files a/TestFiles/SH126-FmtNumId-17.xlsx and b/TestFiles/SH126-FmtNumId-17.xlsx differ diff --git a/TestFiles/SH127-FmtNumId-18.xlsx b/TestFiles/SH127-FmtNumId-18.xlsx index a4781ea9..2e4b09b6 100644 Binary files a/TestFiles/SH127-FmtNumId-18.xlsx and b/TestFiles/SH127-FmtNumId-18.xlsx differ diff --git a/TestFiles/SH128-FmtNumId-19.xlsx b/TestFiles/SH128-FmtNumId-19.xlsx index 6da92aa0..33f5f37b 100644 Binary files a/TestFiles/SH128-FmtNumId-19.xlsx and b/TestFiles/SH128-FmtNumId-19.xlsx differ diff --git a/TestFiles/SH129-FmtNumId-20.xlsx b/TestFiles/SH129-FmtNumId-20.xlsx index 66255d0c..5e8b2574 100644 Binary files a/TestFiles/SH129-FmtNumId-20.xlsx and b/TestFiles/SH129-FmtNumId-20.xlsx differ diff --git a/TestFiles/SH130-FmtNumId-21.xlsx b/TestFiles/SH130-FmtNumId-21.xlsx index 8df8198b..062a3488 100644 Binary files a/TestFiles/SH130-FmtNumId-21.xlsx and b/TestFiles/SH130-FmtNumId-21.xlsx differ diff --git a/TestFiles/SH131-FmtNumId-22.xlsx b/TestFiles/SH131-FmtNumId-22.xlsx index 8c51aa0a..184795e3 100644 Binary files a/TestFiles/SH131-FmtNumId-22.xlsx and b/TestFiles/SH131-FmtNumId-22.xlsx differ diff --git a/TestFiles/SH151-Custom-Cell-Format-Currency.xlsx b/TestFiles/SH151-Custom-Cell-Format-Currency.xlsx index 36d29d4f..7a20c5c7 100644 Binary files a/TestFiles/SH151-Custom-Cell-Format-Currency.xlsx and b/TestFiles/SH151-Custom-Cell-Format-Currency.xlsx differ diff --git a/TestFiles/SH152-Custom-Cell-Format.xlsx b/TestFiles/SH152-Custom-Cell-Format.xlsx index cddeb579..b1e576a9 100644 Binary files a/TestFiles/SH152-Custom-Cell-Format.xlsx and b/TestFiles/SH152-Custom-Cell-Format.xlsx differ diff --git a/TestFiles/SH201-Cell-C1-Without-R-Attr.xlsx b/TestFiles/SH201-Cell-C1-Without-R-Attr.xlsx index e26b2576..a4482419 100644 Binary files a/TestFiles/SH201-Cell-C1-Without-R-Attr.xlsx and b/TestFiles/SH201-Cell-C1-Without-R-Attr.xlsx differ diff --git a/TestFiles/SH202-Cell-C1-D1-Without-R-Attr.xlsx b/TestFiles/SH202-Cell-C1-D1-Without-R-Attr.xlsx index 2fd22661..9e65a459 100644 Binary files a/TestFiles/SH202-Cell-C1-D1-Without-R-Attr.xlsx and b/TestFiles/SH202-Cell-C1-D1-Without-R-Attr.xlsx differ diff --git a/TestFiles/SH203-Cell-C1-D1-E1-Without-R-Attr.xlsx b/TestFiles/SH203-Cell-C1-D1-E1-Without-R-Attr.xlsx index 8409213f..6919c4d0 100644 Binary files a/TestFiles/SH203-Cell-C1-D1-E1-Without-R-Attr.xlsx and b/TestFiles/SH203-Cell-C1-D1-E1-Without-R-Attr.xlsx differ diff --git a/TestFiles/SH204-Cell-A1-B1-C1-Without-R-Attr.xlsx b/TestFiles/SH204-Cell-A1-B1-C1-Without-R-Attr.xlsx index edc36328..15f1702e 100644 Binary files a/TestFiles/SH204-Cell-A1-B1-C1-Without-R-Attr.xlsx and b/TestFiles/SH204-Cell-A1-B1-C1-Without-R-Attr.xlsx differ diff --git a/TestFiles/Spreadsheet.xlsx b/TestFiles/Spreadsheet.xlsx index 8bf78f3a..a135ffab 100644 Binary files a/TestFiles/Spreadsheet.xlsx and b/TestFiles/Spreadsheet.xlsx differ diff --git a/TestFiles/T0013.html b/TestFiles/T0013.html index 2e8bb44a..1637fc1e 100644 --- a/TestFiles/T0013.html +++ b/TestFiles/T0013.html @@ -1,17 +1,20 @@ + - + +

    Paragraph 1 with s text and some more text.

    -
    +

    Paragraph 1 with strong text and some more text.

    -
    +

    Paragraph 1 with sub text and some more text.

    -
    +

    Paragraph 1 with sup text and some more text.

    -
    +

    Paragraph 1 with u text and some more text.

    + \ No newline at end of file diff --git a/TestFiles/T0921_files/img.PNG b/TestFiles/T0921_files/img.png similarity index 100% rename from TestFiles/T0921_files/img.PNG rename to TestFiles/T0921_files/img.png diff --git a/TestFiles/T0922_files/img.PNG b/TestFiles/T0922_files/img.png similarity index 100% rename from TestFiles/T0922_files/img.PNG rename to TestFiles/T0922_files/img.png diff --git a/TestFiles/T0924_files/img.PNG b/TestFiles/T0923_files/img.png similarity index 100% rename from TestFiles/T0924_files/img.PNG rename to TestFiles/T0923_files/img.png diff --git a/TestFiles/T0925_files/img.PNG b/TestFiles/T0924_files/img.png similarity index 100% rename from TestFiles/T0925_files/img.PNG rename to TestFiles/T0924_files/img.png diff --git a/TestFiles/T0925_files/img.png b/TestFiles/T0925_files/img.png new file mode 100644 index 00000000..f37ad459 Binary files /dev/null and b/TestFiles/T0925_files/img.png differ diff --git a/TestFiles/Tabs.docx b/TestFiles/Tabs.docx new file mode 100644 index 00000000..2445dbd9 Binary files /dev/null and b/TestFiles/Tabs.docx differ diff --git a/TestFiles/Tabs.docxExpectation.png b/TestFiles/Tabs.docxExpectation.png new file mode 100644 index 00000000..8aee6a2a Binary files /dev/null and b/TestFiles/Tabs.docxExpectation.png differ diff --git a/TestFiles/Tabs.docxExpectation.png.diff.win.png b/TestFiles/Tabs.docxExpectation.png.diff.win.png new file mode 100644 index 00000000..59c499c2 Binary files /dev/null and b/TestFiles/Tabs.docxExpectation.png.diff.win.png differ diff --git a/TestFiles/Tabs.docxExpectation.png.diff.win20250308224328.png b/TestFiles/Tabs.docxExpectation.png.diff.win20250308224328.png new file mode 100644 index 00000000..8ef74209 Binary files /dev/null and b/TestFiles/Tabs.docxExpectation.png.diff.win20250308224328.png differ diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 03645f09..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,12 +0,0 @@ -os: Visual Studio 2017 - -environment: - image: Visual Studio 2017 - Configuration: Release - -build_script: - - cmd: dotnet restore - - cmd: dotnet build - -test_script: - - cmd: dotnet test .\OpenXmlPowerTools.Tests diff --git a/cla.md b/cla.md new file mode 100644 index 00000000..7f486dd1 --- /dev/null +++ b/cla.md @@ -0,0 +1,55 @@ +# Codeuctivity Individual Contributor License Agreement + +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”). + +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. + +**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. + +**Patent License.** You hereby grant, and agree to grant, to Codeuctivity a non-exclusive, perpetual, irrevocable, +worldwide, fully-paid, royalty-free, transferable patent license to make, have made, use, offer to sell, sell, +import, and otherwise transfer your Contributions, where such license applies only to those patent claims +licensable by you that are necessarily infringed by your Contributions alone or by combination of your +Contributions with the Project to which such Contributions were submitted, with the right to sublicense the +foregoing rights through multiple tiers of sublicensees. + +**Moral Rights.** To the fullest extent permitted under applicable law, you hereby waive, and agree not to +assert, all of your “moral rights” in or relating to your Contributions for the benefit of Codeuctivity, its assigns, and +their respective direct and indirect sublicensees. + +**Third Party Content/Rights.** If your Contribution includes or is based on any source code, object code, bug +fixes, configuration changes, tools, specifications, documentation, data, materials, feedback, information or +other works of authorship that were not authored by you (“Third Party Content”) or if you are aware of any +third party intellectual property or proprietary rights associated with your Contribution (“Third Party Rights”), +then you agree to include with the submission of your Contribution full details respecting such Third Party +Content and Third Party Rights, including, without limitation, identification of which aspects of your +Contribution contain Third Party Content or are associated with Third Party Rights, the owner/author of the +Third Party Content and Third Party Rights, where you obtained the Third Party Content, and any applicable +third party license terms or restrictions respecting the Third Party Content and Third Party Rights. For greater +certainty, the foregoing obligations respecting the identification of Third Party Content and Third Party Rights +do not apply to any portion of a Project that is incorporated into your Contribution to that same Project. + +**Representations.** You represent that, other than the Third Party Content and Third Party Rights identified by +you in accordance with this Agreement, you are the sole author of your Contributions and are legally entitled +to grant the foregoing licenses and waivers in respect of your Contributions. If your Contributions were +created in the course of your employment with your past or present employer(s), you represent that such +employer(s) has authorized you to make your Contributions on behalf of such employer(s) or such employer +(s) has waived all of their right, title or interest in or to your Contributions. + +**Disclaimer.** To the fullest extent permitted under applicable law, your Contributions are provided on an "asis" +basis, without any warranties or conditions, express or implied, including, without limitation, any implied +warranties or conditions of non-infringement, merchantability or fitness for a particular purpose. You are not +required to provide support for your Contributions, except to the extent you desire to provide support. + +**No Obligation.** You acknowledge that Codeuctivity is under no obligation to use or incorporate your Contributions +into any of the Projects. The decision to use or incorporate your Contributions into any of the Projects will be +made at the sole discretion of Codeuctivity or its authorized delegates. + +**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, +other than such principles directing application of Austrian law. The parties hereby submit to venue in, and +jurisdiction of the courts located in Vienna, Austria for purposes relating to this Agreement. In the event +that any of the provisions of this Agreement shall be held by a court or other tribunal of competent jurisdiction +to be unenforceable, the remaining portions hereof shall remain in full force and effect. + +**Assignment.** You agree that Codeuctivity may assign this Agreement, and all of its rights, obligations and licenses +hereunder. diff --git a/rules.ruleset b/rules.ruleset deleted file mode 100644 index dac47ac1..00000000 --- a/rules.ruleset +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/stylecop.json b/stylecop.json deleted file mode 100644 index 5df7f1db..00000000 --- a/stylecop.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "documentExposedElements": false, - "documentInternalElements": false, - "companyName": "Microsoft", - "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", - "xmlHeader": false, - "variables": { - "licenseName": "MIT", - "licenseFile": "LICENSE" - } - }, - "layoutRules": { - "newlineAtEndOfFile": "require" - }, - "indentation": { - "indentationSize": 4, - "useTabs": false - }, - "namingRules": {}, - "orderingRules": { - "blankLinesBetweenUsingGroups": "require", - "systemUsingDirectivesFirst": true, - "usingDirectivesPlacement": "outsideNamespace", - "elementOrder": [ - "kind", - "constant", - "accessibility", - "static", - "readonly" - ] - } - } -} diff --git a/testenvironments.json b/testenvironments.json new file mode 100644 index 00000000..539ead6b --- /dev/null +++ b/testenvironments.json @@ -0,0 +1,10 @@ +{ + "version": "1", + "environments": [ + { + "name": "Ubuntu", + "type": "wsl", + "wslDistribution": "Ubuntu" + } + ] +}