├── .gitattributes ├── .gitignore ├── .nuke ├── LICENSE ├── README.md ├── ReportDotNet.sln ├── ReportDotNet.sln.DotSettings ├── build.cmd ├── build.ps1 ├── build.sh ├── build ├── .editorconfig ├── Build.cs ├── _build.csproj └── _build.csproj.DotSettings ├── gif.gif ├── src ├── ReportDotNet.Core │ ├── Alignment.cs │ ├── BorderStyle.cs │ ├── Borders.cs │ ├── Cell.cs │ ├── CellBuilder.cs │ ├── CellParameters.cs │ ├── Create.cs │ ├── Factories.cs │ ├── Field.cs │ ├── FieldPart.cs │ ├── Helpers.cs │ ├── IDocument.cs │ ├── IPageElement.cs │ ├── Page.cs │ ├── PageBuilder.cs │ ├── PageLayout.cs │ ├── PageOrientation.cs │ ├── PageParameters.cs │ ├── PageSize.cs │ ├── Paragraph.cs │ ├── ParagraphBuilder.cs │ ├── ParagraphParameters.cs │ ├── Part.cs │ ├── Picture.cs │ ├── PictureBuilder.cs │ ├── PictureParameters.cs │ ├── PicturePart.cs │ ├── ReportDotNet.Core.csproj │ ├── Row.cs │ ├── RowBuilder.cs │ ├── RowHeightType.cs │ ├── RowParameters.cs │ ├── StubPicture.cs │ ├── StubPictureBuilder.cs │ ├── StubPictureParameters.cs │ ├── StubPicturePart.cs │ ├── Table.cs │ ├── TableBuilder.cs │ ├── TableParameters.cs │ ├── TextDirection.cs │ ├── TextPart.cs │ └── VerticalAlignment.cs ├── ReportDotNet.Docx │ ├── Converters │ │ ├── CellConverter.cs │ │ ├── PageConverter.cs │ │ ├── ParagraphConverter.cs │ │ ├── PictureConverter.cs │ │ ├── RowConverter.cs │ │ └── TableConverter.cs │ ├── CreateExtensions.cs │ ├── DefaultStyles.cs │ ├── DocxDocument.cs │ ├── Extensions.cs │ ├── FillRectangleOffsetsCalculator.cs │ ├── Offsets.cs │ ├── OpenXmlUnits.cs │ ├── ReportDotNet.Docx.csproj │ ├── ReportDotNet.Docx.nuspec │ └── packages.config └── ReportDotNet.Web │ ├── App │ ├── DirectoryWatcher.cs │ ├── GoogleDocsUploader.cs │ ├── ReportHub.cs │ └── ReportRenderer.cs │ ├── Controllers │ └── HomeController.cs │ ├── Examples │ ├── PaymentOrder │ │ ├── Data.cs │ │ └── Template.cs │ ├── Simple │ │ └── Template.cs │ ├── SimpleXml │ │ ├── [Content_Types].xml │ │ ├── _rels │ │ │ └── .rels │ │ ├── docProps │ │ │ ├── app.xml │ │ │ └── core.xml │ │ └── word │ │ │ ├── _rels │ │ │ └── document.xml.rels │ │ │ ├── document.xml │ │ │ ├── fontTable.xml │ │ │ ├── settings.xml │ │ │ ├── styles.xml │ │ │ ├── theme │ │ │ └── theme1.xml │ │ │ └── webSettings.xml │ └── StampAndSigns │ │ ├── AccountantSign.png │ │ ├── BossSign.png │ │ ├── Data.cs │ │ ├── Stamp.png │ │ └── Template.cs │ ├── Program.cs │ ├── ReportDotNet.Web.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── wwwroot │ ├── index.css │ ├── index.html │ ├── index.js │ ├── jquery-3.1.1.js │ ├── jquery-3.1.1.min.js │ └── signalr.min.js │ └── yarn.lock └── tests └── ReportDotNet.Core.Tests ├── CellBuilderTest.cs ├── ReportDotNet.Core.Tests.csproj └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | ## Ignore Visual Studio temporary files, build results, and 50 | ## files generated by popular Visual Studio add-ons. 51 | ## 52 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 53 | 54 | # User-specific files 55 | *.suo 56 | *.user 57 | *.userosscache 58 | *.sln.docstates 59 | 60 | # User-specific files (MonoDevelop/Xamarin Studio) 61 | *.userprefs 62 | 63 | # Build results 64 | [Dd]ebug/ 65 | [Dd]ebugPublic/ 66 | [Rr]elease/ 67 | [Rr]eleases/ 68 | x64/ 69 | x86/ 70 | bld/ 71 | [Bb]in/ 72 | [Oo]bj/ 73 | [Ll]og/ 74 | 75 | # Visual Studio 2015 cache/options directory 76 | .vs/ 77 | # Uncomment if you have tasks that create the project's static files in wwwroot 78 | #wwwroot/ 79 | 80 | # MSTest test Results 81 | [Tt]est[Rr]esult*/ 82 | [Bb]uild[Ll]og.* 83 | 84 | # NUNIT 85 | *.VisualState.xml 86 | TestResult.xml 87 | 88 | # Build Results of an ATL Project 89 | [Dd]ebugPS/ 90 | [Rr]eleasePS/ 91 | dlldata.c 92 | 93 | # .NET Core 94 | project.lock.json 95 | project.fragment.lock.json 96 | artifacts/ 97 | **/Properties/launchSettings.json 98 | 99 | *_i.c 100 | *_p.c 101 | *_i.h 102 | *.ilk 103 | *.meta 104 | *.obj 105 | *.pch 106 | *.pdb 107 | *.pgc 108 | *.pgd 109 | *.rsp 110 | *.sbr 111 | *.tlb 112 | *.tli 113 | *.tlh 114 | *.tmp 115 | *.tmp_proj 116 | *.log 117 | *.vspscc 118 | *.vssscc 119 | .builds 120 | *.pidb 121 | *.svclog 122 | *.scc 123 | 124 | # Chutzpah Test files 125 | _Chutzpah* 126 | 127 | # Visual C++ cache files 128 | ipch/ 129 | *.aps 130 | *.ncb 131 | *.opendb 132 | *.opensdf 133 | *.sdf 134 | *.cachefile 135 | *.VC.db 136 | *.VC.VC.opendb 137 | 138 | # Visual Studio profiler 139 | *.psess 140 | *.vsp 141 | *.vspx 142 | *.sap 143 | 144 | # TFS 2012 Local Workspace 145 | $tf/ 146 | 147 | # Guidance Automation Toolkit 148 | *.gpState 149 | 150 | # ReSharper is a .NET coding add-in 151 | _ReSharper*/ 152 | *.[Rr]e[Ss]harper 153 | *.DotSettings.user 154 | 155 | # JustCode is a .NET coding add-in 156 | .JustCode 157 | 158 | # TeamCity is a build add-in 159 | _TeamCity* 160 | 161 | # DotCover is a Code Coverage Tool 162 | *.dotCover 163 | 164 | # Visual Studio code coverage results 165 | *.coverage 166 | *.coveragexml 167 | 168 | # NCrunch 169 | _NCrunch_* 170 | .*crunch*.local.xml 171 | nCrunchTemp_* 172 | 173 | # MightyMoose 174 | *.mm.* 175 | AutoTest.Net/ 176 | 177 | # Web workbench (sass) 178 | .sass-cache/ 179 | 180 | # Installshield output folder 181 | [Ee]xpress/ 182 | 183 | # DocProject is a documentation generator add-in 184 | DocProject/buildhelp/ 185 | DocProject/Help/*.HxT 186 | DocProject/Help/*.HxC 187 | DocProject/Help/*.hhc 188 | DocProject/Help/*.hhk 189 | DocProject/Help/*.hhp 190 | DocProject/Help/Html2 191 | DocProject/Help/html 192 | 193 | # Click-Once directory 194 | publish/ 195 | 196 | # Publish Web Output 197 | *.[Pp]ublish.xml 198 | *.azurePubxml 199 | # TODO: Comment the next line if you want to checkin your web deploy settings 200 | # but database connection strings (with potential passwords) will be unencrypted 201 | *.pubxml 202 | *.publishproj 203 | 204 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 205 | # checkin your Azure Web App publish settings, but sensitive information contained 206 | # in these scripts will be unencrypted 207 | PublishScripts/ 208 | 209 | # NuGet Packages 210 | *.nupkg 211 | # The packages folder can be ignored because of Package Restore 212 | **/packages/* 213 | # except build/, which is used as an MSBuild target. 214 | !**/packages/build/ 215 | # Uncomment if necessary however generally it will be regenerated when needed 216 | #!**/packages/repositories.config 217 | # NuGet v3's project.json files produces more ignorable files 218 | *.nuget.props 219 | *.nuget.targets 220 | 221 | # Microsoft Azure Build Output 222 | csx/ 223 | *.build.csdef 224 | 225 | # Microsoft Azure Emulator 226 | ecf/ 227 | rcf/ 228 | 229 | # Windows Store app package directories and files 230 | AppPackages/ 231 | BundleArtifacts/ 232 | Package.StoreAssociation.xml 233 | _pkginfo.txt 234 | 235 | # Visual Studio cache files 236 | # files ending in .cache can be ignored 237 | *.[Cc]ache 238 | # but keep track of directories ending in .cache 239 | !*.[Cc]ache/ 240 | 241 | # Others 242 | ClientBin/ 243 | ~$* 244 | *~ 245 | *.dbmdl 246 | *.dbproj.schemaview 247 | *.jfm 248 | *.pfx 249 | *.publishsettings 250 | orleans.codegen.cs 251 | 252 | # Since there are multiple workflows, uncomment next line to ignore bower_components 253 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 254 | #bower_components/ 255 | 256 | # RIA/Silverlight projects 257 | Generated_Code/ 258 | 259 | # Backup & report files from converting an old project file 260 | # to a newer Visual Studio version. Backup files are not needed, 261 | # because we have git ;-) 262 | _UpgradeReport_Files/ 263 | Backup*/ 264 | UpgradeLog*.XML 265 | UpgradeLog*.htm 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | 277 | # Microsoft Fakes 278 | FakesAssemblies/ 279 | 280 | # GhostDoc plugin setting file 281 | *.GhostDoc.xml 282 | 283 | # Node.js Tools for Visual Studio 284 | .ntvs_analysis.dat 285 | node_modules/ 286 | 287 | # Typescript v1 declaration files 288 | typings/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # JetBrains Rider 315 | .idea/ 316 | *.sln.iml 317 | 318 | # CodeRush 319 | .cr/ 320 | 321 | # Python Tools for Visual Studio (PTVS) 322 | __pycache__/ 323 | *.pyc 324 | 325 | # Cake - Uncomment if you are using it 326 | # tools/** 327 | # !tools/packages.config 328 | 329 | # Telerik's JustMock configuration file 330 | *.jmconfig 331 | 332 | # BizTalk build output 333 | *.btp.cs 334 | *.btm.cs 335 | *.odx.cs 336 | *.xsd.cs 337 | -------------------------------------------------------------------------------- /.nuke: -------------------------------------------------------------------------------- 1 | ReportDotNet.sln -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vyacheslav Mostovoy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReportDotNet 2 | 3 | ### Write the code and see the resulting docx in real time 4 | ![gif.gif](gif.gif) 5 | -------------------------------------------------------------------------------- /ReportDotNet.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportDotNet.Core", "src\ReportDotNet.Core\ReportDotNet.Core.csproj", "{3BE01964-7B8B-40E1-9BC1-8FA13AB274AE}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportDotNet.Docx", "src\ReportDotNet.Docx\ReportDotNet.Docx.csproj", "{4D668235-EDD3-4C25-9CA7-59878E3D4FFB}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportDotNet.Core.Tests", "tests\ReportDotNet.Core.Tests\ReportDotNet.Core.Tests.csproj", "{92207667-DC9F-4DE8-954D-0067839B89CD}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportDotNet.Web", "src\ReportDotNet.Web\ReportDotNet.Web.csproj", "{B0ED4323-F629-4FDC-AC84-7E7976D110FD}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{50E9C5BF-6D6E-4315-B5ED-2874B79DAC24}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {50E9C5BF-6D6E-4315-B5ED-2874B79DAC24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {50E9C5BF-6D6E-4315-B5ED-2874B79DAC24}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {3BE01964-7B8B-40E1-9BC1-8FA13AB274AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {3BE01964-7B8B-40E1-9BC1-8FA13AB274AE}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {3BE01964-7B8B-40E1-9BC1-8FA13AB274AE}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {3BE01964-7B8B-40E1-9BC1-8FA13AB274AE}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {4D668235-EDD3-4C25-9CA7-59878E3D4FFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {4D668235-EDD3-4C25-9CA7-59878E3D4FFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {4D668235-EDD3-4C25-9CA7-59878E3D4FFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {4D668235-EDD3-4C25-9CA7-59878E3D4FFB}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {92207667-DC9F-4DE8-954D-0067839B89CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {92207667-DC9F-4DE8-954D-0067839B89CD}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {92207667-DC9F-4DE8-954D-0067839B89CD}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {92207667-DC9F-4DE8-954D-0067839B89CD}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {B0ED4323-F629-4FDC-AC84-7E7976D110FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {B0ED4323-F629-4FDC-AC84-7E7976D110FD}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {B0ED4323-F629-4FDC-AC84-7E7976D110FD}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {B0ED4323-F629-4FDC-AC84-7E7976D110FD}.Release|Any CPU.Build.0 = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | EndGlobal 45 | -------------------------------------------------------------------------------- /ReportDotNet.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | Space 3 | NEVER 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True 10 | True 11 | True 12 | True -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | :; set -eo pipefail 2 | :; ./build.sh "$@" 3 | :; exit $? 4 | 5 | @ECHO OFF 6 | powershell -ExecutionPolicy ByPass -NoProfile %0\..\build.ps1 %* 7 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param( 3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 4 | [string[]]$BuildArguments 5 | ) 6 | 7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" 8 | 9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { exit 1 } 10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 11 | 12 | ########################################################################### 13 | # CONFIGURATION 14 | ########################################################################### 15 | 16 | $BuildProjectFile = "$PSScriptRoot\build\_build.csproj" 17 | $TempDirectory = "$PSScriptRoot\\.tmp" 18 | 19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json" 20 | $DotNetInstallUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1" 21 | $DotNetChannel = "Current" 22 | 23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 25 | 26 | ########################################################################### 27 | # EXECUTION 28 | ########################################################################### 29 | 30 | function ExecSafe([scriptblock] $cmd) { 31 | & $cmd 32 | if ($LASTEXITCODE) { exit $LASTEXITCODE } 33 | } 34 | 35 | # If global.json exists, load expected version 36 | if (Test-Path $DotNetGlobalFile) { 37 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) 38 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { 39 | $DotNetVersion = $DotNetGlobal.sdk.version 40 | } 41 | } 42 | 43 | # If dotnet is installed locally, and expected version is not set or installation matches the expected version 44 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` 45 | (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) { 46 | $env:DOTNET_EXE = (Get-Command "dotnet").Path 47 | } 48 | else { 49 | $DotNetDirectory = "$TempDirectory\dotnet-win" 50 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" 51 | 52 | # Download install script 53 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" 54 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null 55 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) 56 | 57 | # Install by channel or version 58 | if (!(Test-Path variable:DotNetVersion)) { 59 | ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } 60 | } else { 61 | ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } 62 | } 63 | } 64 | 65 | Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" 66 | 67 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false -nologo -clp:NoSummary --verbosity quiet } 68 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } 69 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo $(bash --version 2>&1 | head -n 1) 4 | 5 | set -eo pipefail 6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) 7 | 8 | ########################################################################### 9 | # CONFIGURATION 10 | ########################################################################### 11 | 12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj" 13 | TEMP_DIRECTORY="$SCRIPT_DIR//.tmp" 14 | 15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" 16 | DOTNET_INSTALL_URL="https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.sh" 17 | DOTNET_CHANNEL="Current" 18 | 19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 21 | 22 | ########################################################################### 23 | # EXECUTION 24 | ########################################################################### 25 | 26 | function FirstJsonValue { 27 | perl -nle 'print $1 if m{"'$1'": "([^"]+)",?}' <<< ${@:2} 28 | } 29 | 30 | # If global.json exists, load expected version 31 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then 32 | DOTNET_VERSION=$(FirstJsonValue "version" $(cat "$DOTNET_GLOBAL_FILE")) 33 | if [[ "$DOTNET_VERSION" == "" ]]; then 34 | unset DOTNET_VERSION 35 | fi 36 | fi 37 | 38 | # If dotnet is installed locally, and expected version is not set or installation matches the expected version 39 | if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version 2>&1) == "$DOTNET_VERSION") ]]; then 40 | export DOTNET_EXE="$(command -v dotnet)" 41 | else 42 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" 43 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" 44 | 45 | # Download install script 46 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" 47 | mkdir -p "$TEMP_DIRECTORY" 48 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" 49 | chmod +x "$DOTNET_INSTALL_FILE" 50 | 51 | # Install by channel or version 52 | if [[ -z ${DOTNET_VERSION+x} ]]; then 53 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path 54 | else 55 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path 56 | fi 57 | fi 58 | 59 | echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" 60 | 61 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false -nologo -clp:NoSummary --verbosity quiet 62 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 63 | -------------------------------------------------------------------------------- /build/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | dotnet_style_qualification_for_field = false:warning 3 | dotnet_style_qualification_for_property = false:warning 4 | dotnet_style_qualification_for_method = false:warning 5 | dotnet_style_qualification_for_event = false:warning 6 | dotnet_style_require_accessibility_modifiers = never:warning 7 | 8 | csharp_style_expression_bodied_methods = true:silent 9 | csharp_style_expression_bodied_properties = true:warning 10 | csharp_style_expression_bodied_indexers = true:warning 11 | csharp_style_expression_bodied_accessors = true:warning 12 | -------------------------------------------------------------------------------- /build/Build.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Nuke.Common; 3 | using Nuke.Common.Execution; 4 | using Nuke.Common.Git; 5 | using Nuke.Common.IO; 6 | using Nuke.Common.ProjectModel; 7 | using Nuke.Common.Tooling; 8 | using Nuke.Common.Tools.DotNet; 9 | using Nuke.Common.Tools.GitHub; 10 | using Nuke.Common.Tools.GitVersion; 11 | using Nuke.Common.Utilities.Collections; 12 | using static Nuke.Common.IO.FileSystemTasks; 13 | using static Nuke.Common.IO.PathConstruction; 14 | using static Nuke.Common.Tools.DotNet.DotNetTasks; 15 | 16 | [CheckBuildProjectConfigurations] 17 | class Build: NukeBuild 18 | { 19 | public static int Main() => Execute(x => x.Compile); 20 | 21 | [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] 22 | readonly Configuration Configuration = 23 | IsLocalBuild 24 | ? Configuration.Debug 25 | : Configuration.Release; 26 | 27 | [Parameter("NuGet api key")] readonly string ApiKey; 28 | readonly string LicenseFile = RootDirectory / "LICENSE"; 29 | 30 | [Parameter("Local nuget source")] readonly string LocalNugetSource; 31 | 32 | [Solution] readonly Solution Solution; 33 | [GitRepository] readonly GitRepository GitRepository; 34 | [GitVersion] readonly GitVersion GitVersion; 35 | 36 | AbsolutePath SourceDirectory => RootDirectory / "src"; 37 | AbsolutePath TestsDirectory => RootDirectory / "tests"; 38 | AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts"; 39 | 40 | Target Clean 41 | => _ => _ 42 | .Before(Restore) 43 | .Executes(() => 44 | { 45 | SourceDirectory.GlobDirectories("**/bin", "**/obj") 46 | .ToArray() 47 | .ForEach(DeleteDirectory); 48 | TestsDirectory.GlobDirectories("**/bin", "**/obj") 49 | .ToArray() 50 | .ForEach(DeleteDirectory); 51 | EnsureCleanDirectory(ArtifactsDirectory); 52 | }); 53 | 54 | Target Restore 55 | => _ => _ 56 | .Executes(() => 57 | { 58 | DotNetRestore(s => s 59 | .SetProjectFile(Solution)); 60 | }); 61 | 62 | Target Compile 63 | => _ => _ 64 | .DependsOn(Restore) 65 | .Executes(() => 66 | { 67 | DotNetBuild(s => s 68 | .SetProjectFile(Solution) 69 | .SetConfiguration(Configuration) 70 | .SetAssemblyVersion(GitVersion.AssemblySemVer) 71 | .SetFileVersion(GitVersion.AssemblySemFileVer) 72 | .SetInformationalVersion(GitVersion.InformationalVersion) 73 | .EnableNoRestore()); 74 | }); 75 | 76 | Target Pack 77 | => _ => _ 78 | .DependsOn(Clean, Compile) 79 | .Executes(() => 80 | { 81 | var licenseUrl = 82 | GitRepository.GetGitHubBrowseUrl(LicenseFile, "master"); 83 | DotNetPack(s => s 84 | //.SetPackageReleaseNotes(changelogUrl) 85 | .SetWorkingDirectory(RootDirectory) 86 | .SetPackageLicenseUrl(licenseUrl) 87 | .SetProject(Solution.Path) 88 | .SetRepositoryUrl("https://github.com/mifopen/ReportDotNet") 89 | .EnableNoBuild() 90 | .SetConfiguration(Configuration) 91 | .EnableIncludeSymbols() 92 | .SetOutputDirectory(ArtifactsDirectory) 93 | .SetDescription("Docx reports generator") 94 | .SetAuthors("mif") 95 | .SetPackageTags("reports", "docx") 96 | .SetVersion(GitVersion.NuGetVersionV2)); 97 | }); 98 | 99 | Target Publish 100 | => _ => _ 101 | .DependsOn(Pack) 102 | .Requires(() => ApiKey) 103 | .Requires(() => Equals(Configuration, Configuration.Release)) 104 | .Executes(() => 105 | { 106 | GlobFiles(ArtifactsDirectory, "*.nupkg") 107 | .NotEmpty() 108 | .Where(x => !x.EndsWith(".symbols.nupkg")) 109 | .ForEach(x => DotNetNuGetPush(s => s 110 | .SetTargetPath(x) 111 | .SetSource("https://api.nuget.org/v3/index.json") 112 | .SetApiKey(ApiKey))); 113 | }); 114 | 115 | Target PublishLocal 116 | => _ => _ 117 | .DependsOn(Pack) 118 | .Requires(() => LocalNugetSource) 119 | .Executes(() => 120 | { 121 | EnsureExistingDirectory(LocalNugetSource); 122 | GlobFiles(ArtifactsDirectory, "*.nupkg") 123 | .NotEmpty() 124 | .Where(x => !x.EndsWith(".symbols.nupkg")) 125 | .ForEach(x => DotNetNuGetPush(s => s 126 | .SetTargetPath(x) 127 | .SetSource(LocalNugetSource))); 128 | }); 129 | } 130 | -------------------------------------------------------------------------------- /build/_build.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | CS0649;CS0169 7 | .. 8 | .. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /build/_build.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | DO_NOT_SHOW 4 | Implicit 5 | Implicit 6 | ExpressionBody 7 | 0 8 | NEXT_LINE 9 | True 10 | False 11 | 120 12 | IF_OWNER_IS_SINGLE_LINE 13 | WRAP_IF_LONG 14 | False 15 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 16 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> 17 | True 18 | True 19 | True 20 | True 21 | True 22 | True 23 | True 24 | True 25 | True 26 | -------------------------------------------------------------------------------- /gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mifopen/ReportDotNet/6a5b69baee0894a39833da4d9957f60f6631ee47/gif.gif -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Alignment.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public enum Alignment 4 | { 5 | Left, 6 | Center, 7 | Right, 8 | Both 9 | } 10 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/BorderStyle.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public enum BorderStyle 4 | { 5 | Single, 6 | Dashed, 7 | DotDash, 8 | DotDotDash 9 | } 10 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Borders.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ReportDotNet.Core 4 | { 5 | [Flags] 6 | public enum Borders 7 | { 8 | None = 0, 9 | Top = 1 << 0, 10 | Left = 1 << 1, 11 | Bottom = 1 << 2, 12 | Right = 1 << 3, 13 | All = Top | Left | Bottom | Right 14 | } 15 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Cell.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class Cell 4 | { 5 | public CellParameters Parameters { get; } 6 | 7 | public Cell(CellParameters parameters) 8 | { 9 | Parameters = parameters; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/CellBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | 5 | namespace ReportDotNet.Core 6 | { 7 | public class CellBuilder 8 | { 9 | private readonly CellParameters parameters = new CellParameters(); 10 | private string text; 11 | private int? fontSize; 12 | private Alignment? alignment; 13 | private bool bold; 14 | private double? spaceBetweenLines; 15 | 16 | public CellBuilder Width(int? width) => Chain(p => p.Width = width); 17 | public CellBuilder Add(Paragraph paragraph) => Chain(p => p.Paragraph = paragraph); 18 | public CellBuilder Add(string text) => Chain(_ => this.text = text); 19 | public CellBuilder FontSize(int fontSize) => Chain(_ => this.fontSize = fontSize); 20 | public CellBuilder Alignment(Alignment alignment) => Chain(_ => this.alignment = alignment); 21 | public CellBuilder Bold(bool bold = true) => Chain(_ => this.bold = bold); 22 | public CellBuilder SpaceBetweenLines(double? spaceBetweenLines) => Chain(_ => this.spaceBetweenLines = spaceBetweenLines); 23 | public CellBuilder VerticalAlignment(VerticalAlignment verticalAlignment) => Chain(p => p.VerticalAlignment = verticalAlignment); 24 | public CellBuilder Borders(Borders borders) => Chain(p => p.Borders = borders); 25 | public CellBuilder MergeUp(bool mergeUp = true) => Chain(p => p.MergeUp = mergeUp); 26 | public CellBuilder MergeDown(bool mergeDown = true) => Chain(p => p.MergeDown = mergeDown); 27 | public CellBuilder MergeRight(bool mergeRight = true) => Chain(p => p.MergeRight = mergeRight); 28 | public CellBuilder MergeLeft(bool mergeLeft = true) => Chain(p => p.MergeLeft = mergeLeft); 29 | public CellBuilder TextDirection(TextDirection textDirection) => Chain(p => p.TextDirection = textDirection); 30 | public CellBuilder BackgroundColor(Color backgroundColor) => Chain(p => p.BackgroundColor = backgroundColor); 31 | public CellBuilder BorderSize(int borderSize) => Chain(p => p.BorderSize = borderSize); 32 | 33 | public CellBuilder Margin(int? left = null, 34 | int? right = null, 35 | int? top = null, 36 | int? bottom = null) => Chain(p => 37 | { 38 | p.MarginLeft = left; 39 | p.MarginRight = right; 40 | p.MarginTop = top; 41 | p.MarginBottom = bottom; 42 | 43 | }); 44 | 45 | 46 | public CellBuilder Borders(BorderStyle left = BorderStyle.Single, 47 | BorderStyle top = BorderStyle.Single, 48 | BorderStyle right = BorderStyle.Single, 49 | BorderStyle bottom = BorderStyle.Single) => Chain(p => 50 | { 51 | p.LeftBorderStyle = left; 52 | p.TopBorderStyle = top; 53 | p.RightBorderStyle = right; 54 | p.BottomBorderStyle = bottom; 55 | }); 56 | 57 | public Cell Build() 58 | { 59 | //todo clone parameters? 60 | parameters.Paragraph = parameters.Paragraph ?? new Paragraph(new ParagraphParameters 61 | { 62 | Parts = new List { new TextPart { Text = text } }, 63 | FontSize = fontSize, 64 | Alignment = alignment, 65 | Bold = bold, 66 | SpaceBetweenLines = spaceBetweenLines 67 | }); 68 | return new Cell(parameters); 69 | } 70 | 71 | public static implicit operator Cell(CellBuilder builder) => builder.Build(); 72 | 73 | private CellBuilder Chain(Action action) 74 | { 75 | action(parameters); 76 | return this; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/CellParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace ReportDotNet.Core 4 | { 5 | public class CellParameters 6 | { 7 | public Paragraph Paragraph { get; set; } 8 | public int? Width { get; set; } 9 | public VerticalAlignment? VerticalAlignment { get; set; } 10 | public Borders? Borders { get; set; } 11 | public int? MarginLeft { get; set; } 12 | public int? MarginRight { get; set; } 13 | public int? MarginTop { get; set; } 14 | public int? MarginBottom { get; set; } 15 | public bool MergeDown { get; set; } 16 | public bool MergeUp { get; set; } 17 | 18 | 19 | public bool MergeLeft { get; set; } 20 | public bool MergeRight { get; set; } 21 | 22 | public BorderStyle LeftBorderStyle { get; set; } 23 | public BorderStyle TopBorderStyle { get; set; } 24 | public BorderStyle RightBorderStyle { get; set; } 25 | public BorderStyle BottomBorderStyle { get; set; } 26 | public TextDirection? TextDirection { get; set; } 27 | public Color? BackgroundColor { get; set; } 28 | public double? BorderSize { get; set; } 29 | } 30 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Create.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class Create 4 | { 5 | // ReSharper disable once UnassignedReadonlyField 6 | public static readonly Create Document; 7 | } 8 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Factories.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public static class Factories 4 | { 5 | public static PageBuilder Page() => new PageBuilder(); 6 | 7 | public static TableBuilder Table(params Row[] rows) => new TableBuilder().Add(rows); 8 | public static TableBuilder Table(int width) => new TableBuilder().Width(width); 9 | 10 | public static RowBuilder Row() => new RowBuilder(); 11 | public static RowBuilder Row(int height) => new RowBuilder().Height(height); 12 | public static RowBuilder Row(params Cell[] cells) => new RowBuilder().Add(cells); 13 | 14 | public static CellBuilder Cell() => new CellBuilder(); 15 | public static CellBuilder Cell(int? width) => new CellBuilder().Width(width); 16 | 17 | public static CellBuilder Cell(string text, 18 | int? width = null) => new CellBuilder().Add(text).Width(width); 19 | 20 | public static ParagraphBuilder Paragraph() => new ParagraphBuilder(); 21 | public static ParagraphBuilder Paragraph(string text) => new ParagraphBuilder().Add(text); 22 | 23 | public static PictureBuilder Picture(byte[] bytes) => new PictureBuilder().Bytes(bytes); 24 | public static StubPictureBuilder StubPicture() => new StubPictureBuilder(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Field.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public enum Field 4 | { 5 | PageNumber, 6 | PageCount 7 | } 8 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/FieldPart.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class FieldPart: Part 4 | { 5 | public Field Field { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public static class Helpers 7 | { 8 | public static int[] GetWidths(int width, 9 | IEnumerable initialWidths) 10 | { 11 | var result = new[] { width }; 12 | foreach (var widths in initialWidths) 13 | result = Merge(result, widths); 14 | return result; 15 | } 16 | 17 | public static int[] GetSpans(int[] tableCols, 18 | int[] rowCols) 19 | { 20 | var result = new int[rowCols.Length]; 21 | 22 | var c = 0; 23 | for (var i = 0; i < rowCols.Length; i++) 24 | { 25 | var rowCol = rowCols[i]; 26 | for (int j = c, span = 1; j < tableCols.Length; j++, span++) 27 | { 28 | rowCol -= tableCols[j]; 29 | if (rowCol < 0) 30 | throw new InvalidOperationException("Неправильно поделились колонки для таблицы"); 31 | if (rowCol > 0) 32 | continue; 33 | result[i] = span; 34 | c = j + 1; 35 | break; 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | 42 | private static int[] Merge(int[] a, 43 | int[] b) 44 | { 45 | var ar = new int[a.Length]; 46 | a.CopyTo(ar, 0); 47 | var br = new int[b.Length]; 48 | b.CopyTo(br, 0); 49 | var result = new List(); 50 | for (int i = 0, j = 0; i < ar.Length || j < br.Length;) 51 | if (i == ar.Length) 52 | { 53 | result.Add(br[j]); 54 | j++; 55 | } 56 | else if (j == br.Length) 57 | { 58 | result.Add(ar[i]); 59 | i++; 60 | } 61 | else if (ar[i] == br[j]) 62 | { 63 | result.Add(ar[i]); 64 | i++; 65 | j++; 66 | } 67 | else if (ar[i] > br[j]) 68 | { 69 | result.Add(br[j]); 70 | ar[i] -= br[j]; 71 | j++; 72 | } 73 | else 74 | { 75 | result.Add(ar[i]); 76 | br[j] -= ar[i]; 77 | i++; 78 | } 79 | return result.ToArray(); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/IDocument.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public interface IDocument 4 | { 5 | IDocument AddPage(Page page); 6 | byte[] Save(); 7 | void SetDefaultPageLayout(PageLayout pageLayout); 8 | PageLayout GetDefaultPageLayout(); 9 | void SetDefaultFooter(Table table); 10 | Table GetDefaultFooter(); 11 | } 12 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/IPageElement.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public interface IPageElement 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Page.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class Page 4 | { 5 | public PageParameters Parameters { get; } 6 | 7 | public Page(PageParameters parameters) 8 | { 9 | Parameters = parameters; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PageBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public class PageBuilder 7 | { 8 | private readonly PageParameters parameters = new PageParameters(); 9 | 10 | public PageBuilder Orientation(PageOrientation orientation) => Chain(p => p.Orientation = orientation); 11 | public PageBuilder HeaderMargin(int headerMargin) => Chain(p => p.HeaderMargin = headerMargin); 12 | public PageBuilder FooterMargin(int footerMargin) => Chain(p => p.FooterMargin = footerMargin); 13 | public PageBuilder Size(PageSize size) => Chain(p => p.Size = size); 14 | public PageBuilder Footer(Table footer) => Chain(p => p.Footer = footer); 15 | 16 | public PageBuilder Add(params Paragraph[] paragraphs) => 17 | Chain(p => p.Elements.AddRange(paragraphs.Where(x => x != null))); 18 | 19 | public PageBuilder Add(params Table[] tables) => 20 | Chain(p => p.Elements.AddRange(tables.Where(x => x != null))); 21 | 22 | public PageBuilder Margin(int? left = null, 23 | int? top = null, 24 | int? right = null, 25 | int? bottom = null) => Chain(p => 26 | { 27 | p.MarginLeft = left; 28 | p.MarginTop = top; 29 | p.MarginRight = right; 30 | p.MarginBottom = bottom; 31 | }); 32 | 33 | public Page Build() => new Page(parameters); 34 | 35 | public static implicit operator Page(PageBuilder builder) => builder.Build(); 36 | 37 | private PageBuilder Chain(Action action) 38 | { 39 | action(parameters); 40 | return this; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PageLayout.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class PageLayout 4 | { 5 | public PageLayout() 6 | { 7 | Size = PageSize.A4; 8 | } 9 | 10 | public PageOrientation? Orientation { get; set; } 11 | public int? MarginLeft { get; set; } 12 | public int? MarginTop { get; set; } 13 | public int? MarginRight { get; set; } 14 | public int? MarginBottom { get; set; } 15 | public int? FooterMargin { get; set; } 16 | public int? HeaderMargin { get; set; } 17 | public PageSize Size { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PageOrientation.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public enum PageOrientation 4 | { 5 | Portrait, 6 | Landscape 7 | } 8 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PageParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ReportDotNet.Core 4 | { 5 | public class PageParameters 6 | { 7 | public PageOrientation? Orientation { get; set; } 8 | public int? MarginLeft { get; set; } 9 | public int? MarginTop { get; set; } 10 | public int? MarginRight { get; set; } 11 | public int? MarginBottom { get; set; } 12 | public int? FooterMargin { get; set; } 13 | public int? HeaderMargin { get; set; } 14 | public PageSize Size { get; set; } = PageSize.A4; 15 | public Table Footer { get; set; } 16 | public readonly List Elements = new List(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PageSize.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class PageSize 4 | { 5 | public double Width { get; set; } 6 | public double Height { get; set; } 7 | 8 | public static readonly PageSize A4 = new PageSize { Width = 210, Height = 297 }; 9 | } 10 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Paragraph.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class Paragraph: IPageElement 4 | { 5 | public ParagraphParameters Parameters { get; set; } 6 | 7 | public Paragraph(ParagraphParameters parameters) 8 | { 9 | Parameters = parameters; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/ParagraphBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public class ParagraphBuilder 7 | { 8 | private readonly ParagraphParameters parameters = new ParagraphParameters(); 9 | 10 | public ParagraphBuilder FontSize(int fontSize) => Chain(p => p.FontSize = fontSize); 11 | public ParagraphBuilder Alignment(Alignment alignment) => Chain(p => p.Alignment = alignment); 12 | public ParagraphBuilder Bold(bool bold = true) => Chain(p => p.Bold = bold); 13 | public ParagraphBuilder SpaceBetweenLines(double spaceBetweenLines) => Chain(p => p.SpaceBetweenLines = spaceBetweenLines); 14 | public ParagraphBuilder Add(string text) => Chain(p => p.Parts.Add(new TextPart { Text = text })); 15 | public ParagraphBuilder Add(Field field) => Chain(p => p.Parts.Add(new FieldPart { Field = field })); 16 | public ParagraphBuilder Add(Picture picture) => Chain(p => p.Parts.Add(new PicturePart { Picture = picture })); 17 | public ParagraphBuilder Add(StubPicture stubPicture) => Chain(p => p.Parts.Add(new StubPicturePart { StubPicture = stubPicture })); 18 | public ParagraphBuilder BackgroundColor(Color color) => Chain(p => p.BackgroundColor = color); 19 | 20 | public Paragraph Build() => new Paragraph(parameters); 21 | 22 | public static implicit operator Paragraph(ParagraphBuilder builder) => builder.Build(); 23 | 24 | private ParagraphBuilder Chain(Action action) 25 | { 26 | action(parameters); 27 | return this; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/ParagraphParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public class ParagraphParameters 7 | { 8 | public int? FontSize { get; set; } 9 | public Alignment? Alignment { get; set; } 10 | public bool Bold { get; set; } 11 | public List Parts { get; set; } = new List(); 12 | public double? SpaceBetweenLines { get; set; } 13 | public Color? BackgroundColor { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Part.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public abstract class Part 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Picture.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class Picture 4 | { 5 | public PictureParameters Parameters { get; } 6 | 7 | public Picture(PictureParameters parameters) 8 | { 9 | Parameters = parameters; 10 | } 11 | 12 | public bool IsEmpty() => Parameters.Bytes == null || Parameters.Bytes.Length == 0; 13 | } 14 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PictureBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ReportDotNet.Core 4 | { 5 | public class PictureBuilder 6 | { 7 | private readonly PictureParameters parameters = new PictureParameters(); 8 | 9 | public PictureBuilder Bytes(byte[] bytes) => Chain(p => p.Bytes = bytes); 10 | public PictureBuilder MaxWidth(int maxWidth) => Chain(p => p.MaxWidth = maxWidth); 11 | public PictureBuilder MaxHeight(int maxHeight) => Chain(p => p.MaxHeight = maxHeight); 12 | public PictureBuilder OffsetX(int? offsetX) => Chain(p => p.OffsetX = offsetX); 13 | public PictureBuilder OffsetY(int? offsetY) => Chain(p => p.OffsetY = offsetY); 14 | public PictureBuilder Description(string description) => Chain(p => p.Description = description); 15 | 16 | public Picture Build() => new Picture(parameters); 17 | 18 | public static implicit operator Picture(PictureBuilder builder) => builder.Build(); 19 | 20 | private PictureBuilder Chain(Action action) 21 | { 22 | action(parameters); 23 | return this; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PictureParameters.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class PictureParameters 4 | { 5 | public byte[] Bytes { get; set; } 6 | public int MaxWidth { get; set; } 7 | public int MaxHeight { get; set; } 8 | public int? OffsetX { get; set; } 9 | public int? OffsetY { get; set; } 10 | public string Description { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/PicturePart.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class PicturePart: Part 4 | { 5 | public Picture Picture { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/ReportDotNet.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 8 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Row.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public class Row 7 | { 8 | public RowParameters Parameters { get; } 9 | public Table Table { private get; set; } 10 | 11 | public Row(RowParameters parameters) 12 | { 13 | Parameters = parameters; 14 | } 15 | 16 | public int[] GetWidths() 17 | { 18 | if (Parameters.GetCells().Count(c => c.Parameters.Width == null) > 1) 19 | throw new InvalidOperationException("Multiple cells with relative width in one row aren't supported"); 20 | var relativeCellWidth = Table.Parameters.Width - Parameters.GetCells().Sum(c => c.Parameters.Width ?? 0); 21 | return Parameters.GetCells().Select(x => x.Parameters.Width ?? relativeCellWidth).ToArray(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/RowBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ReportDotNet.Core 6 | { 7 | public class RowBuilder 8 | { 9 | private readonly RowParameters parameters = new RowParameters(); 10 | 11 | public RowBuilder Height(int height) => Chain(p => p.Height = height); 12 | public RowBuilder HeightType(RowHeightType heightType) => Chain(p => p.HeightType = heightType); 13 | 14 | public RowBuilder Add(params Cell[] cells) => 15 | Chain(p => p.AddCells(cells.Where(x => x != null))); 16 | 17 | public RowBuilder Add(IEnumerable cells) => 18 | Chain(p => p.AddCells(cells.Where(x => x != null))); 19 | 20 | public Row Build() => new Row(parameters); 21 | 22 | public static implicit operator Row(RowBuilder builder) => builder.Build(); 23 | 24 | private RowBuilder Chain(Action action) 25 | { 26 | action(parameters); 27 | return this; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/RowHeightType.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public enum RowHeightType 4 | { 5 | Exact, 6 | AtLeast 7 | } 8 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/RowParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using static ReportDotNet.Core.Factories; 4 | 5 | namespace ReportDotNet.Core 6 | { 7 | public class RowParameters 8 | { 9 | public int? Height { get; set; } 10 | public RowHeightType HeightType { get; set; } 11 | private readonly List cells = new List(); 12 | 13 | public void AddCells(IEnumerable cell) => cells.AddRange(cell); 14 | 15 | public Cell[] GetCells() 16 | { 17 | return cells.Any() ? cells.ToArray() : new[] { Cell().Build() }; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/StubPicture.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class StubPicture 4 | { 5 | public StubPictureParameters Parameters { get; } 6 | 7 | public StubPicture(StubPictureParameters parameters) 8 | { 9 | Parameters = parameters; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/StubPictureBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public class StubPictureBuilder 7 | { 8 | private readonly StubPictureParameters parameters = new StubPictureParameters(); 9 | 10 | public StubPictureBuilder Color(Color color) => Chain(p => p.Color = color); 11 | public StubPictureBuilder MaxWidth(int maxWidth) => Chain(p => p.MaxWidth = maxWidth); 12 | public StubPictureBuilder MaxHeight(int maxHeight) => Chain(p => p.MaxHeight = maxHeight); 13 | public StubPictureBuilder OffsetX(int? offsetX) => Chain(p => p.OffsetX = offsetX); 14 | public StubPictureBuilder OffsetY(int? offsetY) => Chain(p => p.OffsetY = offsetY); 15 | public StubPictureBuilder Description(string description) => Chain(p => p.Description = description); 16 | 17 | public StubPicture Build() => new StubPicture(parameters); 18 | 19 | public static implicit operator StubPicture(StubPictureBuilder builder) => 20 | builder.Build(); 21 | 22 | private StubPictureBuilder Chain(Action action) 23 | { 24 | action(parameters); 25 | return this; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/StubPictureParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.IO; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public class StubPictureParameters 7 | { 8 | public byte[] Bytes 9 | { 10 | get 11 | { 12 | var bitmap = new Bitmap(MaxWidth, MaxHeight); 13 | for (var i = 0; i < bitmap.Width; i++) 14 | for (var j = 0; j < bitmap.Height; j++) 15 | bitmap.SetPixel(i, j, Color.Black); 16 | return ImageToByte(bitmap); 17 | } 18 | } 19 | 20 | private static byte[] ImageToByte(Image img) 21 | { 22 | using var stream = new MemoryStream(); 23 | img.Save(stream, System.Drawing.Imaging.ImageFormat.Png); 24 | return stream.ToArray(); 25 | } 26 | 27 | public Color Color { get; set; } = Color.Black; 28 | public int MaxWidth { get; set; } 29 | public int MaxHeight { get; set; } 30 | public int? OffsetX { get; set; } 31 | public int? OffsetY { get; set; } 32 | public string Description { get; set; } 33 | } 34 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/StubPicturePart.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class StubPicturePart: Part 4 | { 5 | public StubPicture StubPicture { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/Table.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class Table: IPageElement 4 | { 5 | public TableParameters Parameters { get; } 6 | 7 | public Table(TableParameters parameters) 8 | { 9 | Parameters = parameters; 10 | foreach (var row in Parameters.Rows) 11 | row.Table = this; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/TableBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ReportDotNet.Core 5 | { 6 | public class TableBuilder 7 | { 8 | private readonly TableParameters parameters = new TableParameters(); 9 | 10 | public TableBuilder Borders(Borders borders) => Chain(p => p.Borders = borders); 11 | public TableBuilder FontSize(int fontSize) => Chain(p => p.FontSize = fontSize); 12 | 13 | public TableBuilder CellMargin(int? left = null, 14 | int? right = null, 15 | int? top = null, 16 | int? bottom = null) => Chain(p => 17 | { 18 | p.CellMarginLeft = left; 19 | p.CellMarginRight = right; 20 | p.CellMarginTop = top; 21 | p.CellMarginBottom = bottom; 22 | }); 23 | 24 | public TableBuilder Width(int width) => Chain(p => p.Width = width); 25 | public TableBuilder Add(params Row[] rows) => Chain(p => p.Rows.AddRange(rows)); 26 | public TableBuilder Add(IEnumerable rows) => Chain(p => p.Rows.AddRange(rows)); 27 | 28 | public Table Build() => new Table(parameters); 29 | 30 | public static implicit operator Table(TableBuilder builder) => builder.Build(); 31 | 32 | private TableBuilder Chain(Action action) 33 | { 34 | action(parameters); 35 | return this; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/TableParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ReportDotNet.Core 4 | { 5 | public class TableParameters 6 | { 7 | public int Width { get; set; } 8 | public Borders? Borders { get; set; } 9 | public int? CellMarginLeft { get; set; } 10 | public int? CellMarginRight { get; set; } 11 | public int? CellMarginTop { get; set; } 12 | public int? CellMarginBottom { get; set; } 13 | public int? FontSize { get; set; } 14 | public List Rows { get; set; } = new List(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/TextDirection.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public enum TextDirection 4 | { 5 | LeftRight_TopBottom, 6 | RightLeft_TopBottom, 7 | LeftRight_BottomTop 8 | } 9 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/TextPart.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public class TextPart: Part 4 | { 5 | public string Text { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Core/VerticalAlignment.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Core 2 | { 3 | public enum VerticalAlignment 4 | { 5 | Top, 6 | Center, 7 | Bottom, 8 | } 9 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Converters/CellConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DocumentFormat.OpenXml.Packaging; 3 | using DocumentFormat.OpenXml.Wordprocessing; 4 | using ReportDotNet.Core; 5 | using Color = System.Drawing.Color; 6 | using Table = ReportDotNet.Core.Table; 7 | using TextDirection = ReportDotNet.Core.TextDirection; 8 | 9 | namespace ReportDotNet.Docx.Converters 10 | { 11 | internal static class CellConverter 12 | { 13 | public static TableCell Convert(Cell cell, 14 | WordprocessingDocument document, 15 | Table table, 16 | int span) 17 | { 18 | var tableCell = new TableCell(); 19 | var cellProperties = new TableCellProperties(); 20 | var parameters = cell.Parameters; 21 | 22 | if (span > 1) 23 | cellProperties.GridSpan = new GridSpan { Val = span }; 24 | 25 | cellProperties.TableCellVerticalAlignment = new TableCellVerticalAlignment 26 | { 27 | Val = GetVerticalAlignment(parameters.VerticalAlignment ?? VerticalAlignment.Bottom) 28 | }; 29 | 30 | cellProperties.TableCellMargin = GetMargin(parameters.MarginLeft ?? table.Parameters.CellMarginLeft, 31 | parameters.MarginRight ?? table.Parameters.CellMarginRight, 32 | parameters.MarginTop ?? table.Parameters.CellMarginTop, 33 | parameters.MarginBottom ?? table.Parameters.CellMarginBottom); 34 | 35 | if (parameters.MergeDown) 36 | cellProperties.VerticalMerge = new VerticalMerge { Val = MergedCellValues.Restart }; 37 | else if (parameters.MergeUp) 38 | cellProperties.VerticalMerge = new VerticalMerge(); 39 | 40 | if (parameters.MergeRight) 41 | cellProperties.HorizontalMerge = new HorizontalMerge { Val = MergedCellValues.Restart }; 42 | else if (parameters.MergeLeft) 43 | cellProperties.HorizontalMerge = new HorizontalMerge(); 44 | 45 | var borders = parameters.Borders ?? table.Parameters.Borders ?? Borders.None; 46 | if (borders != Borders.None) 47 | cellProperties.TableCellBorders = new TableCellBorders 48 | { 49 | BottomBorder = borders.HasFlag(Borders.Bottom) 50 | ? GetBorder(parameters.BottomBorderStyle, parameters.BorderSize) 51 | : null, 52 | LeftBorder = borders.HasFlag(Borders.Left) 53 | ? GetBorder(parameters.LeftBorderStyle, parameters.BorderSize) 54 | : null, 55 | TopBorder = borders.HasFlag(Borders.Top) 56 | ? GetBorder(parameters.TopBorderStyle, parameters.BorderSize) 57 | : null, 58 | RightBorder = borders.HasFlag(Borders.Right) 59 | ? GetBorder(parameters.RightBorderStyle, parameters.BorderSize) 60 | : null 61 | }; 62 | 63 | if (parameters.TextDirection.HasValue) 64 | cellProperties.TextDirection = new DocumentFormat.OpenXml.Wordprocessing.TextDirection { Val = ConvertTextDirection(parameters.TextDirection.Value) }; 65 | 66 | if (parameters.BackgroundColor.HasValue) 67 | cellProperties.Shading = new Shading 68 | { 69 | Color = parameters.BackgroundColor.Value.ToHex(), 70 | Fill = "auto", 71 | Val = ShadingPatternValues.Solid 72 | }; 73 | 74 | tableCell.AppendChild(ParagraphConverter.Convert(parameters.Paragraph, document, table.Parameters.FontSize)); 75 | 76 | tableCell.TableCellProperties = cellProperties; 77 | return tableCell; 78 | } 79 | 80 | private static TBorder GetBorder(BorderStyle borderStyle, 81 | double? borderSize) 82 | where TBorder: BorderType, new() 83 | { 84 | return new TBorder 85 | { 86 | Val = BorderStyleToBorderValues(borderStyle), 87 | Color = Color.Black.ToHex(), 88 | Size = (uint) ((borderSize ?? 0.5) * 4), 89 | Space = 0 90 | }; 91 | } 92 | 93 | private static TextDirectionValues ConvertTextDirection(TextDirection textDirection) 94 | { 95 | switch (textDirection) 96 | { 97 | case TextDirection.LeftRight_TopBottom: 98 | return TextDirectionValues.LefToRightTopToBottom; 99 | case TextDirection.RightLeft_TopBottom: 100 | return TextDirectionValues.TopToBottomRightToLeft; 101 | case TextDirection.LeftRight_BottomTop: 102 | return TextDirectionValues.BottomToTopLeftToRight; 103 | default: 104 | throw new ArgumentOutOfRangeException(nameof(textDirection), textDirection, null); 105 | } 106 | } 107 | 108 | private static BorderValues BorderStyleToBorderValues(BorderStyle borderStyle) 109 | { 110 | switch (borderStyle) 111 | { 112 | case BorderStyle.Single: 113 | return BorderValues.Single; 114 | case BorderStyle.Dashed: 115 | return BorderValues.Dashed; 116 | case BorderStyle.DotDash: 117 | return BorderValues.DotDash; 118 | case BorderStyle.DotDotDash: 119 | return BorderValues.DotDotDash; 120 | default: 121 | throw new ArgumentOutOfRangeException(nameof(borderStyle), borderStyle, null); 122 | } 123 | } 124 | 125 | private static TableCellMargin GetMargin(int? left, 126 | int? right, 127 | int? top, 128 | int? bottom) 129 | { 130 | if (left == null && right == null && top == null && bottom == null) 131 | return null; 132 | 133 | return new TableCellMargin 134 | { 135 | LeftMargin = left.HasValue 136 | ? new LeftMargin 137 | { 138 | Width = (left * OpenXmlUnits.Dxa).ToString(), 139 | Type = TableWidthUnitValues.Dxa 140 | } 141 | : null, 142 | RightMargin = right.HasValue 143 | ? new RightMargin 144 | { 145 | Width = (right * OpenXmlUnits.Dxa).ToString(), 146 | Type = TableWidthUnitValues.Dxa 147 | } 148 | : null, 149 | TopMargin = top.HasValue 150 | ? new TopMargin 151 | { 152 | Width = (top * OpenXmlUnits.Dxa).ToString(), 153 | Type = TableWidthUnitValues.Dxa 154 | } 155 | : null, 156 | BottomMargin = bottom.HasValue 157 | ? new BottomMargin 158 | { 159 | Width = (bottom * OpenXmlUnits.Dxa).ToString(), 160 | Type = TableWidthUnitValues.Dxa 161 | } 162 | : null 163 | }; 164 | } 165 | 166 | private static TableVerticalAlignmentValues GetVerticalAlignment(VerticalAlignment verticalAlignment) 167 | { 168 | switch (verticalAlignment) 169 | { 170 | case VerticalAlignment.Top: 171 | return TableVerticalAlignmentValues.Top; 172 | case VerticalAlignment.Center: 173 | return TableVerticalAlignmentValues.Center; 174 | case VerticalAlignment.Bottom: 175 | return TableVerticalAlignmentValues.Bottom; 176 | default: 177 | throw new ArgumentOutOfRangeException(nameof(verticalAlignment), verticalAlignment, null); 178 | } 179 | } 180 | } 181 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Converters/PageConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using DocumentFormat.OpenXml; 4 | using DocumentFormat.OpenXml.Packaging; 5 | using DocumentFormat.OpenXml.Wordprocessing; 6 | using ReportDotNet.Core; 7 | using PageSize = DocumentFormat.OpenXml.Wordprocessing.PageSize; 8 | using Paragraph = DocumentFormat.OpenXml.Wordprocessing.Paragraph; 9 | using Table = ReportDotNet.Core.Table; 10 | 11 | namespace ReportDotNet.Docx.Converters 12 | { 13 | internal static class PageConverter 14 | { 15 | public static OpenXmlElement[] Convert(Page page, 16 | IDocument document, 17 | WordprocessingDocument wpDocument, 18 | bool isLastPage) 19 | { 20 | var parts = page.Parameters.Elements.Select(p => PartToOpenXmlElement(p, wpDocument)); 21 | 22 | if (isLastPage) 23 | { 24 | FillSectionProperties(wpDocument, 25 | wpDocument.MainDocumentPart.Document.Body.GetSectionProperties(), 26 | page, 27 | document.GetDefaultPageLayout(), 28 | document.GetDefaultFooter()); 29 | return parts.ToArray(); 30 | } 31 | 32 | return parts.Concat(new[] 33 | { 34 | new Paragraph 35 | { 36 | ParagraphProperties = new ParagraphProperties 37 | { 38 | SectionProperties = FillSectionProperties(wpDocument, 39 | new SectionProperties(), 40 | page, 41 | document.GetDefaultPageLayout(), 42 | document.GetDefaultFooter()) 43 | } 44 | } 45 | }) 46 | .ToArray(); 47 | } 48 | 49 | private static OpenXmlElement PartToOpenXmlElement(IPageElement part, 50 | WordprocessingDocument document) 51 | { 52 | var paragraph = part as Core.Paragraph; 53 | if (paragraph != null) 54 | return ParagraphConverter.Convert(paragraph, document); 55 | 56 | var table = part as Table; 57 | if (table != null) 58 | return TableConverter.Convert(table, document); 59 | 60 | throw new InvalidOperationException($"can't convert part of page with type [{part.GetType()}] to OpenXmlElement"); 61 | } 62 | 63 | private static SectionProperties FillSectionProperties(WordprocessingDocument wpDocument, 64 | SectionProperties sectionProperties, 65 | Page page, 66 | PageLayout defaultPageLayout, 67 | Table defaultFooter) 68 | { 69 | var pageOrientation = page.Parameters.Orientation ?? defaultPageLayout.Orientation ?? PageOrientation.Portrait; 70 | 71 | var width = (uint) OpenXmlUnits.FromMmTo20thOfPoint(page.Parameters.Size.Width); 72 | var height = (uint) OpenXmlUnits.FromMmTo20thOfPoint(page.Parameters.Size.Height); 73 | sectionProperties.AppendChild(new PageSize 74 | { 75 | Orient = ConvertOrientation(pageOrientation), 76 | Width = pageOrientation == PageOrientation.Portrait ? width : height, 77 | Height = pageOrientation == PageOrientation.Portrait ? height : width 78 | }); 79 | sectionProperties.AppendChild(new PageMargin 80 | { 81 | Left = (uint?) GetMargin(page.Parameters.MarginLeft, defaultPageLayout.MarginLeft), 82 | Top = GetMargin(page.Parameters.MarginTop, defaultPageLayout.MarginTop), 83 | Right = (uint?) GetMargin(page.Parameters.MarginRight, defaultPageLayout.MarginRight), 84 | Bottom = GetMargin(page.Parameters.MarginBottom, defaultPageLayout.MarginBottom), 85 | Header = (uint?) GetMargin(page.Parameters.HeaderMargin, defaultPageLayout.HeaderMargin), 86 | Footer = (uint?) GetMargin(page.Parameters.FooterMargin, defaultPageLayout.FooterMargin) 87 | }); 88 | if (page.Parameters.Footer != null || defaultFooter != null) 89 | { 90 | var footerPart = wpDocument.MainDocumentPart.AddNewPart(); 91 | var footerPartId = wpDocument.MainDocumentPart.GetIdOfPart(footerPart); 92 | sectionProperties.AppendChild(new FooterReference { Id = footerPartId, Type = HeaderFooterValues.Default }); 93 | var footer = new Footer(TableConverter.Convert(page.Parameters.Footer ?? defaultFooter, wpDocument)); 94 | footer.Save(footerPart); 95 | } 96 | return sectionProperties; 97 | } 98 | 99 | private static int? GetMargin(int? fromPage, 100 | int? fromDocument) 101 | { 102 | if (fromPage.HasValue || fromDocument.HasValue) 103 | return (fromPage ?? fromDocument) * OpenXmlUnits.Dxa; 104 | return null; 105 | } 106 | 107 | private static PageOrientationValues ConvertOrientation(PageOrientation orientation) 108 | { 109 | switch (orientation) 110 | { 111 | case PageOrientation.Portrait: 112 | return PageOrientationValues.Portrait; 113 | case PageOrientation.Landscape: 114 | return PageOrientationValues.Landscape; 115 | default: 116 | throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null); 117 | } 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Converters/ParagraphConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using DocumentFormat.OpenXml; 4 | using DocumentFormat.OpenXml.Packaging; 5 | using DocumentFormat.OpenXml.Wordprocessing; 6 | using ReportDotNet.Core; 7 | using FontFamily = System.Drawing.FontFamily; 8 | using Paragraph = ReportDotNet.Core.Paragraph; 9 | 10 | namespace ReportDotNet.Docx.Converters 11 | { 12 | internal static class ParagraphConverter 13 | { 14 | public static OpenXmlElement Convert(Paragraph paragraph, 15 | WordprocessingDocument document, 16 | int? defaultFontSize = null) 17 | { 18 | var docxParagraph = new DocumentFormat.OpenXml.Wordprocessing.Paragraph(); 19 | var parameters = paragraph.Parameters; 20 | 21 | var paragraphProperties = new ParagraphProperties(); 22 | 23 | if (parameters.Alignment.HasValue) 24 | paragraphProperties.Justification = new Justification { Val = GetJustificationValues(parameters.Alignment.Value) }; 25 | 26 | if (parameters.SpaceBetweenLines.HasValue) 27 | paragraphProperties.SpacingBetweenLines = new SpacingBetweenLines 28 | { 29 | LineRule = LineSpacingRuleValues.Auto, 30 | Line = (240 * parameters.SpaceBetweenLines.Value).ToString() 31 | }; 32 | 33 | var runProperties = new RunProperties(); 34 | if (parameters.Bold) 35 | runProperties.Bold = new Bold(); 36 | 37 | var fontSize = parameters.FontSize ?? defaultFontSize; 38 | if (fontSize.HasValue) 39 | { 40 | var rPr = new ParagraphMarkRunProperties(new FontSize { Val = (fontSize.Value * 2).ToString() }, 41 | new FontSizeComplexScript { Val = (fontSize.Value * 2).ToString() }); 42 | paragraphProperties.AppendChild(rPr); 43 | runProperties.FontSize = new FontSize { Val = (fontSize.Value * 2).ToString() }; 44 | runProperties.FontSizeComplexScript = new FontSizeComplexScript { Val = (fontSize.Value * 2).ToString() }; 45 | } 46 | 47 | var defaultFont = FontFamily.GenericSansSerif.Name; 48 | runProperties.RunFonts = new RunFonts 49 | { 50 | Ascii = defaultFont, 51 | ComplexScript = defaultFont, 52 | HighAnsi = defaultFont 53 | }; 54 | 55 | if (parameters.BackgroundColor.HasValue) 56 | runProperties.Shading = new Shading 57 | { 58 | Val = ShadingPatternValues.Clear, 59 | Color = "auto", 60 | Fill = parameters.BackgroundColor.Value.ToHex() 61 | }; 62 | 63 | foreach (var run in parameters.Parts.Select(x => GetRun(document, x, runProperties))) 64 | docxParagraph.AppendChild(run); 65 | docxParagraph.ParagraphProperties = paragraphProperties; 66 | return docxParagraph; 67 | } 68 | 69 | private static Run GetRun(WordprocessingDocument document, 70 | Part part, 71 | RunProperties properties) 72 | { 73 | var run = new Run { RunProperties = properties.CloneNode(true) }; 74 | 75 | var textPart = part as TextPart; 76 | if (textPart != null) 77 | { 78 | if (textPart.Text == null) 79 | return run; 80 | 81 | var lines = textPart.Text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); 82 | for (var i = 0; i < lines.Length; i++) 83 | { 84 | if (i > 0) 85 | run.AppendChild(new Break()); 86 | if (!string.IsNullOrEmpty(lines[i])) 87 | run.AppendChild(new Text(lines[i]) { Space = SpaceProcessingModeValues.Preserve }); 88 | } 89 | 90 | return run; 91 | } 92 | 93 | var fieldPart = part as FieldPart; 94 | if (fieldPart != null) 95 | { 96 | run.AppendChild(new SimpleField { Instruction = GetInsructionForField(fieldPart.Field) }); 97 | return run; 98 | } 99 | 100 | var picturePart = part as PicturePart; 101 | if (picturePart != null) 102 | { 103 | if (picturePart.Picture?.IsEmpty() == false) 104 | run.AppendChild(PictureConverter.Convert(picturePart.Picture, document)); 105 | return run; 106 | } 107 | 108 | var stubPicturePart = part as StubPicturePart; 109 | if (stubPicturePart != null) 110 | { 111 | if (stubPicturePart.StubPicture != null) 112 | run.AppendChild(PictureConverter.Convert(stubPicturePart.StubPicture, document)); 113 | return run; 114 | } 115 | 116 | throw new InvalidOperationException($"Can't process part of paragraph with type {part.GetType().FullName}"); 117 | } 118 | 119 | private static JustificationValues GetJustificationValues(Alignment alignment) 120 | { 121 | switch (alignment) 122 | { 123 | case Alignment.Left: 124 | return JustificationValues.Left; 125 | case Alignment.Center: 126 | return JustificationValues.Center; 127 | case Alignment.Right: 128 | return JustificationValues.Right; 129 | case Alignment.Both: 130 | return JustificationValues.Both; 131 | default: 132 | throw new ArgumentOutOfRangeException(nameof(alignment), alignment, null); 133 | } 134 | } 135 | 136 | private static string GetInsructionForField(Field field) 137 | { 138 | switch (field) 139 | { 140 | case Field.PageNumber: 141 | return " PAGE \\* MERGEFORMAT "; 142 | case Field.PageCount: 143 | return " NUMPAGES \\* MERGEFORMAT "; 144 | default: 145 | throw new ArgumentOutOfRangeException(nameof(field), field, null); 146 | } 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Converters/PictureConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.IO; 3 | using DocumentFormat.OpenXml; 4 | using DocumentFormat.OpenXml.Drawing; 5 | using DocumentFormat.OpenXml.Drawing.Wordprocessing; 6 | using DocumentFormat.OpenXml.Packaging; 7 | using DocumentFormat.OpenXml.Wordprocessing; 8 | using ReportDotNet.Core; 9 | using Anchor = DocumentFormat.OpenXml.Drawing.Wordprocessing.Anchor; 10 | using BlipFill = DocumentFormat.OpenXml.Drawing.Pictures.BlipFill; 11 | using Color = System.Drawing.Color; 12 | using NonVisualDrawingProperties = DocumentFormat.OpenXml.Drawing.Pictures.NonVisualDrawingProperties; 13 | using NonVisualPictureDrawingProperties = DocumentFormat.OpenXml.Drawing.Pictures.NonVisualPictureDrawingProperties; 14 | using NonVisualPictureProperties = DocumentFormat.OpenXml.Drawing.Pictures.NonVisualPictureProperties; 15 | using Outline = DocumentFormat.OpenXml.Drawing.Outline; 16 | using Picture = ReportDotNet.Core.Picture; 17 | using ShapeProperties = DocumentFormat.OpenXml.Drawing.Pictures.ShapeProperties; 18 | using VerticalAlignment = DocumentFormat.OpenXml.Drawing.Wordprocessing.VerticalAlignment; 19 | 20 | namespace ReportDotNet.Docx.Converters 21 | { 22 | internal static class PictureConverter 23 | { 24 | internal static OpenXmlElement Convert(StubPicture picture, 25 | WordprocessingDocument document) 26 | { 27 | return Convert(picture.Parameters.Bytes, 28 | picture.Parameters.MaxWidth, 29 | picture.Parameters.MaxHeight, 30 | picture.Parameters.OffsetX, 31 | picture.Parameters.OffsetY, 32 | picture.Parameters.Description, 33 | picture.Parameters.Color, 34 | document); 35 | } 36 | 37 | internal static OpenXmlElement Convert(Picture picture, 38 | WordprocessingDocument document) 39 | { 40 | return Convert(picture.Parameters.Bytes, 41 | picture.Parameters.MaxWidth, 42 | picture.Parameters.MaxHeight, 43 | picture.Parameters.OffsetX, 44 | picture.Parameters.OffsetY, 45 | picture.Parameters.Description, 46 | null, 47 | document); 48 | } 49 | 50 | private static OpenXmlElement Convert(byte[] bytes, 51 | int maxWidth, 52 | int maxHeight, 53 | int? offsetX, 54 | int? offsetY, 55 | string description, 56 | Color? color, 57 | WordprocessingDocument document) 58 | { 59 | var imagePart = document.MainDocumentPart.AddImagePart(ImagePartType.Png); 60 | imagePart.FeedData(new MemoryStream(bytes)); 61 | int width, height; 62 | using (var img = Image.FromStream(new MemoryStream(bytes))) 63 | { 64 | width = img.Width; 65 | height = img.Height; 66 | } 67 | 68 | var offsets = FillRectangleOffsetsCalculator.Calculate(new Size(width, height), new Size(maxWidth, maxHeight)); 69 | var offset = new Offset { X = 0, Y = 0 }; 70 | if (offsetX.HasValue) 71 | offset.X = offsetX * OpenXmlUnits.EmuPerPixel; 72 | if (offsetY.HasValue) 73 | offset.Y = offsetY * OpenXmlUnits.EmuPerPixel; 74 | 75 | return new Drawing( 76 | new Anchor( 77 | new WrapNone(), 78 | new DocProperties 79 | { 80 | Id = 1, 81 | Name = "name", 82 | Description = description 83 | }, 84 | new Graphic 85 | { 86 | GraphicData = new GraphicData(new DocumentFormat.OpenXml.Drawing.Pictures.Picture 87 | { 88 | NonVisualPictureProperties = new NonVisualPictureProperties 89 | { 90 | NonVisualDrawingProperties = new NonVisualDrawingProperties 91 | { 92 | Id = 0, 93 | Name = "name" 94 | }, 95 | NonVisualPictureDrawingProperties = 96 | new NonVisualPictureDrawingProperties() 97 | }, 98 | BlipFill = new BlipFill(new Stretch 99 | { 100 | FillRectangle = new FillRectangle 101 | { 102 | Top = offsets.Top, 103 | Left = offsets.Left, 104 | Bottom = offsets.Bottom, 105 | Right = offsets.Right 106 | } 107 | }) 108 | { 109 | Blip = new Blip(color != null 110 | ? new ColorReplacement 111 | { 112 | RgbColorModelHex = 113 | new RgbColorModelHex 114 | { 115 | Val = 116 | color 117 | .Value 118 | .ToHex() 119 | } 120 | } 121 | : null) 122 | { 123 | Embed = document.MainDocumentPart.GetIdOfPart(imagePart), 124 | } 125 | }, 126 | ShapeProperties = new ShapeProperties(new PresetGeometry 127 | { 128 | Preset = ShapeTypeValues.Rectangle, 129 | AdjustValueList = new AdjustValueList() 130 | }, 131 | color != null 132 | ? new Outline(new SolidFill 133 | { 134 | RgbColorModelHex = 135 | new RgbColorModelHex 136 | { 137 | Val 138 | = color 139 | .Value 140 | .ToHex() 141 | } 142 | }) 143 | : null) 144 | { 145 | Transform2D = new Transform2D 146 | { 147 | Offset = offset, 148 | Extents = new Extents 149 | { 150 | Cx = maxWidth * OpenXmlUnits 151 | .EmuPerPixel, 152 | Cy = maxHeight * OpenXmlUnits 153 | .EmuPerPixel 154 | } 155 | } 156 | } 157 | }) 158 | { 159 | Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" 160 | } 161 | }) 162 | { 163 | DistanceFromTop = 0, 164 | DistanceFromBottom = 0, 165 | DistanceFromLeft = 0, 166 | DistanceFromRight = 0, 167 | SimplePos = false, 168 | RelativeHeight = 0, 169 | BehindDoc = false, 170 | Locked = false, 171 | LayoutInCell = true, 172 | AllowOverlap = true, 173 | SimplePosition = new SimplePosition { X = 0, Y = 0 }, 174 | HorizontalPosition = new HorizontalPosition 175 | { 176 | RelativeFrom = HorizontalRelativePositionValues.Column, 177 | HorizontalAlignment = new HorizontalAlignment("right") 178 | }, 179 | VerticalPosition = new VerticalPosition 180 | { 181 | RelativeFrom = VerticalRelativePositionValues.Paragraph, 182 | VerticalAlignment = new VerticalAlignment("top") 183 | }, 184 | Extent = new Extent 185 | { 186 | Cx = maxWidth * OpenXmlUnits.EmuPerPixel, 187 | Cy = maxHeight * OpenXmlUnits.EmuPerPixel 188 | }, 189 | EffectExtent = new EffectExtent 190 | { 191 | LeftEdge = 0, 192 | TopEdge = 0, 193 | RightEdge = 0, 194 | BottomEdge = 0 195 | } 196 | }); 197 | } 198 | } 199 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Converters/RowConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using DocumentFormat.OpenXml; 4 | using DocumentFormat.OpenXml.Packaging; 5 | using DocumentFormat.OpenXml.Wordprocessing; 6 | using ReportDotNet.Core; 7 | using Table = ReportDotNet.Core.Table; 8 | 9 | namespace ReportDotNet.Docx.Converters 10 | { 11 | internal static class RowConverter 12 | { 13 | public static TableRow Convert(Row row, 14 | WordprocessingDocument document, 15 | Table table, 16 | int[] tableWidths) 17 | { 18 | var docxRow = new TableRow(); 19 | var parameters = row.Parameters; 20 | if (parameters.Height.HasValue) 21 | { 22 | docxRow.TableRowProperties = new TableRowProperties(); 23 | docxRow.TableRowProperties.AppendChild(new TableRowHeight 24 | { 25 | Val = (uint) parameters.Height.Value * OpenXmlUnits.Dxa, 26 | HeightType = ConvertHeightType(parameters.HeightType) 27 | }); 28 | } 29 | 30 | var spans = Helpers.GetSpans(tableWidths, row.GetWidths()); 31 | var cells = parameters.GetCells().ToArray(); 32 | for (var i = 0; i < cells.Length; i++) 33 | docxRow.AppendChild(CellConverter.Convert(cells[i], document, table, spans[i])); 34 | 35 | return docxRow; 36 | } 37 | 38 | private static EnumValue ConvertHeightType(RowHeightType heightType) 39 | { 40 | switch (heightType) 41 | { 42 | case RowHeightType.Exact: 43 | return HeightRuleValues.Exact; 44 | case RowHeightType.AtLeast: 45 | return HeightRuleValues.AtLeast; 46 | default: 47 | throw new ArgumentOutOfRangeException(nameof(heightType), heightType, null); 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Converters/TableConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using DocumentFormat.OpenXml; 3 | using DocumentFormat.OpenXml.Packaging; 4 | using DocumentFormat.OpenXml.Wordprocessing; 5 | using ReportDotNet.Core; 6 | using Table = ReportDotNet.Core.Table; 7 | 8 | namespace ReportDotNet.Docx.Converters 9 | { 10 | internal static class TableConverter 11 | { 12 | public static OpenXmlElement Convert(Table table, 13 | WordprocessingDocument document) 14 | { 15 | var parameters = table.Parameters; 16 | var widths = Helpers.GetWidths(parameters.Width, parameters.Rows.Select(x => x.GetWidths())); 17 | var docxTable = CreateTable(parameters.Width, widths); 18 | foreach (var row in parameters.Rows) 19 | docxTable.AppendChild(RowConverter.Convert(row, document, table, widths)); 20 | return docxTable; 21 | } 22 | 23 | private static DocumentFormat.OpenXml.Wordprocessing.Table CreateTable(float width, 24 | int[] columnWidths) 25 | { 26 | var table = new DocumentFormat.OpenXml.Wordprocessing.Table(); 27 | var tableProperties = new TableProperties 28 | { 29 | TableWidth = new TableWidth 30 | { 31 | Width = (width * OpenXmlUnits.Dxa).ToString(), 32 | Type = TableWidthUnitValues.Dxa 33 | }, 34 | TableLayout = new TableLayout 35 | { 36 | Type = TableLayoutValues.Fixed 37 | } 38 | }; 39 | table.AppendChild(tableProperties); 40 | table.AppendChild(new TableGrid(columnWidths.Select(x => new GridColumn { Width = (x * OpenXmlUnits.Dxa).ToString() }))); 41 | return table; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/CreateExtensions.cs: -------------------------------------------------------------------------------- 1 | using ReportDotNet.Core; 2 | 3 | namespace ReportDotNet.Docx 4 | { 5 | public static class CreateExtensions 6 | { 7 | public static IDocument Docx(this Create _) => new DocxDocument(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/DocxDocument.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using DocumentFormat.OpenXml; 5 | using DocumentFormat.OpenXml.Packaging; 6 | using DocumentFormat.OpenXml.Wordprocessing; 7 | using ReportDotNet.Core; 8 | using ReportDotNet.Docx.Converters; 9 | using Table = ReportDotNet.Core.Table; 10 | 11 | namespace ReportDotNet.Docx 12 | { 13 | internal class DocxDocument: IDocument 14 | { 15 | private readonly WordprocessingDocument document; 16 | private readonly MemoryStream memoryStream; 17 | private readonly List pages = new List(); 18 | private PageLayout defaultPageLayout; 19 | private Table defaultFooter; 20 | 21 | public DocxDocument() 22 | { 23 | memoryStream = new MemoryStream(); 24 | document = WordprocessingDocument.Create(memoryStream, WordprocessingDocumentType.Document); 25 | document.AddMainDocumentPart(); 26 | document.MainDocumentPart.Document = new Document { Body = new Body() }; 27 | var stylesPart = document.MainDocumentPart.AddNewPart(); 28 | DefaultStyles.Styles.Value.Save(stylesPart); 29 | //todo looks ugly 30 | SetDefaultPageLayout(new PageLayout 31 | { 32 | Orientation = PageOrientation.Portrait 33 | }); 34 | } 35 | 36 | public byte[] Save() 37 | { 38 | foreach (var page in pages) 39 | { 40 | var isLastPage = page == pages.Last(); 41 | document.MainDocumentPart.Document.Body.Append(PageConverter.Convert(page, this, document, isLastPage)); 42 | } 43 | 44 | using (memoryStream) 45 | using (document) 46 | document.Close(); 47 | return memoryStream.ToArray(); 48 | } 49 | 50 | public IDocument AddPage(Page page) 51 | { 52 | pages.Add(page); 53 | return this; 54 | } 55 | 56 | public void SetDefaultPageLayout(PageLayout pageLayout) 57 | { 58 | defaultPageLayout = pageLayout; 59 | } 60 | 61 | PageLayout IDocument.GetDefaultPageLayout() 62 | { 63 | return defaultPageLayout; 64 | } 65 | 66 | public void SetDefaultFooter(Table table) 67 | { 68 | defaultFooter = table; 69 | } 70 | 71 | public Table GetDefaultFooter() 72 | { 73 | return defaultFooter; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using DocumentFormat.OpenXml; 3 | using DocumentFormat.OpenXml.Wordprocessing; 4 | using Color = System.Drawing.Color; 5 | 6 | namespace ReportDotNet.Docx 7 | { 8 | internal static class Extensions 9 | { 10 | public static string ToHex(this Color source) 11 | { 12 | return ByteToHex(source.R) + ByteToHex(source.G) + ByteToHex(source.B); 13 | } 14 | 15 | private static string ByteToHex(byte @byte) 16 | { 17 | var hex = @byte.ToString("X"); 18 | return hex.Length < 2 ? "0" + hex : hex; 19 | } 20 | 21 | public static T CloneNode(this T element, 22 | bool deep) 23 | where T: OpenXmlElement 24 | { 25 | return (T) element.CloneNode(deep); 26 | } 27 | 28 | public static SectionProperties GetSectionProperties(this Body body) 29 | { 30 | var sectionProperties = body.Elements().ToArray(); 31 | return sectionProperties.Any() 32 | ? sectionProperties.Single() 33 | : body.AppendChild(new SectionProperties()); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/FillRectangleOffsetsCalculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace ReportDotNet.Docx 5 | { 6 | internal static class FillRectangleOffsetsCalculator 7 | { 8 | private const int OpenxmlPercentMultiplier = 1000; 9 | 10 | public static Offsets Calculate(Size imageSize, 11 | Size placeholderSize) 12 | { 13 | var widthRatio = 1.0 * imageSize.Width / placeholderSize.Width; 14 | var heightRatio = 1.0 * imageSize.Height / placeholderSize.Height; 15 | 16 | return widthRatio > heightRatio 17 | ? CenterVertical(imageSize, placeholderSize) 18 | : CenterHorizontal(imageSize, placeholderSize); 19 | } 20 | 21 | private static Offsets CenterVertical(Size imageSize, 22 | Size placeholderSize) 23 | { 24 | var imageNewWidth = placeholderSize.Width; 25 | var imageNewHeight = imageSize.Height * imageNewWidth / imageSize.Width; 26 | 27 | var verticalOffset = (placeholderSize.Height - imageNewHeight) / 2.0; 28 | var verticalOffsetInWordPercents = PercentOf(verticalOffset, placeholderSize.Height) * OpenxmlPercentMultiplier; 29 | 30 | return Offsets.VerticalCenter(Convert.ToInt32(verticalOffsetInWordPercents)); 31 | } 32 | 33 | private static Offsets CenterHorizontal(Size imageSize, 34 | Size placeholderSize) 35 | { 36 | var newImageHeight = placeholderSize.Height; 37 | var newImageWidth = imageSize.Width * newImageHeight / imageSize.Height; 38 | 39 | var horizontalOffsetInPixels = (placeholderSize.Width - newImageWidth) / 2.0; 40 | var horizontalOffsetInWordPercents = PercentOf(horizontalOffsetInPixels, placeholderSize.Width) * OpenxmlPercentMultiplier; 41 | 42 | return Offsets.HorizontalCenter(Convert.ToInt32(horizontalOffsetInWordPercents)); 43 | } 44 | 45 | private static double PercentOf(double value, 46 | double of) 47 | { 48 | return value / of * 100; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/Offsets.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Docx 2 | { 3 | internal class Offsets 4 | { 5 | private Offsets(int? top, 6 | int? right, 7 | int? bottom, 8 | int? left) 9 | { 10 | Top = top; 11 | Bottom = bottom; 12 | Left = left; 13 | Right = right; 14 | } 15 | 16 | public static Offsets VerticalCenter(int verticalOffsetEachSide) 17 | { 18 | return new Offsets(NullIfZero(verticalOffsetEachSide), 19 | null, 20 | NullIfZero(verticalOffsetEachSide), 21 | null); 22 | } 23 | 24 | public static Offsets HorizontalCenter(int horizontalOffsetEachSide) 25 | { 26 | return new Offsets(null, 27 | NullIfZero(horizontalOffsetEachSide), 28 | null, 29 | NullIfZero(horizontalOffsetEachSide)); 30 | } 31 | 32 | private static int? NullIfZero(int offset) 33 | { 34 | return offset == 0 ? (int?) null : offset; 35 | } 36 | 37 | public int? Top { get; private set; } 38 | public int? Right { get; private set; } 39 | public int? Bottom { get; private set; } 40 | public int? Left { get; private set; } 41 | } 42 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/OpenXmlUnits.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ReportDotNet.Docx 4 | { 5 | internal static class OpenXmlUnits 6 | { 7 | public const int Dxa = 15; 8 | public const int EmuPerPixel = 9525; 9 | 10 | public static int FromMmTo20thOfPoint(double mm) 11 | { 12 | return (int) Math.Round(mm / 10 / 2.54 * 72 * 20, MidpointRounding.AwayFromZero); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/ReportDotNet.Docx.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 8 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/ReportDotNet.Docx.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ReportDotNet.Docx 5 | $version$ 6 | ReportDotNet.Docx 7 | mif.open@gmail.com 8 | mif.open@gmail.com 9 | https://github.com/mifopen/ReportDotNet 10 | false 11 | Simple .NET library for complex reports in docx format 12 | Hello! =) 13 | Copyright 2017 14 | reports docx reporting-engine reporting reporting-services docx-generator wysiwyg 15 | 16 | -------------------------------------------------------------------------------- /src/ReportDotNet.Docx/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/App/DirectoryWatcher.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.AspNetCore.SignalR; 3 | using ReportDotNet.Web.Controllers; 4 | 5 | namespace ReportDotNet.Web.App 6 | { 7 | public class DirectoryWatcher 8 | { 9 | private readonly IHubContext hubContext; 10 | 11 | public DirectoryWatcher(IHubContext hubContext) 12 | { 13 | this.hubContext = hubContext; 14 | } 15 | 16 | private FileSystemWatcher currentWatcher; 17 | private string currentFolder; 18 | 19 | public void Watch(string templateFolder) 20 | { 21 | if (currentFolder == templateFolder) 22 | return; 23 | 24 | lock (this) 25 | { 26 | if (currentFolder == templateFolder) 27 | return; 28 | 29 | currentFolder = templateFolder; 30 | 31 | currentWatcher?.Dispose(); 32 | currentWatcher = new FileSystemWatcher(templateFolder) 33 | { 34 | EnableRaisingEvents = true, 35 | IncludeSubdirectories = true 36 | }; 37 | currentWatcher.Changed += Handler; 38 | currentWatcher.Renamed += Handler; 39 | } 40 | } 41 | 42 | private void Handler(object sender, 43 | FileSystemEventArgs e) 44 | { 45 | hubContext.Clients.All.SendCoreAsync("reportUpdated", new object[0]); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/App/GoogleDocsUploader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Google.Apis.Auth.OAuth2; 5 | using Google.Apis.Drive.v3; 6 | using Google.Apis.Services; 7 | using Google.Apis.Util.Store; 8 | using File = Google.Apis.Drive.v3.Data.File; 9 | 10 | namespace ReportDotNet.Web.App 11 | { 12 | public class GoogleDocsUploader 13 | { 14 | private static readonly string[] Scopes = {DriveService.Scope.DriveFile}; 15 | 16 | public async Task Update(byte[] source) 17 | { 18 | UserCredential credential; 19 | 20 | using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read)) 21 | { 22 | credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( 23 | GoogleClientSecrets.Load(stream).Secrets, 24 | Scopes, 25 | "user", 26 | CancellationToken.None, 27 | new FileDataStore("token.json", true) 28 | ); 29 | } 30 | 31 | var driveService = new DriveService(new BaseClientService.Initializer 32 | { 33 | HttpClientInitializer = credential, 34 | ApplicationName = "Google Docs API .NET Quickstart" 35 | }); 36 | 37 | 38 | var fileMetadata = new File 39 | { 40 | // Name = Guid.NewGuid().ToString(), 41 | // MimeType = "application/vnd.google-apps.document" 42 | }; 43 | await using (var stream = new MemoryStream(source)) 44 | { 45 | var request = driveService.Files.Update(fileMetadata, 46 | "1K4_XzsMcgzIYjgNDgbMQUAMSNQibZy3Z92Y188AjTzk", 47 | stream, 48 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); 49 | 50 | request.Fields = "id"; 51 | await request.UploadAsync(); 52 | return request.ResponseBody.Id; 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/App/ReportHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | 3 | namespace ReportDotNet.Web.App 4 | { 5 | public class ReportHub: Hub 6 | { 7 | public void ReportUpdated() 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/App/ReportRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text.RegularExpressions; 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.CSharp; 10 | using Microsoft.Extensions.DependencyModel; 11 | using ReportDotNet.Core; 12 | using ReportDotNet.Docx; 13 | 14 | namespace ReportDotNet.Web.App 15 | { 16 | public class ReportRenderer 17 | { 18 | public RenderedReport Render(string templateDirectoryPath) 19 | { 20 | return File.Exists(Path.Combine(templateDirectoryPath, "Template.cs")) 21 | ? RenderReportDotNetTemplate(templateDirectoryPath) 22 | : RenderUnzipedDocx(templateDirectoryPath); 23 | } 24 | 25 | private static RenderedReport RenderReportDotNetTemplate(string templateDirectoryPath) 26 | { 27 | var templatePath = Path.Combine(templateDirectoryPath, "Template.cs"); 28 | var templateType = CreateTemplateType(templatePath); 29 | var log = new List(); 30 | Action logAction = (lineNumber, 31 | line, 32 | obj) => log.Add($"#{lineNumber}: {line}: {obj}"); 33 | var method = GetFillDocumentMethod(templateType); 34 | var document = Create.Document.Docx(); 35 | var arguments = method.GetParameters().Length == 2 36 | ? new object[] {document, logAction} 37 | : new object[] {document, logAction, templateDirectoryPath}; 38 | method.Invoke(null, arguments); 39 | 40 | return new RenderedReport 41 | { 42 | Log = log.ToArray(), 43 | Bytes = document.Save() 44 | }; 45 | } 46 | 47 | private static RenderedReport RenderUnzipedDocx(string templateDirectoryPath) 48 | { 49 | const string zipFileName = "somefile.docx"; 50 | File.Delete(zipFileName); 51 | ZipFile.CreateFromDirectory(templateDirectoryPath, zipFileName); 52 | var zipBytes = File.ReadAllBytes(zipFileName); 53 | File.Delete(zipFileName); 54 | return new RenderedReport 55 | { 56 | Log = new string[0], 57 | Bytes = zipBytes 58 | }; 59 | } 60 | 61 | private static Type CreateTemplateType(string templatePath) 62 | { 63 | var defaultCompileLibraries = DependencyContext.Default.CompileLibraries; 64 | // .Where(x => x.Name.Contains("ReportDotNet") 65 | // || x.Name == "Microsoft.NETCore.App"); 66 | var references = defaultCompileLibraries 67 | .SelectMany(cl => cl.ResolveReferencePaths()) 68 | .Select(asm => MetadataReference.CreateFromFile(asm)) 69 | .ToArray(); 70 | var compilation = CSharpCompilation.Create(assemblyName: "NewReport.dll", 71 | syntaxTrees: new[] {GetSyntaxTree(templatePath)}, 72 | references: references, 73 | options: new CSharpCompilationOptions( 74 | OutputKind.DynamicallyLinkedLibrary)); 75 | try 76 | { 77 | using (var ms = new MemoryStream()) 78 | { 79 | var result = compilation.Emit(ms); 80 | if (result.Success) 81 | { 82 | var types = Assembly.Load(ms.ToArray()).GetTypes().Where(x => x.Name == "Template").ToArray(); 83 | if (types.Length != 1) 84 | throw new Exception( 85 | $"There must be at least one type with name Template in example {templatePath}"); 86 | 87 | return types.Single(); 88 | } 89 | 90 | var failures = result.Diagnostics.Where(diagnostic => 91 | diagnostic.IsWarningAsError || 92 | diagnostic.Severity == DiagnosticSeverity.Error); 93 | throw new InvalidOperationException( 94 | string.Join(Environment.NewLine, failures.Select(x => $"{x.Id}: {x.GetMessage()}"))); 95 | } 96 | } 97 | finally 98 | { 99 | File.Delete(compilation.AssemblyName); 100 | } 101 | } 102 | 103 | private static MethodInfo GetFillDocumentMethod(Type type) 104 | { 105 | return type.GetMethods() 106 | .Single(m => 107 | { 108 | var parameters = m.GetParameters(); 109 | return m.IsStatic 110 | && parameters.Length >= 2 111 | && parameters[0].ParameterType == typeof(IDocument) 112 | && parameters[1].ParameterType == typeof(Action) 113 | && (parameters.Length == 2 || parameters[2].ParameterType == typeof(string)); 114 | }); 115 | } 116 | 117 | private static readonly Regex logRegex = 118 | new Regex("\\slog[(](.*)[)];", RegexOptions.Compiled | RegexOptions.Singleline); 119 | 120 | private static readonly Regex logParameterRegex = 121 | new Regex("\\sAction log", RegexOptions.Compiled | RegexOptions.Singleline); 122 | 123 | private static SyntaxTree GetSyntaxTree(string fileName) 124 | { 125 | var lines = File.ReadAllLines(fileName) 126 | .Select((l, 127 | i) => logParameterRegex.Replace(l, "Action log")) 128 | .Select((l, 129 | i) => logRegex.Replace(l, $"log({i + 1}, \"$1\", $1);")) 130 | .ToArray(); 131 | return CSharpSyntaxTree.ParseText(string.Join(Environment.NewLine, lines)); 132 | } 133 | 134 | public class RenderedReport 135 | { 136 | public byte[] Bytes { get; set; } 137 | public string[] Log { get; set; } 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.AspNetCore.Mvc; 7 | using ReportDotNet.Web.App; 8 | 9 | namespace ReportDotNet.Web.Controllers 10 | { 11 | public class HomeController : Controller 12 | { 13 | private readonly GoogleDocsUploader googleDocsUploader; 14 | private readonly ReportRenderer reportRenderer; 15 | private readonly DirectoryWatcher directoryWatcher; 16 | private readonly IWebHostEnvironment webHostEnvironment; 17 | 18 | public HomeController(GoogleDocsUploader googleDocsUploader, 19 | ReportRenderer reportRenderer, 20 | DirectoryWatcher directoryWatcher, 21 | IWebHostEnvironment webHostEnvironment) 22 | { 23 | this.googleDocsUploader = googleDocsUploader; 24 | this.reportRenderer = reportRenderer; 25 | this.directoryWatcher = directoryWatcher; 26 | this.webHostEnvironment = webHostEnvironment; 27 | } 28 | 29 | [HttpGet] 30 | public async Task Render(string templateName) 31 | { 32 | var templateDirectoryPath = GetTemplateDirectoryPath(templateName); 33 | var renderedReport = reportRenderer.Render(templateDirectoryPath); 34 | var googleDocFileId = await googleDocsUploader.Update(renderedReport.Bytes); 35 | return Json(new 36 | { 37 | Log = string.Join("
", renderedReport.Log), 38 | GoogleDocUrl = $"https://docs.google.com/document/d/{googleDocFileId}/edit" 39 | }); 40 | } 41 | 42 | [HttpGet] 43 | public FileContentResult GetDocx(string templateName) 44 | { 45 | var templateDirectoryPath = GetTemplateDirectoryPath(templateName); 46 | var renderedReport = reportRenderer.Render(templateDirectoryPath); 47 | return File(renderedReport.Bytes, 48 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 49 | "result.docx"); 50 | } 51 | 52 | private string GetTemplateDirectoryPath(string templateName) 53 | { 54 | var templateProjectDirectory = GetTemplateProjectDirectory(); 55 | var templateDirectoryPath = Path.Combine(templateProjectDirectory, templateName); 56 | EnsureTemplateDirectory(templateDirectoryPath, templateName, templateProjectDirectory); 57 | directoryWatcher.Watch(templateProjectDirectory); 58 | return templateDirectoryPath; 59 | } 60 | 61 | private static void EnsureTemplateDirectory(string templateDirectoryPath, 62 | string templateDirectoryName, 63 | string templateProjectDirectory) 64 | { 65 | if (!Directory.Exists(templateDirectoryPath)) 66 | { 67 | var directories = new DirectoryInfo(templateProjectDirectory) 68 | .EnumerateDirectories() 69 | .Select(x => x.Name) 70 | .Except(new[] {"bin", "obj", "Properties"}); 71 | throw new Exception($"Are you sure that directory {templateDirectoryName} exists in template project?" + 72 | $" There are only {string.Join(", ", directories)}."); 73 | } 74 | } 75 | 76 | private string GetTemplateProjectDirectory() 77 | { 78 | var webProjectPath = webHostEnvironment.ContentRootPath; 79 | return Path.Combine(webProjectPath, "Examples"); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/PaymentOrder/Data.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | 6 | namespace ReportDotNet.Web.Examples.PaymentOrder 7 | { 8 | public class Data 9 | { 10 | public DateTime Date { get; set; } 11 | public decimal Sum { get; set; } 12 | 13 | public string LegalName { get; set; } 14 | public string Okpo { get; set; } 15 | public string DocumentNumber { get; set; } 16 | public string BossFio { get; set; } 17 | public string AccountantFio { get; set; } 18 | public string PostOfTheBoss { get; set; } 19 | public string CorrespondingAccountingRecord { get; set; } 20 | public string Contractor { set; get; } 21 | public string Cause { get; set; } 22 | public string Appendix { get; set; } 23 | 24 | public string DateStr => Date.ToShortDateString(); 25 | 26 | public string Day => Date.Day.ToString().PadLeft(2, '0'); 27 | 28 | public string Month => Date.ToString("MMMM", CultureInfo.InvariantCulture); 29 | 30 | public string Year => Date.Year.ToString(); 31 | 32 | public string SumStr => Sum.ToString("F2"); 33 | 34 | public string SumRubles => Math.Floor(Sum).ToString(); 35 | 36 | public string SumCopecks => ((Sum - Math.Floor(Sum)) * 100).ToString().PadLeft(2, '0'); 37 | 38 | public string SumInWords => "ten hundreds"; 39 | 40 | public string LegalName33First => SplitIn2Lines(LegalName, 33)[0]; 41 | 42 | public string LegalName33Second => SplitIn2Lines(LegalName, 33)[1]; 43 | 44 | public string Contractor26First => SplitInManyLines(Contractor, 26, 35)[0]; 45 | 46 | public string Contractor26Second => SplitInManyLines(Contractor, 26, 35)[1]; 47 | 48 | public string Contractor26Third => SplitInManyLines(Contractor, 26, 35)[2]; 49 | 50 | public string Contractor45First => SplitIn2Lines(Contractor, 45)[0]; 51 | 52 | public string Contractor45Second => SplitIn2Lines(Contractor, 45)[1]; 53 | 54 | public string Cause26First => SplitInManyLines(Cause, 26, 35)[0]; 55 | 56 | public string Cause26Second => SplitInManyLines(Cause, 26, 35)[1]; 57 | 58 | public string Cause26Third => SplitInManyLines(Cause, 26, 35)[2]; 59 | 60 | public string Cause45First => SplitIn2Lines(Cause, 45)[0]; 61 | 62 | public string Cause45Second => SplitIn2Lines(Cause, 45)[1]; 63 | 64 | public string SumInWords36First => SplitIn2Lines(SumInWords, 36)[0]; 65 | 66 | public string SumInWords36Second => SplitIn2Lines(SumInWords, 36)[1]; 67 | 68 | private static string[] SplitInManyLines(string source, 69 | params int[] linesLength) 70 | { 71 | source = source ?? ""; 72 | var first = linesLength.Aggregate(new[] { source }, (current, 73 | l) => 74 | { 75 | var r = current.LastOrDefault(); 76 | return current.Take(current.Length - 2).Concat(SplitIn2Lines(r, l)).ToArray(); 77 | }); 78 | var second = Enumerable.Repeat(0, linesLength.Length + 1); 79 | return EagerZip(first, second) 80 | .Select(x => x.Key) 81 | .ToArray(); 82 | return new string[0]; 83 | } 84 | 85 | private static string[] SplitIn2Lines(string source, 86 | int line1MaxLength) 87 | { 88 | source = source ?? ""; 89 | var result = new string[2]; 90 | var totalSumParts = source.Split(' '); 91 | result[0] = ""; 92 | result[1] = ""; 93 | 94 | foreach (var t in totalSumParts) 95 | if (result[0].Length + t.Length <= line1MaxLength) 96 | result[0] += t + ' '; 97 | else 98 | break; 99 | if (result[0].Length <= source.Length) 100 | result[1] = source.Substring(result[0].Length); 101 | 102 | for (var i = 0; i < result.Length; ++i) 103 | result[i] = result[i].Trim(); 104 | return result; 105 | } 106 | 107 | private static IEnumerable> EagerZip(IEnumerable first, 108 | IEnumerable second) 109 | { 110 | return EagerZip(first, second, (x, 111 | y) => new KeyValuePair(x, y)); 112 | } 113 | 114 | private static IEnumerable EagerZip(IEnumerable first, 115 | IEnumerable second, 116 | Func createPair) 117 | { 118 | using (var secondEnumerator = second.GetEnumerator()) 119 | { 120 | var secondExhaust = false; 121 | foreach (var firstItem in first) 122 | yield return createPair(firstItem, 123 | secondExhaust || 124 | (secondExhaust = !secondEnumerator.MoveNext()) 125 | ? default(TSecond) 126 | : secondEnumerator.Current); 127 | while (secondEnumerator.MoveNext()) 128 | yield return createPair(default(TFirst), secondEnumerator.Current); 129 | } 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/Simple/Template.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using ReportDotNet.Core; 4 | using static ReportDotNet.Core.Factories; 5 | 6 | namespace ReportDotNet.Web.Examples.Simple 7 | { 8 | public static class Template 9 | { 10 | public static void FillDocument(IDocument document, 11 | Action log) 12 | { 13 | var footer = Table(300) 14 | .Add(Row(Cell() 15 | .Add(Paragraph() 16 | .Add(Field.PageNumber) 17 | .Add(" / ") 18 | .Add(Field.PageCount)))); 19 | document.AddPage(Page() 20 | .Orientation(PageOrientation.Portrait) 21 | .Footer(footer) 22 | .Add(Paragraph("This is the first paragraph on the first page"), 23 | Paragraph("\\n in text will add new line \n just like that"), 24 | Paragraph("Text with right alignment") 25 | .Alignment(Alignment.Right), 26 | Paragraph("Bold 14pt with background color") 27 | .Bold() 28 | .BackgroundColor(Color.Aqua) 29 | .FontSize(14), 30 | Paragraph("Let's add another page and play with tables"))); 31 | 32 | var page = Page() 33 | .Orientation(PageOrientation.Landscape) 34 | .Footer(footer) 35 | .Add(Table(400) 36 | .Borders(Borders.All) 37 | .Add(Row(Cell("1,1", 200) 38 | .Alignment(Alignment.Right), 39 | Cell("1,2", 200) 40 | .BorderSize(3)), 41 | Row(Cell("2,1", 200) 42 | .BackgroundColor(Color.Aqua), 43 | Cell("2,2", 100) 44 | .MergeDown() 45 | .Alignment(Alignment.Center) 46 | .VerticalAlignment(VerticalAlignment.Center) 47 | .TextDirection(TextDirection.RightLeft_TopBottom)), 48 | Row(Cell("3,1", 200), 49 | Cell("3,2", 100) 50 | .MergeUp()))); 51 | 52 | page.Add(Paragraph()); 53 | 54 | var table = Table(700) 55 | .Borders(Borders.Top | Borders.Bottom); 56 | for (var i = 16; i < 100; i += 10) 57 | table.Add(Row(i) 58 | .HeightType(RowHeightType.Exact) 59 | .Add(Cell($"row with height = {i}") 60 | .VerticalAlignment(VerticalAlignment.Center))); 61 | 62 | page.Add(table); 63 | 64 | page.Add(Paragraph() 65 | .Add(StubPicture() 66 | .MaxWidth(200) 67 | .MaxHeight(100) 68 | .Color(Color.Brown))); 69 | 70 | document.AddPage(page); 71 | 72 | var variableName = "Some log text"; 73 | log(variableName); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/[Content_Types].xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 9 | 11 | 13 | 15 | 16 | 17 | 19 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/_rels/.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/docProps/app.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0 5 | 1 6 | 3 7 | 21 8 | Microsoft Office Word 9 | 0 10 | 1 11 | 1 12 | false 13 | 14 | false 15 | 23 16 | false 17 | false 18 | 15.0000 19 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/docProps/core.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Мостовой Вячеслав Игоревич 8 | 9 | 10 | Мостовой Вячеслав Игоревич 11 | 2 12 | 2017-05-16T05:32:00Z 13 | 2017-05-16T05:32:00Z 14 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/word/_rels/document.xml.rels: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/word/document.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Some text 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/word/fontTable.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/word/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/word/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/word/theme/theme1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/SimpleXml/word/webSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/StampAndSigns/AccountantSign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mifopen/ReportDotNet/6a5b69baee0894a39833da4d9957f60f6631ee47/src/ReportDotNet.Web/Examples/StampAndSigns/AccountantSign.png -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/StampAndSigns/BossSign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mifopen/ReportDotNet/6a5b69baee0894a39833da4d9957f60f6631ee47/src/ReportDotNet.Web/Examples/StampAndSigns/BossSign.png -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/StampAndSigns/Data.cs: -------------------------------------------------------------------------------- 1 | namespace ReportDotNet.Web.Examples.StampAndSigns 2 | { 3 | public class Data 4 | { 5 | public byte[] Stamp { get; set; } 6 | public byte[] BossSign { get; set; } 7 | public byte[] AccountantSign { get; set; } 8 | public string BossPosition { get; set; } 9 | public string BossName { get; set; } 10 | public string AccountantName { get; set; } 11 | public bool HasNoAccountant { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/StampAndSigns/Stamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mifopen/ReportDotNet/6a5b69baee0894a39833da4d9957f60f6631ee47/src/ReportDotNet.Web/Examples/StampAndSigns/Stamp.png -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Examples/StampAndSigns/Template.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using ReportDotNet.Core; 4 | using static ReportDotNet.Core.Factories; 5 | 6 | namespace ReportDotNet.Web.Examples.StampAndSigns 7 | { 8 | public static class Template 9 | { 10 | public static void FillDocument(IDocument document, 11 | Action log, 12 | string currentTemplateDirectory) 13 | { 14 | FillDocument(document, new Data 15 | { 16 | AccountantName = "Accountant Name", 17 | AccountantSign = File.ReadAllBytes(Path.Combine(currentTemplateDirectory, "AccountantSign.png")), 18 | BossName = "Boss Name", 19 | BossPosition = "Boss Position", 20 | BossSign = File.ReadAllBytes(Path.Combine(currentTemplateDirectory, "BossSign.png")), 21 | HasNoAccountant = false, 22 | Stamp = File.ReadAllBytes(Path.Combine(currentTemplateDirectory, "Stamp.png")) 23 | }); 24 | } 25 | 26 | private static void FillDocument(IDocument document, 27 | Data data) 28 | { 29 | document.AddPage(Page() 30 | .Orientation(PageOrientation.Portrait) 31 | .Margin(10, 0, 0, 0) 32 | .Size(new PageSize 33 | { 34 | Width = 143, 35 | Height = 51 36 | }) 37 | .Add(GetPageContent(data))); 38 | } 39 | 40 | private static TableBuilder GetPageContent(Data data) 41 | { 42 | return Table(545) 43 | .FontSize(9) 44 | .Add(Row() 45 | .Height(40) 46 | .Add(Cell(data.BossPosition, 280) 47 | .Borders(Borders.Bottom) 48 | .Alignment(Alignment.Center), 49 | Cell(80) 50 | .Borders(Borders.Bottom) 51 | .Add(Paragraph() 52 | .Add(Picture(data.BossSign) 53 | .MaxWidth(100) 54 | .MaxHeight(45) 55 | .OffsetX(10))), 56 | Cell(20) 57 | .Add(Paragraph() 58 | .Add(Picture(data.Stamp) 59 | .MaxWidth(170) 60 | .MaxHeight(170) 61 | .OffsetX(-100) 62 | .OffsetY(20))), 63 | Cell(data.BossName) 64 | .Borders(Borders.Bottom) 65 | .Alignment(Alignment.Center) 66 | ), 67 | Row(RemarkCell("(position)") 68 | .Width(280), 69 | RemarkCell("(signature)") 70 | .Width(80), 71 | Cell(20), 72 | RemarkCell("(full name)")), 73 | data.HasNoAccountant 74 | ? Row() 75 | : Row() 76 | .Height(40) 77 | .Add(Cell(280), 78 | Cell(80) 79 | .Add(Paragraph() 80 | .Add(Picture(data.AccountantSign) 81 | .MaxWidth(100) 82 | .MaxHeight(45) 83 | .OffsetX(10))) 84 | .Borders(Borders.Bottom), 85 | Cell(20), 86 | Cell(data.AccountantName) 87 | .Borders(Borders.Bottom) 88 | .Alignment(Alignment.Center)), 89 | data.HasNoAccountant 90 | ? Row() 91 | : Row(Cell(280), 92 | RemarkCell("(signature)") 93 | .Width(80), 94 | Cell(20), 95 | RemarkCell("(full name)")) 96 | ); 97 | } 98 | 99 | // Cell style reusing example 100 | private static CellBuilder RemarkCell(string text) 101 | { 102 | return Cell(text) 103 | .Alignment(Alignment.Center) 104 | .VerticalAlignment(VerticalAlignment.Top) 105 | .FontSize(7); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace ReportDotNet.Web 6 | { 7 | public class Program 8 | { 9 | public static async Task Main(string[] args) 10 | { 11 | await CreateHostBuilder(args).Build().RunAsync(); 12 | } 13 | 14 | private static IHostBuilder CreateHostBuilder(string[] args) => 15 | Host.CreateDefaultBuilder(args) 16 | .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); 17 | } 18 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/ReportDotNet.Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | ReportDotNet.Web 6 | true 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using ReportDotNet.Web.App; 6 | 7 | namespace ReportDotNet.Web 8 | { 9 | public class Startup 10 | { 11 | public void ConfigureServices(IServiceCollection services) 12 | { 13 | services.AddSignalR(); 14 | services.AddMvc(); 15 | 16 | services.AddSingleton(); 17 | services.AddSingleton(); 18 | services.AddSingleton(); 19 | } 20 | 21 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 22 | { 23 | if (env.IsDevelopment()) 24 | { 25 | app.UseDeveloperExceptionPage(); 26 | } 27 | else 28 | { 29 | app.UseExceptionHandler("/Error"); 30 | } 31 | 32 | app.UseDefaultFiles(); 33 | app.UseStaticFiles(); 34 | app.UseRouting(); 35 | app.UseEndpoints(endpoints => 36 | { 37 | endpoints.MapControllers(); 38 | endpoints.MapDefaultControllerRoute(); 39 | endpoints.MapHub("/reportHub"); 40 | }); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/ReportDotNet.Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/wwwroot/index.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100% 3 | } 4 | 5 | body { 6 | background-color: rgb(82, 86, 89); 7 | margin: 0; 8 | } 9 | 10 | .pageWrap { 11 | display: flex; 12 | height: 100%; 13 | } 14 | 15 | .leftPanel { 16 | width: 250px; 17 | } 18 | 19 | .section { 20 | margin-bottom: 10px; 21 | } 22 | 23 | .header { 24 | font: 18pt monospace; 25 | font-weight: bold; 26 | } 27 | 28 | .text { 29 | color: white; 30 | font: 15pt monospace; 31 | } 32 | 33 | .googleDocIframe { 34 | height: 100%; 35 | width: 100%; 36 | display: none; 37 | } 38 | 39 | .lds-roller { 40 | display: inline-block; 41 | position: relative; 42 | width: 80px; 43 | height: 80px; 44 | } 45 | 46 | .lds-roller div { 47 | animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 48 | transform-origin: 40px 40px; 49 | } 50 | 51 | .lds-roller div:after { 52 | content: " "; 53 | display: block; 54 | position: absolute; 55 | width: 7px; 56 | height: 7px; 57 | border-radius: 50%; 58 | background: #fff; 59 | margin: -4px 0 0 -4px; 60 | } 61 | 62 | .lds-roller div:nth-child(1) { 63 | animation-delay: -0.036s; 64 | } 65 | 66 | .lds-roller div:nth-child(1):after { 67 | top: 63px; 68 | left: 63px; 69 | } 70 | 71 | .lds-roller div:nth-child(2) { 72 | animation-delay: -0.072s; 73 | } 74 | 75 | .lds-roller div:nth-child(2):after { 76 | top: 68px; 77 | left: 56px; 78 | } 79 | 80 | .lds-roller div:nth-child(3) { 81 | animation-delay: -0.108s; 82 | } 83 | 84 | .lds-roller div:nth-child(3):after { 85 | top: 71px; 86 | left: 48px; 87 | } 88 | 89 | .lds-roller div:nth-child(4) { 90 | animation-delay: -0.144s; 91 | } 92 | 93 | .lds-roller div:nth-child(4):after { 94 | top: 72px; 95 | left: 40px; 96 | } 97 | 98 | .lds-roller div:nth-child(5) { 99 | animation-delay: -0.18s; 100 | } 101 | 102 | .lds-roller div:nth-child(5):after { 103 | top: 71px; 104 | left: 32px; 105 | } 106 | 107 | .lds-roller div:nth-child(6) { 108 | animation-delay: -0.216s; 109 | } 110 | 111 | .lds-roller div:nth-child(6):after { 112 | top: 68px; 113 | left: 24px; 114 | } 115 | 116 | .lds-roller div:nth-child(7) { 117 | animation-delay: -0.252s; 118 | } 119 | 120 | .lds-roller div:nth-child(7):after { 121 | top: 63px; 122 | left: 17px; 123 | } 124 | 125 | .lds-roller div:nth-child(8) { 126 | animation-delay: -0.288s; 127 | } 128 | 129 | .lds-roller div:nth-child(8):after { 130 | top: 56px; 131 | left: 12px; 132 | } 133 | 134 | @keyframes lds-roller { 135 | 0% { 136 | transform: rotate(0deg); 137 | } 138 | 100% { 139 | transform: rotate(360deg); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/wwwroot/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ReportDotNet 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |
15 |
Template
16 | 17 | 18 |
19 | 20 |
21 |
22 |
23 |
Log
24 | 25 |
26 | 27 |
28 |
29 | 30 |
Timestamp
31 | 32 |
33 |
34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/wwwroot/index.js: -------------------------------------------------------------------------------- 1 | window.onload = function () { 2 | const connection = new signalR.HubConnectionBuilder() 3 | .withUrl("/reportHub") 4 | .configureLogging(signalR.LogLevel.Information) 5 | .build(); 6 | connection.on("ReportUpdated", updateRenderingStatus); 7 | connection.start().catch(err => console.error(err.toString())); 8 | 9 | const $loader = $("#loader"); 10 | const $iframe = $("#google_doc_iframe"); 11 | 12 | $("#downloadButton").click(() => { 13 | window.location.href = `/Home/GetDocx?templateName=${getTemplateName()}`; 14 | }); 15 | 16 | initTemplateName(); 17 | 18 | updateRenderingStatus(); 19 | 20 | function updateRenderingStatus() { 21 | $("#error_iframe").hide(); 22 | $.getJSON(`/Home/Render?templateName=${getTemplateName()}`) 23 | .done(res => { 24 | $("#log").html(res.log || "none"); 25 | if ($iframe.attr("src") !== res.googleDocUrl) 26 | $iframe.attr("src", res.googleDocUrl); 27 | $loader.hide(); 28 | $iframe.show(); 29 | }) 30 | .fail(handleError) 31 | .always(() => $("#time").html(new Date().toLocaleTimeString())); 32 | } 33 | 34 | function initTemplateName() { 35 | $("#templateName").val(localStorage.getItem("templateName") || "Simple"); 36 | $("#templateNameButton").click(() => { 37 | localStorage.setItem("templateName", getTemplateName()); 38 | $loader.show(); 39 | updateRenderingStatus(); 40 | }); 41 | } 42 | 43 | function resetTemplateName() { 44 | localStorage.removeItem("templateName"); 45 | $("#templateName").val("Simple"); 46 | } 47 | 48 | function getTemplateName() { 49 | return $("#templateName").val(); 50 | } 51 | 52 | function handleError(er) { 53 | $loader.hide(); 54 | resetTemplateName(); 55 | const iframe = $("#error_iframe"); 56 | iframe.show(); 57 | iframe[0].src = `data:text/html;charset=utf-8,${escape(er.responseText)}`; 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /src/ReportDotNet.Web/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@aspnet/signalr@^1.0.0": 6 | version "1.0.0" 7 | resolved "https://registry.yarnpkg.com/@aspnet/signalr/-/signalr-1.0.0.tgz#26b10a32014a65c540cc5053cffa883c320788ce" 8 | -------------------------------------------------------------------------------- /tests/ReportDotNet.Core.Tests/CellBuilderTest.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using Shouldly; 3 | using Xunit; 4 | using static ReportDotNet.Core.Factories; 5 | 6 | namespace ReportDotNet.Core.Tests 7 | { 8 | public class CellBuilderTest 9 | { 10 | [Fact] 11 | public void Simple() 12 | { 13 | var cellBuilder = Cell() 14 | .Alignment(Alignment.Center) 15 | .BackgroundColor(Color.Blue) 16 | .Bold() 17 | .BorderSize(7) 18 | .Borders(Borders.Bottom | Borders.Right) 19 | .Borders(top: BorderStyle.DotDash, bottom: BorderStyle.DotDotDash) 20 | .FontSize(26) 21 | .Margin(left: 3, right: 9) 22 | .MergeDown() 23 | .MergeUp() 24 | .MergeLeft() 25 | .MergeRight() 26 | .SpaceBetweenLines(1.15) 27 | .TextDirection(TextDirection.LeftRight_TopBottom) 28 | .VerticalAlignment(VerticalAlignment.Center) 29 | .Width(32) 30 | .Add("some text"); 31 | 32 | var cellParameters = cellBuilder.Build().Parameters; 33 | cellParameters.Width.ShouldBe(32); 34 | cellParameters.BackgroundColor.ShouldBe(Color.Blue); 35 | cellParameters.BorderSize.ShouldBe(7); 36 | cellParameters.Borders.ShouldBe(Borders.Bottom | Borders.Right); 37 | cellParameters.TopBorderStyle.ShouldBe(BorderStyle.DotDash); 38 | cellParameters.LeftBorderStyle.ShouldBe(BorderStyle.Single); 39 | cellParameters.BottomBorderStyle.ShouldBe(BorderStyle.DotDotDash); 40 | cellParameters.RightBorderStyle.ShouldBe(BorderStyle.Single); 41 | cellParameters.MarginLeft.ShouldBe(3); 42 | cellParameters.MarginRight.ShouldBe(9); 43 | cellParameters.MergeDown.ShouldBeTrue(); 44 | cellParameters.MergeUp.ShouldBeTrue(); 45 | cellParameters.MergeRight.ShouldBeTrue(); 46 | cellParameters.MergeLeft.ShouldBeTrue(); 47 | cellParameters.TextDirection.ShouldBe(TextDirection.LeftRight_TopBottom); 48 | cellParameters.VerticalAlignment.ShouldBe(VerticalAlignment.Center); 49 | cellParameters.Width.ShouldBe(32); 50 | var paragraphParameters = cellParameters.Paragraph.Parameters; 51 | paragraphParameters.Alignment.ShouldBe(Alignment.Center); 52 | paragraphParameters.FontSize.ShouldBe(26); 53 | paragraphParameters.Bold.ShouldBeTrue(); 54 | paragraphParameters.SpaceBetweenLines.ShouldBe(1.15); 55 | paragraphParameters.Parts 56 | .ShouldHaveSingleItem() 57 | .ShouldBeOfType() 58 | .Text.ShouldBe("some text"); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /tests/ReportDotNet.Core.Tests/ReportDotNet.Core.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 8 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/ReportDotNet.Core.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | --------------------------------------------------------------------------------