├── .github └── workflows │ ├── build-validation.yml │ ├── nuget-publish.yml │ ├── pre-release.yml │ └── pullrequest-pr.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── PdfLibCore.sln ├── README.md ├── assets └── icon.png ├── src ├── ExampleApp │ ├── Data │ │ ├── A17_FlightPlan.pdf │ │ ├── sample-10-mb.pdf │ │ └── sample-images.pdf │ ├── DataProvider.cs │ ├── Dockerfile │ ├── ExampleApp.csproj │ └── Program.cs ├── PdfLibCore.FreeImage │ ├── PdfLibCore.FreeImage.csproj │ └── PdfiumBitmapExtensions.cs ├── PdfLibCore.ImageSharp │ ├── PdfLibCore.ImageSharp.csproj │ └── PdfiumBitmapExtensions.cs ├── PdfLibCore.MagickNet │ ├── PdfLibCore.MagickNet.csproj │ └── PdfiumBitmapExtensions.cs ├── PdfLibCore.SkiaSharp │ ├── PdfLibCore.SkiaSharp.csproj │ └── PdfiumBitmapExtensions.cs ├── PdfLibCore │ ├── BmpStream.cs │ ├── Enums │ │ ├── ActionTypes.cs │ │ ├── BitmapFormats.cs │ │ ├── DocumentPermissions.cs │ │ ├── DuplexTypes.cs │ │ ├── FlattenFlags.cs │ │ ├── FlattenResults.cs │ │ ├── FontTypes.cs │ │ ├── MetadataTags.cs │ │ ├── ObjectTypes.cs │ │ ├── PageModes.cs │ │ ├── PageObjTypes.cs │ │ ├── PageOrientations.cs │ │ ├── PathFillModes.cs │ │ ├── RenderingFlags.cs │ │ ├── RenderingStatus.cs │ │ ├── SaveFlags.cs │ │ ├── SearchFlags.cs │ │ └── ZoomModes.cs │ ├── PdfAction.cs │ ├── PdfBookmark.cs │ ├── PdfDestination.cs │ ├── PdfDestinationCollection.cs │ ├── PdfDocument.cs │ ├── PdfLibCore.csproj │ ├── PdfPage.cs │ ├── PdfPageCollection.cs │ ├── PdfPageLocation.cs │ ├── PdfPageRange.cs │ ├── Pdfium.cs │ ├── Pdfium.g.cs │ ├── Pdfium.tt │ ├── Pdfium.xml │ ├── PdfiumBitmap.cs │ ├── PdfiumException.cs │ ├── PinnedGCHandle.cs │ ├── Types │ │ ├── FPDF_COLOR.cs │ │ ├── FPDF_ERR.cs │ │ ├── FPDF_FILE_ACCESS.cs │ │ ├── FPDF_IMAGEOBJ_METADATA.cs │ │ ├── FPDF_LIBRARY_CONFIG.cs │ │ ├── FPDF_TEXT_RENDERMODE.cs │ │ ├── FPDF_TypeDef.cs │ │ ├── FPDF_TypeDef.tt │ │ ├── FS_MATRIX.cs │ │ ├── FS_QUADPOINTSF.cs │ │ ├── FS_RECTF.cs │ │ ├── FS_SIZEF.cs │ │ ├── IFSDK_PAUSE.cs │ │ ├── IHandle.cs │ │ └── NativeWrapper.cs │ └── common.ttinclude └── UnitTests │ └── PdfLibCore.UnitTests │ ├── Data │ └── test.pdf │ ├── PdfDocumentTests.cs │ ├── PdfLibCore.UnitTests.csproj │ ├── PdfPageTests.cs │ └── PdfPageToImageTests.cs └── tools ├── build_nuget.sh └── get_pdfium.sh /.github/workflows/build-validation.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - '**.csproj' 8 | - '**.cs' 9 | 10 | env: 11 | DOTNET_VERSION: '6.0' # set this to the dot net version to use 12 | 13 | jobs: 14 | build: 15 | name: build-${{matrix.os}} 16 | runs-on: ${{matrix.os}} 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest, windows-latest, macOS-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Setup .NET Core 24 | uses: actions/setup-dotnet@v1 25 | with: 26 | dotnet-version: ${{ env.DOTNET_VERSION }} 27 | 28 | - name: Install dependencies 29 | run: dotnet restore 30 | 31 | - name: Build 32 | run: | 33 | dotnet build --configuration Release --no-restore 34 | 35 | - name: Test 36 | run: | 37 | dotnet test --no-restore --verbosity normal 38 | -------------------------------------------------------------------------------- /.github/workflows/nuget-publish.yml: -------------------------------------------------------------------------------- 1 | name: Release Package Version 2 | on: 3 | push: 4 | tags: 5 | - 2.* 6 | 7 | env: 8 | DOTNET_VERSION: '6.0' # set this to the dot net version to use 9 | 10 | jobs: 11 | build: 12 | if: github.event.base_ref == 'refs/heads/master' 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Print Tag Ref 17 | run: echo $GITHUB_REF 18 | 19 | - name: Extract Version Number 20 | uses: actions-ecosystem/action-regex-match@v2 21 | id: regex-match 22 | with: 23 | text: ${{ github.ref }} 24 | regex: '[0-9.]+' 25 | 26 | - name: Print Version Number 27 | run: echo '${{ steps.regex-match.outputs.match }}' 28 | 29 | - uses: actions/checkout@v2 30 | - name: Setup .NET Core 31 | uses: actions/setup-dotnet@v1 32 | with: 33 | dotnet-version: ${{ env.DOTNET_VERSION }} 34 | 35 | - name: Restore dependencies 36 | run: dotnet restore 37 | 38 | - name: Build 39 | run: | 40 | dotnet build --configuration Release --no-restore 41 | 42 | - name: Test 43 | run: | 44 | dotnet test --no-restore --verbosity normal 45 | 46 | - name: Publish PdfLibCore 47 | uses: drusellers/publish-nuget@master 48 | with: 49 | project-file-path: src/PdfLibCore/PdfLibCore.csproj 50 | version: ${{ steps.regex-match.outputs.match }} 51 | tag-commit: false 52 | nuget-key: ${{secrets.NUGET_API_KEY}} 53 | 54 | - name: Publish PdfLibCore.FreeImage 55 | uses: drusellers/publish-nuget@master 56 | with: 57 | project-file-path: src/PdfLibCore.FreeImage/PdfLibCore.FreeImage.csproj 58 | version: ${{ steps.regex-match.outputs.match }} 59 | tag-commit: false 60 | nuget-key: ${{secrets.NUGET_API_KEY}} 61 | 62 | - name: Publish PdfLibCore.ImageSharp 63 | uses: drusellers/publish-nuget@master 64 | with: 65 | project-file-path: src/PdfLibCore.ImageSharp/PdfLibCore.ImageSharp.csproj 66 | version: ${{ steps.regex-match.outputs.match }} 67 | tag-commit: false 68 | nuget-key: ${{secrets.NUGET_API_KEY}} 69 | 70 | - name: Publish PdfLibCore.MagickNet 71 | uses: drusellers/publish-nuget@master 72 | with: 73 | project-file-path: src/PdfLibCore.MagickNet/PdfLibCore.MagickNet.csproj 74 | version: ${{ steps.regex-match.outputs.match }} 75 | tag-commit: false 76 | nuget-key: ${{secrets.NUGET_API_KEY}} 77 | 78 | - name: Publish PdfLibCore.SkiaSharp 79 | uses: drusellers/publish-nuget@master 80 | with: 81 | project-file-path: src/PdfLibCore.SkiaSharp/PdfLibCore.SkiaSharp.csproj 82 | version: ${{ steps.regex-match.outputs.match }} 83 | tag-commit: false 84 | nuget-key: ${{secrets.NUGET_API_KEY}} 85 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | name: Pre Release Package Version 2 | on: 3 | push: 4 | tags: 5 | - 2.* 6 | jobs: 7 | build: 8 | if: github.event.base_ref == 'refs/heads/dev/v3' 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - name: Print Tag Ref 13 | run: echo $GITHUB_REF 14 | 15 | - name: Extract Version Number 16 | uses: actions-ecosystem/action-regex-match@v2 17 | id: regex-match 18 | with: 19 | text: ${{ github.ref }} 20 | regex: '[0-9.]+-pre[0-9]+' 21 | 22 | - name: Print Version Number 23 | run: echo '${{ steps.regex-match.outputs.match }}' 24 | 25 | - uses: actions/checkout@v2 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: 6.0 30 | 31 | - name: Restore dependencies 32 | run: dotnet restore 33 | 34 | - name: Build 35 | run: | 36 | dotnet build --configuration Release --no-restore 37 | 38 | - name: Test 39 | run: | 40 | dotnet test --no-restore --verbosity normal 41 | 42 | - name: Publish PdfLibCore 43 | uses: drusellers/publish-nuget@master 44 | with: 45 | project-file-path: src/PdfLibCore/PdfLibCore.csproj 46 | version: ${{ steps.regex-match.outputs.match }} 47 | tag-commit: false 48 | nuget-key: ${{secrets.NUGET_API_KEY}} 49 | 50 | - name: Publish PdfLibCore.FreeImage 51 | uses: drusellers/publish-nuget@master 52 | with: 53 | project-file-path: src/PdfLibCore.FreeImage/PdfLibCore.FreeImage.csproj 54 | version: ${{ steps.regex-match.outputs.match }} 55 | tag-commit: false 56 | nuget-key: ${{secrets.NUGET_API_KEY}} 57 | 58 | - name: Publish PdfLibCore.ImageSharp 59 | uses: drusellers/publish-nuget@master 60 | with: 61 | project-file-path: src/PdfLibCore.ImageSharp/PdfLibCore.ImageSharp.csproj 62 | version: ${{ steps.regex-match.outputs.match }} 63 | tag-commit: false 64 | nuget-key: ${{secrets.NUGET_API_KEY}} 65 | 66 | - name: Publish PdfLibCore.MagickNet 67 | uses: drusellers/publish-nuget@master 68 | with: 69 | project-file-path: src/PdfLibCore.MagickNet/PdfLibCore.MagickNet.csproj 70 | version: ${{ steps.regex-match.outputs.match }} 71 | tag-commit: false 72 | nuget-key: ${{secrets.NUGET_API_KEY}} 73 | 74 | - name: Publish PdfLibCore.SkiaSharp 75 | uses: drusellers/publish-nuget@master 76 | with: 77 | project-file-path: src/PdfLibCore.SkiaSharp/PdfLibCore.SkiaSharp.csproj 78 | version: ${{ steps.regex-match.outputs.match }} 79 | tag-commit: false 80 | nuget-key: ${{secrets.NUGET_API_KEY}} 81 | -------------------------------------------------------------------------------- /.github/workflows/pullrequest-pr.yml: -------------------------------------------------------------------------------- 1 | name: pull request 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | paths: 7 | - '**.cs' 8 | - '**.csproj' 9 | 10 | env: 11 | DOTNET_VERSION: '6.0' # set this to the dot net version to use 12 | 13 | jobs: 14 | build: 15 | name: build-${{matrix.os}} 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest, windows-latest, macOS-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Setup .NET Core 24 | uses: actions/setup-dotnet@v1 25 | with: 26 | dotnet-version: ${{ env.DOTNET_VERSION }} 27 | 28 | - name: Install dependencies 29 | run: dotnet restore 30 | 31 | - name: Build 32 | run: | 33 | dotnet build --no-restore 34 | 35 | - name: Test 36 | run: | 37 | dotnet test --no-restore --verbosity normal 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | *.idea 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Mono auto generated files 19 | mono_crash.* 20 | 21 | # Build results 22 | [Dd]ebug/ 23 | [Dd]ebugPublic/ 24 | [Rr]elease/ 25 | [Rr]eleases/ 26 | x64/ 27 | x86/ 28 | [Aa][Rr][Mm]/ 29 | [Aa][Rr][Mm]64/ 30 | bld/ 31 | [Bb]in/ 32 | [Oo]bj/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # StyleCop 67 | StyleCopReport.xml 68 | 69 | # Files built by Visual Studio 70 | *_i.c 71 | *_p.c 72 | *_h.h 73 | *.ilk 74 | *.meta 75 | *.obj 76 | *.iobj 77 | *.pch 78 | *.pdb 79 | *.ipdb 80 | *.pgc 81 | *.pgd 82 | *.rsp 83 | *.sbr 84 | *.tlb 85 | *.tli 86 | *.tlh 87 | *.tmp 88 | *.tmp_proj 89 | *_wpftmp.csproj 90 | *.log 91 | *.vspscc 92 | *.vssscc 93 | .builds 94 | *.pidb 95 | *.svclog 96 | *.scc 97 | 98 | # Chutzpah Test files 99 | _Chutzpah* 100 | 101 | # Visual C++ cache files 102 | ipch/ 103 | *.aps 104 | *.ncb 105 | *.opendb 106 | *.opensdf 107 | *.sdf 108 | *.cachefile 109 | *.VC.db 110 | *.VC.VC.opendb 111 | 112 | # Visual Studio profiler 113 | *.psess 114 | *.vsp 115 | *.vspx 116 | *.sap 117 | 118 | # Visual Studio Trace Files 119 | *.e2e 120 | 121 | # TFS 2012 Local Workspace 122 | $tf/ 123 | 124 | # Guidance Automation Toolkit 125 | *.gpState 126 | 127 | # ReSharper is a .NET coding add-in 128 | _ReSharper*/ 129 | *.[Rr]e[Ss]harper 130 | *.DotSettings.user 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | 351 | # Ionide (cross platform F# VS Code tools) working folder 352 | .ionide/ 353 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: PdfLibCore.sln 3 | mono: none 4 | dotnet: 3.1.200 5 | os: 6 | - linux 7 | - osx 8 | script: 9 | - cd src 10 | - dotnet restore 11 | - dotnet build -c Release -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jan Baarssen 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 | -------------------------------------------------------------------------------- /PdfLibCore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfLibCore", "src\PdfLibCore\PdfLibCore.csproj", "{30AE626B-48CC-42F7-8FBA-C4BCC379581F}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleApp", "src\ExampleApp\ExampleApp.csproj", "{09AC490D-E1EB-442D-B48C-42DBE6EB6948}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfLibCore.ImageSharp", "src\PdfLibCore.ImageSharp\PdfLibCore.ImageSharp.csproj", "{5E0525AA-DF07-4F5A-8A80-2C54E922047D}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfLibCore.MagickNet", "src\PdfLibCore.MagickNet\PdfLibCore.MagickNet.csproj", "{043157F2-19A4-42CD-9768-C72D95A08F34}" 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfLibCore.SkiaSharp", "src\PdfLibCore.SkiaSharp\PdfLibCore.SkiaSharp.csproj", "{3679701B-B0F9-40AD-B340-4838A698D991}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfLibCore.FreeImage", "src\PdfLibCore.FreeImage\PdfLibCore.FreeImage.csproj", "{0A84A8FC-D7C4-4D1D-B223-D884C97828B2}" 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{820F5B9F-0B28-41E1-8D72-243F000A2C1C}" 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfLibCore.UnitTests", "src\UnitTests\PdfLibCore.UnitTests\PdfLibCore.UnitTests.csproj", "{AFCCC5F8-6BB1-466E-A69F-2EE2ADC59CFC}" 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4781BB81-D064-44FC-994D-9C2568A05560}" 20 | ProjectSection(SolutionItems) = preProject 21 | LICENSE.txt = LICENSE.txt 22 | README.md = README.md 23 | tools\get_pdfium.sh = tools\get_pdfium.sh 24 | tools\build_nuget.sh = tools\build_nuget.sh 25 | EndProjectSection 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Release|Any CPU = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {30AE626B-48CC-42F7-8FBA-C4BCC379581F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {30AE626B-48CC-42F7-8FBA-C4BCC379581F}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {30AE626B-48CC-42F7-8FBA-C4BCC379581F}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {30AE626B-48CC-42F7-8FBA-C4BCC379581F}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {09AC490D-E1EB-442D-B48C-42DBE6EB6948}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {09AC490D-E1EB-442D-B48C-42DBE6EB6948}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {09AC490D-E1EB-442D-B48C-42DBE6EB6948}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {09AC490D-E1EB-442D-B48C-42DBE6EB6948}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {5E0525AA-DF07-4F5A-8A80-2C54E922047D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {5E0525AA-DF07-4F5A-8A80-2C54E922047D}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {5E0525AA-DF07-4F5A-8A80-2C54E922047D}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {5E0525AA-DF07-4F5A-8A80-2C54E922047D}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {043157F2-19A4-42CD-9768-C72D95A08F34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {043157F2-19A4-42CD-9768-C72D95A08F34}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {043157F2-19A4-42CD-9768-C72D95A08F34}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {043157F2-19A4-42CD-9768-C72D95A08F34}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {3679701B-B0F9-40AD-B340-4838A698D991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {3679701B-B0F9-40AD-B340-4838A698D991}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {3679701B-B0F9-40AD-B340-4838A698D991}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {3679701B-B0F9-40AD-B340-4838A698D991}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {0A84A8FC-D7C4-4D1D-B223-D884C97828B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {0A84A8FC-D7C4-4D1D-B223-D884C97828B2}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {0A84A8FC-D7C4-4D1D-B223-D884C97828B2}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {0A84A8FC-D7C4-4D1D-B223-D884C97828B2}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {AFCCC5F8-6BB1-466E-A69F-2EE2ADC59CFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {AFCCC5F8-6BB1-466E-A69F-2EE2ADC59CFC}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {AFCCC5F8-6BB1-466E-A69F-2EE2ADC59CFC}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {AFCCC5F8-6BB1-466E-A69F-2EE2ADC59CFC}.Release|Any CPU.Build.0 = Release|Any CPU 61 | EndGlobalSection 62 | GlobalSection(NestedProjects) = preSolution 63 | {AFCCC5F8-6BB1-466E-A69F-2EE2ADC59CFC} = {820F5B9F-0B28-41E1-8D72-243F000A2C1C} 64 | EndGlobalSection 65 | EndGlobal 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![build](https://github.com/jbaarssen/PdfLibCore/actions/workflows/build-validation.yml/badge.svg)](https://github.com/jbaarssen/PdfLibCore/actions/workflows/build-validation.yml) 2 | [![Release Package Version](https://github.com/jbaarssen/PdfLibCore/actions/workflows/nuget-publish.yml/badge.svg)](https://github.com/jbaarssen/PdfLibCore/actions/workflows/nuget-publish.yml) 3 | 4 | # PdfLibCore 5 | PdfLib CORE is a fast PDF editing and reading library for modern .NET Core applications. 6 | 7 | ## Example: Creating images from all pages in a PDF 8 | 9 | Opening a PDF file can be done either through providing a filepath, a stream or a byte array.. 10 | 11 | ```c# 12 | var dpiX, dpiY = 300D; 13 | var i = 0; 14 | 15 | using var pdfDocument = new PdfDocument(File.Open(<>, FileMode.Open)); 16 | foreach (var page in pdfDocument.Pages) 17 | { 18 | using var pdfPage = page; 19 | var pageWidth = (int) (dpiX * pdfPage.Size.Width / 72); 20 | var pageHeight = (int) (dpiY * pdfPage.Size.Height / 72); 21 | 22 | using var bitmap = new PdfiumBitmap(pageWidth, pageHeight, true); 23 | pdfPage.Render(bitmap, PageOrientations.Normal, RenderingFlags.LcdText); 24 | using var stream = bitmap.AsBmpStream(dpiX, dpiY); 25 | 26 | // <<< do something with your stream...>>> 27 | } 28 | ``` 29 | 30 | ## Notes 31 | If you encounter errors in T4 generation on Windows VS, convert the line endings to CRLF and it will work. -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbaarssen/PdfLibCore/6b155b257e49ac02da2bc33f52c4f199d50cf5bf/assets/icon.png -------------------------------------------------------------------------------- /src/ExampleApp/Data/A17_FlightPlan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbaarssen/PdfLibCore/6b155b257e49ac02da2bc33f52c4f199d50cf5bf/src/ExampleApp/Data/A17_FlightPlan.pdf -------------------------------------------------------------------------------- /src/ExampleApp/Data/sample-10-mb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbaarssen/PdfLibCore/6b155b257e49ac02da2bc33f52c4f199d50cf5bf/src/ExampleApp/Data/sample-10-mb.pdf -------------------------------------------------------------------------------- /src/ExampleApp/Data/sample-images.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbaarssen/PdfLibCore/6b155b257e49ac02da2bc33f52c4f199d50cf5bf/src/ExampleApp/Data/sample-images.pdf -------------------------------------------------------------------------------- /src/ExampleApp/DataProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace ExampleApp 6 | { 7 | public static class DataProvider 8 | { 9 | // ReSharper disable PossibleNullReferenceException 10 | private static string Name => typeof(DataProvider).FullName![..typeof(DataProvider).FullName!.IndexOf("Provider", StringComparison.Ordinal)]; 11 | 12 | // This file is only here to resolve the assembly from the integration test projects 13 | // you can find the references by running Shift + F12 on the class 14 | // Please do not delete this file as long as there are references to it 15 | 16 | /// 17 | /// Retrieves one of the embedded test files which can be used for various purposes 18 | /// 19 | /// The name of the file as located in the "DataProvider" folder 20 | /// The file in the form of a byte array 21 | public static byte[] GetEmbeddedResource(string resourceName) => 22 | GetEmbeddedResourceAsStream(resourceName).ToArray(); 23 | 24 | /// 25 | /// Retrieves one of the embedded test files which can be used for various purposes 26 | /// 27 | /// The name of the file as located in the "DataProvider" folder 28 | /// The file in the form of a stream 29 | public static MemoryStream GetEmbeddedResourceAsStream(string resourceName) 30 | { 31 | using var resourceStream = Assembly.GetAssembly(typeof(DataProvider))?.GetManifestResourceStream($"{Name}.{resourceName}"); 32 | var memStream = new MemoryStream(); 33 | resourceStream?.CopyTo(memStream); 34 | memStream.Seek(0, SeekOrigin.Begin); 35 | return memStream; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/ExampleApp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 2 | ARG BUILD_CONFIGURATION=Debug 3 | ENV ASPNETCORE_ENVIRONMENT=Development 4 | ENV ASPNETCORE_URLS=http://+:80 5 | ENV DOTNET_USE_POLLING_FILE_WATCHER=true 6 | EXPOSE 80 7 | 8 | WORKDIR /src 9 | COPY ["ExampleApp/ExampleApp.csproj", "ExampleApp/"] 10 | COPY ["PdfLibCore/PdfLibCore.csproj", "PdfLibCore/"] 11 | COPY . . 12 | WORKDIR "/src/ExampleApp" 13 | RUN dotnet build -c $BUILD_CONFIGURATION 14 | 15 | RUN echo "exec dotnet run --no-build --no-launch-profile -c $BUILD_CONFIGURATION -- \"\$@\"" > /entrypoint.sh 16 | 17 | ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] -------------------------------------------------------------------------------- /src/ExampleApp/ExampleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 9 7 | 8 | 9 | 10 | AnyCPU 11 | 12 | 13 | 14 | AnyCPU 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/ExampleApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | using PdfLibCore; 8 | using PdfLibCore.Enums; 9 | using SixLabors.ImageSharp; 10 | using SixLabors.ImageSharp.Formats.Bmp; 11 | using SixLabors.ImageSharp.Processing; 12 | 13 | // ReSharper disable UnusedMember.Local 14 | 15 | namespace ExampleApp 16 | { 17 | [ExcludeFromCodeCoverage] 18 | public static class Program 19 | { 20 | private const int Max = 865; 21 | private const string Destination = "c:/temp/output"; 22 | 23 | public static async Task Main(string[] args) 24 | { 25 | Console.WriteLine("Start..."); 26 | 27 | var stopWatch = new Stopwatch(); 28 | stopWatch.Start(); 29 | 30 | await Task.WhenAll( 31 | Run("A17_FlightPlan.pdf", Path.Combine(Destination, "lightplan")), 32 | Run("sample-10-mb.pdf", Path.Combine(Destination, "sample-10")), 33 | Run("A17_FlightPlan.pdf", Path.Combine(Destination, "flightplan2")), 34 | Run("sample-images.pdf", Path.Combine(Destination, "sample-images"))); 35 | 36 | stopWatch.Stop(); 37 | // Get the elapsed time as a TimeSpan value. 38 | var ts = stopWatch.Elapsed; 39 | 40 | // Format and display the TimeSpan value. 41 | Console.WriteLine("RunTime " + $"{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds:00}.{ts.Milliseconds / 10:00}"); 42 | } 43 | 44 | private static async Task Run(string name, string destination) 45 | { 46 | if (Directory.Exists(destination)) 47 | { 48 | Directory.Delete(destination, true); 49 | } 50 | Directory.CreateDirectory(destination!); 51 | 52 | Console.WriteLine($"Starting with {name}"); 53 | await foreach (var page in GetImagesFromPdf(name)) 54 | { 55 | using var image = await Image.LoadAsync(page.Item2, new BmpDecoder()); 56 | var data = await ResizeAndSaveToJpeg(image); 57 | await File.WriteAllBytesAsync(Path.Combine(destination, $"{page.i}.jpeg"), data); 58 | } 59 | 60 | Console.WriteLine($"Done with {name}"); 61 | } 62 | 63 | private static async IAsyncEnumerable<(int i, Stream)> GetImagesFromPdf(string name) 64 | { 65 | var input = DataProvider.GetEmbeddedResource(name); 66 | using var pdfDocument = new PdfDocument(input); 67 | 68 | Console.WriteLine($"Getting {pdfDocument.Pages.Count} images for {name}"); 69 | 70 | foreach (var page in pdfDocument.Pages) 71 | { 72 | using var bitmap = new PdfiumBitmap( 73 | (int) (page.Size.Width / 72 * 144D), 74 | (int) (page.Size.Height / 72 * 144D), 75 | true); 76 | page.Render(bitmap, PageOrientations.Normal, RenderingFlags.LcdText); 77 | yield return (page.Index, bitmap.AsBmpStream()); 78 | } 79 | 80 | Console.WriteLine($"Done getting images for {name}"); 81 | } 82 | 83 | private static async Task ResizeAndSaveToJpeg(Image image) 84 | { 85 | var isPortrait = image.Width <= image.Height; 86 | using var clone = image.Clone(src => src.Resize(new ResizeOptions 87 | { 88 | Mode = ResizeMode.Max, 89 | Size = isPortrait 90 | ? new Size(Max, 0) 91 | : new Size(0, Max), 92 | Sampler = KnownResamplers.Lanczos3, 93 | Compand = true 94 | })); 95 | 96 | using var ms = new MemoryStream(); 97 | await clone.SaveAsJpegAsync(ms); 98 | return ms.ToArray(); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/PdfLibCore.FreeImage/PdfLibCore.FreeImage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | x64 6 | true 7 | Jan Baarssen, Jan Kokenberg 8 | PdfLib.Core 9 | PdfLib CORE is a fast PDF editing and reading library for modern .NET core applications. 10 | true 11 | 1701;1702;1591;0649 12 | MIT 13 | LICENSE.txt 14 | https://github.com/jbaarssen/PdfLibCore 15 | https://github.com/jbaarssen/PdfLibCore 16 | PDF; Reader; netstandard; PDFium; JPEG; JPG 17 | icon.png 18 | $(ClientOfficialVersion) 19 | $(ClientPreviewVersion) 20 | nightly-$(CurrentDate) 21 | preview 22 | $(ClientVersion) 23 | $(ClientVersion)-$(VersionSuffix) 24 | $(ClientVersion) 25 | true 26 | false 27 | PdfLibCore.CoreCompat.System.Drawing 28 | 29 | 30 | 31 | AnyCPU 32 | 33 | 34 | 35 | AnyCPU 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | icon.png 49 | icon.png 50 | 51 | 52 | LICENSE.txt 53 | LICENSE.txt 54 | 55 | 56 | 57 | 58 | 59 | all 60 | runtime; build; native; contentfiles; analyzers; buildtransitive 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/PdfLibCore.FreeImage/PdfiumBitmapExtensions.cs: -------------------------------------------------------------------------------- 1 | using FreeImageAPI; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace PdfLibCore.FreeImageAPI 5 | { 6 | public static class PdfiumBitmapExtensions 7 | { 8 | public static FIBITMAP AsImage(this PdfiumBitmap bitmap, double dpiX = 72, double dpiY = 72) => 9 | FreeImage.LoadFromStream(bitmap.AsBmpStream(dpiX, dpiY)); 10 | } 11 | } -------------------------------------------------------------------------------- /src/PdfLibCore.ImageSharp/PdfLibCore.ImageSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | any 6 | true 7 | Jan Baarssen, Jan Kokenberg 8 | PdfLib.Core 9 | PdfLib CORE is a fast PDF editing and reading library for modern .NET core applications. 10 | true 11 | 1701;1702;1591;0649 12 | MIT 13 | LICENSE.txt 14 | https://github.com/jbaarssen/PdfLibCore 15 | https://github.com/jbaarssen/PdfLibCore 16 | PDF; Reader; netstandard; PDFium; JPEG; JPG 17 | icon.png 18 | $(ClientOfficialVersion) 19 | $(ClientPreviewVersion) 20 | nightly-$(CurrentDate) 21 | preview 22 | $(ClientVersion) 23 | $(ClientVersion)-$(VersionSuffix) 24 | $(ClientVersion) 25 | true 26 | false 27 | 28 | 29 | 30 | AnyCPU 31 | 32 | 33 | 34 | AnyCPU 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | icon.png 48 | icon.png 49 | 50 | 51 | LICENSE.txt 52 | LICENSE.txt 53 | 54 | 55 | 56 | 57 | 58 | all 59 | runtime; build; native; contentfiles; analyzers; buildtransitive 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/PdfLibCore.ImageSharp/PdfiumBitmapExtensions.cs: -------------------------------------------------------------------------------- 1 | using SixLabors.ImageSharp; 2 | using SixLabors.ImageSharp.Formats.Bmp; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace PdfLibCore.ImageSharp 6 | { 7 | public static class PdfiumBitmapExtensions 8 | { 9 | public static Image AsImage(this PdfiumBitmap bitmap, double dpiX = 72, double dpiY = 72) => 10 | new BmpDecoder().Decode(Configuration.Default, bitmap.AsBmpStream(dpiX, dpiY), default); 11 | } 12 | } -------------------------------------------------------------------------------- /src/PdfLibCore.MagickNet/PdfLibCore.MagickNet.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | x64 6 | true 7 | Jan Baarssen, Jan Kokenberg 8 | PdfLib.Core 9 | PdfLib CORE is a fast PDF editing and reading library for modern .NET core applications. 10 | true 11 | 1701;1702;1591;0649 12 | MIT 13 | LICENSE.txt 14 | https://github.com/jbaarssen/PdfLibCore 15 | https://github.com/jbaarssen/PdfLibCore 16 | PDF; Reader; netstandard; PDFium; JPEG; JPG 17 | icon.png 18 | $(ClientOfficialVersion) 19 | $(ClientPreviewVersion) 20 | nightly-$(CurrentDate) 21 | preview 22 | $(ClientVersion) 23 | $(ClientVersion)-$(VersionSuffix) 24 | $(ClientVersion) 25 | true 26 | false 27 | 28 | 29 | 30 | AnyCPU 31 | 32 | 33 | 34 | AnyCPU 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | icon.png 48 | icon.png 49 | 50 | 51 | LICENSE.txt 52 | LICENSE.txt 53 | 54 | 55 | 56 | 57 | 58 | all 59 | runtime; build; native; contentfiles; analyzers; buildtransitive 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/PdfLibCore.MagickNet/PdfiumBitmapExtensions.cs: -------------------------------------------------------------------------------- 1 | using ImageMagick; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace PdfLibCore.MagickNet 5 | { 6 | public static class PdfiumBitmapExtensions 7 | { 8 | public static MagickImage AsImage(this PdfiumBitmap bitmap, double dpiX = 72, double dpiY = 72) => 9 | new MagickImage(bitmap.AsBmpStream(dpiX, dpiY)); 10 | } 11 | } -------------------------------------------------------------------------------- /src/PdfLibCore.SkiaSharp/PdfLibCore.SkiaSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | x64 6 | true 7 | Jan Baarssen, Jan Kokenberg 8 | PdfLib.Core 9 | PdfLib CORE is a fast PDF editing and reading library for modern .NET core applications. 10 | true 11 | 1701;1702;1591;0649 12 | MIT 13 | LICENSE.txt 14 | https://github.com/jbaarssen/PdfLibCore 15 | https://github.com/jbaarssen/PdfLibCore 16 | PDF; Reader; netstandard; PDFium; JPEG; JPG 17 | icon.png 18 | $(ClientOfficialVersion) 19 | $(ClientPreviewVersion) 20 | nightly-$(CurrentDate) 21 | preview 22 | $(ClientVersion) 23 | $(ClientVersion)-$(VersionSuffix) 24 | $(ClientVersion) 25 | true 26 | false 27 | 28 | 29 | 30 | AnyCPU 31 | 32 | 33 | 34 | AnyCPU 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | icon.png 49 | icon.png 50 | 51 | 52 | LICENSE.txt 53 | LICENSE.txt 54 | 55 | 56 | 57 | 58 | 59 | all 60 | runtime; build; native; contentfiles; analyzers; buildtransitive 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/PdfLibCore.SkiaSharp/PdfiumBitmapExtensions.cs: -------------------------------------------------------------------------------- 1 | using SkiaSharp; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace PdfLibCore.SkiaSharp 5 | { 6 | public static class PdfiumBitmapExtensions 7 | { 8 | public static SKBitmap AsImage(this PdfiumBitmap bitmap, double dpiX = 72, double dpiY = 72) => 9 | SKBitmap.Decode(bitmap.AsBmpStream(dpiX, dpiY)); 10 | } 11 | } -------------------------------------------------------------------------------- /src/PdfLibCore/BmpStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using PdfLibCore.Enums; 5 | 6 | namespace PdfLibCore 7 | { 8 | internal sealed class BmpStream : Stream 9 | { 10 | private const uint BmpHeaderSize = 14; 11 | private const uint DibHeaderSize = 108; // BITMAPV4HEADER 12 | private const uint PixelArrayOffset = BmpHeaderSize + DibHeaderSize; 13 | private const uint CompressionMethod = 3; // BI_BITFIELDS 14 | private const uint MaskR = 0x00_FF_00_00; 15 | private const uint MaskG = 0x00_00_FF_00; 16 | private const uint MaskB = 0x00_00_00_FF; 17 | private const uint MaskA = 0xFF_00_00_00; 18 | 19 | private readonly PdfiumBitmap _bitmap; 20 | private readonly byte[] _header; 21 | private readonly uint _length; 22 | private readonly uint _stride; 23 | private readonly uint _rowLength; 24 | private uint _pos; 25 | 26 | public override bool CanRead => true; 27 | 28 | public override bool CanSeek => true; 29 | 30 | public override bool CanWrite => false; 31 | 32 | public override long Length => _length; 33 | 34 | public override long Position 35 | { 36 | get => _pos; 37 | set => _pos = (uint) value; 38 | } 39 | 40 | public BmpStream(PdfiumBitmap bitmap, double dpiX, double dpiY) 41 | { 42 | if (bitmap.Format == BitmapFormats.Gray) 43 | { 44 | throw new NotSupportedException($"Bitmap format {bitmap.Format} is not yet supported."); 45 | } 46 | 47 | _bitmap = bitmap; 48 | _rowLength = (uint) bitmap.BytesPerPixel * (uint) bitmap.Width; 49 | _stride = ((uint) bitmap.BytesPerPixel * 8 * (uint) bitmap.Width + 31) / 32 * 4; 50 | _length = PixelArrayOffset + _stride * (uint) bitmap.Height; 51 | _header = GetHeader(_length, _bitmap, dpiX, dpiY); 52 | _pos = 0; 53 | } 54 | 55 | private static byte[] GetHeader(uint fileSize, PdfiumBitmap bitmap, double dpiX, double dpiY) 56 | { 57 | const double metersPerInch = 0.0254; 58 | var header = new byte[BmpHeaderSize + DibHeaderSize]; 59 | 60 | using var ms = new MemoryStream(header); 61 | using var writer = new BinaryWriter(ms); 62 | writer.Write((byte) 'B'); 63 | writer.Write((byte) 'M'); 64 | writer.Write(fileSize); 65 | writer.Write(0u); 66 | writer.Write(PixelArrayOffset); 67 | writer.Write(DibHeaderSize); 68 | writer.Write(bitmap.Width); 69 | writer.Write(-bitmap.Height); // top-down image 70 | writer.Write((ushort) 1); 71 | writer.Write((ushort) (bitmap.BytesPerPixel * 8)); 72 | writer.Write(CompressionMethod); 73 | writer.Write(0); 74 | writer.Write((int) Math.Round(dpiX / metersPerInch)); 75 | writer.Write((int) Math.Round(dpiY / metersPerInch)); 76 | writer.Write(0L); 77 | writer.Write(MaskR); 78 | writer.Write(MaskG); 79 | writer.Write(MaskB); 80 | if (bitmap.Format == BitmapFormats.RGBA) 81 | { 82 | writer.Write(MaskA); 83 | } 84 | return header; 85 | } 86 | 87 | public override void Flush() 88 | { 89 | } 90 | 91 | public override int Read(byte[] buffer, int offset, int count) 92 | { 93 | var bytesToRead = count; 94 | var returnValue = 0; 95 | if (_pos < PixelArrayOffset) 96 | { 97 | returnValue = Math.Min(count, (int) (PixelArrayOffset - _pos)); 98 | Buffer.BlockCopy(_header, (int) _pos, buffer, offset, returnValue); 99 | _pos += (uint) returnValue; 100 | offset += returnValue; 101 | bytesToRead -= returnValue; 102 | } 103 | 104 | if (bytesToRead <= 0) 105 | { 106 | return returnValue; 107 | } 108 | 109 | bytesToRead = Math.Min(bytesToRead, (int) (_length - _pos)); 110 | var idxBuffer = _pos - PixelArrayOffset; 111 | 112 | if (_stride == _bitmap.Stride) 113 | { 114 | Marshal.Copy(_bitmap.Scan0 + (int) idxBuffer, buffer, offset, bytesToRead); 115 | returnValue += bytesToRead; 116 | _pos += (uint) bytesToRead; 117 | return returnValue; 118 | } 119 | 120 | while (bytesToRead > 0) 121 | { 122 | var idxInStride = (int) (idxBuffer / _stride); 123 | var leftInRow = Math.Max(0, (int) _rowLength - idxInStride); 124 | var paddingBytes = (int) (_stride - _rowLength); 125 | var read = Math.Min(bytesToRead, leftInRow); 126 | if (read > 0) 127 | { 128 | Marshal.Copy(_bitmap.Scan0 + (int) idxBuffer, buffer, offset, read); 129 | } 130 | offset += read; 131 | idxBuffer += (uint) read; 132 | bytesToRead -= read; 133 | returnValue += read; 134 | read = Math.Min(bytesToRead, paddingBytes); 135 | for (var i = 0; i < read; i++) 136 | { 137 | buffer[offset + i] = 0; 138 | } 139 | offset += read; 140 | idxBuffer += (uint) read; 141 | bytesToRead -= read; 142 | returnValue += read; 143 | } 144 | _pos = PixelArrayOffset + idxBuffer; 145 | return returnValue; 146 | } 147 | 148 | public override long Seek(long offset, SeekOrigin origin) => Position = origin switch 149 | { 150 | SeekOrigin.Begin => offset, 151 | SeekOrigin.Current => Position + offset, 152 | SeekOrigin.End => Length + offset, 153 | _ => throw new ArgumentOutOfRangeException(nameof(origin), origin, null) 154 | }; 155 | 156 | public override void SetLength(long value) => 157 | throw new NotSupportedException(); 158 | 159 | public override void Write(byte[] buffer, int offset, int count) => 160 | throw new NotSupportedException(); 161 | } 162 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/ActionTypes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | /// 4 | /// PDF Action Types 5 | /// 6 | public enum ActionTypes : uint 7 | { 8 | /// 9 | /// Unsupported action type. 10 | /// 11 | Unsupported = 0, 12 | 13 | /// 14 | /// Go to a destination within current document. 15 | /// 16 | GoTo = 1, 17 | 18 | /// 19 | /// Go to a destination within another document. 20 | /// 21 | RemoteGoTo = 2, 22 | 23 | /// 24 | /// URI, including web pages and other Internet resources. 25 | /// 26 | Uri = 3, 27 | 28 | /// 29 | /// Launch an application or open a file. 30 | /// 31 | Launch = 4 32 | } 33 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/BitmapFormats.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum BitmapFormats 4 | { 5 | Gray = 1, 6 | RGB = 2, 7 | RGBx = 3, 8 | RGBA = 4 9 | } 10 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/DocumentPermissions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PdfLibCore.Enums 4 | { 5 | /// 6 | /// Flags specifying document permissions. 7 | /// 8 | /// 9 | /// 10 | /// PDF Reference: Table 22 11 | [Flags] 12 | public enum DocumentPermissions : uint 13 | { 14 | /// 15 | /// For of 2: Print the document. 16 | /// For of 3 or greater: Print the document 17 | /// (possibly not at the highest quality level, depending on whether is also set). 18 | /// 19 | Print = 1 << 2, 20 | 21 | /// 22 | /// Modify the contents of the document by operations other than those controlled by , 23 | /// and . 24 | /// 25 | Modify = 1 << 3, 26 | 27 | /// 28 | /// For of 2: Copy or otherwise extract text and graphics from the document, 29 | /// including extracting text and graphics (in support of accessibility to users with disabilities or for other purposes). 30 | /// For of 3 or greater: Copy or otherwise extract text and graphics from 31 | /// the document by operations other than that controlled by . 32 | /// 33 | ExtractTextAndGraphics = 1 << 4, 34 | 35 | /// 36 | /// Add or modify text annotations, fill in interactive form fields, and, if is also set, 37 | /// create or modify interactive form fields (including signature fields). 38 | /// 39 | ModfiyAnnotations = 1 << 5, 40 | 41 | /// 42 | /// For of 3 or greater: Fill in existing interactive form fields 43 | /// (including signature fields), even if is not set. 44 | /// 45 | FillInForms = 1 << 8, 46 | 47 | /// 48 | /// For of 3 or greater: Extract text and graphics 49 | /// (in support of accessibility to users with disabilities or for other purposes). 50 | /// 51 | ExtractTextAndGraphics2 = 1 << 9, 52 | 53 | /// 54 | /// For of 3 or greater: Assemble the document 55 | /// (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if is not set. 56 | /// 57 | AssembleDocument = 1 << 10, 58 | 59 | /// 60 | /// For of 3 or greater: Print the document to a representation 61 | /// from which a faithful digital copy of the PDF content could be generated. When is not set 62 | /// (and is set), printing is limited to a low-level representation of the appearance, possibly of degraded quality. 63 | /// 64 | PrintHighQuality = 1 << 11 65 | } 66 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/DuplexTypes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum DuplexTypes 4 | { 5 | DuplexUndefined = 0, 6 | Simplex, 7 | DuplexFlipShortEdge, 8 | DuplexFlipLongEdge 9 | } 10 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/FlattenFlags.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum FlattenFlags 4 | { 5 | /// 6 | /// Flatten for normal display. 7 | /// 8 | NormalDisplay = 0, 9 | 10 | /// 11 | /// Flatten for print. 12 | /// 13 | Print = 1 14 | } 15 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/FlattenResults.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum FlattenResults 4 | { 5 | /// 6 | /// Flatten operation failed. 7 | /// 8 | Fail = 0, 9 | 10 | /// 11 | /// Flatten operation succeed. 12 | /// 13 | Success = 1, 14 | 15 | /// 16 | /// Nothing to be flattened. 17 | /// 18 | NothingToDo = 2 19 | } 20 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/FontTypes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum FontTypes 4 | { 5 | Type1 = 1, 6 | TrueType = 2 7 | } 8 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/MetadataTags.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum MetadataTags 4 | { 5 | Title, 6 | Author, 7 | Subject, 8 | Keywords, 9 | Creator, 10 | Producer, 11 | CreationDate, 12 | ModDate 13 | } 14 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/ObjectTypes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | /// 4 | /// PDF object types 5 | /// 6 | public enum ObjectTypes 7 | { 8 | Unknown = 0, 9 | Boolean = 1, 10 | Number = 2, 11 | String = 3, 12 | Name = 4, 13 | Array = 5, 14 | Dictionary = 6, 15 | Stream = 7, 16 | Nullobj = 8, 17 | Reference = 9 18 | } 19 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/PageModes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum PageModes 4 | { 5 | /// 6 | /// Unknown page mode. 7 | /// 8 | Unknown = -1, 9 | 10 | /// 11 | /// Document outline, and thumbnails hidden. 12 | /// 13 | UseNone = 0, 14 | 15 | /// 16 | /// Document outline visible. 17 | /// 18 | UseOutlines = 1, 19 | 20 | /// 21 | /// Thumbnail images visible. 22 | /// 23 | UseThumbs = 2, 24 | 25 | /// 26 | /// Full-screen mode, no menu bar, window controls, or other decorations visible. 27 | /// 28 | Fullscreen = 3, 29 | 30 | /// 31 | /// Optional content group panel visible. 32 | /// 33 | UseOC = 4, 34 | 35 | /// 36 | /// Attachments panel visible. 37 | /// 38 | UseAttachments = 5 39 | } 40 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/PageObjTypes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum PageObjTypes 4 | { 5 | Unknown = 0, 6 | Text = 1, 7 | Path = 2, 8 | Image = 3, 9 | Shading = 4, 10 | Form = 5, 11 | } 12 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/PageOrientations.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum PageOrientations 4 | { 5 | Normal = 0, 6 | Rotated90CW = 1, 7 | Rotated180 = 2, 8 | Rotated90CCW = 3, 9 | } 10 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/PathFillModes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum PathFillModes 4 | { 5 | NoFill = 0, 6 | Alternate = 1, 7 | Winding = 2 8 | } 9 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/RenderingFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PdfLibCore.Enums 4 | { 5 | [Flags] 6 | public enum RenderingFlags 7 | { 8 | None = 0, 9 | 10 | /// 11 | /// Set if annotations are to be rendered. 12 | /// 13 | Annotations = 0x01, 14 | 15 | /// 16 | /// Set if using text rendering optimized for LCD display. 17 | /// 18 | LcdText = 0x02, 19 | 20 | /// 21 | /// Don't use the native text output available on some platforms 22 | /// 23 | NoNativeText = 0x04, 24 | 25 | /// 26 | /// Grayscale output. 27 | /// 28 | Grayscale = 0x08, 29 | 30 | /// 31 | /// Set if you want to get some debug info. 32 | /// 33 | DebugInfo = 0x80, 34 | 35 | /// 36 | /// Set if you don't want to catch exceptions. 37 | /// 38 | DontCatch = 0x100, 39 | 40 | /// 41 | /// Limit image cache size. 42 | /// 43 | LimitImageCache = 0x200, 44 | 45 | /// 46 | /// Always use halftone for image stretching. 47 | /// 48 | ForceHalftone = 0x400, 49 | 50 | /// 51 | /// Render for printing. 52 | /// 53 | Printing = 0x800, 54 | 55 | /// 56 | /// Set to disable anti-aliasing on text. 57 | /// 58 | NoSmoothText = 0x1000, 59 | 60 | /// 61 | /// Set to disable anti-aliasing on images. 62 | /// 63 | NoSmoothImage = 0x2000, 64 | 65 | /// 66 | /// Set to disable anti-aliasing on paths. 67 | /// 68 | NoSmoothPath = 0x4000, 69 | 70 | /// 71 | /// Set whether to render in a reverse Byte order, this flag is only used when rendering to a bitmap. 72 | /// 73 | ReverseByteOrder = 0x10, 74 | } 75 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/RenderingStatus.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum RenderingStatus : int 4 | { 5 | Reader = 0, 6 | 7 | ToBeContinued = 1, 8 | 9 | Done = 2, 10 | 11 | Failed = 3 12 | } 13 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/SaveFlags.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum SaveFlags 4 | { 5 | None = 0, 6 | Incremental = 1, 7 | NotIncremental = 2, 8 | RemoveSecurity = 3 9 | } 10 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/SearchFlags.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum SearchFlags : uint 4 | { 5 | /// 6 | /// If not set, it will not match case by default. 7 | /// 8 | MatchCase = 0x00000001, 9 | 10 | /// 11 | /// If not set, it will not match the whole word by default. 12 | /// 13 | MatchWholeWord = 0x00000002 14 | } 15 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Enums/ZoomModes.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Enums 2 | { 3 | public enum ZoomModes 4 | { 5 | Unknown = 0, 6 | XYZ = 1, 7 | Fit = 2, 8 | FitH = 3, 9 | FitV = 4, 10 | FitR = 5, 11 | FitB = 6, 12 | FitBH = 7, 13 | FitBV = 8 14 | } 15 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using PdfLibCore.Enums; 3 | using PdfLibCore.Types; 4 | 5 | namespace PdfLibCore 6 | { 7 | public sealed class PdfAction : NativeWrapper 8 | { 9 | public ActionTypes Type => Pdfium.FPDFAction_GetType(Handle); 10 | 11 | public PdfDestination Destination => new(Document, Pdfium.FPDFAction_GetDest(Document.Handle, Handle), null); 12 | 13 | public string FilePath => Pdfium.FPDFAction_GetFilePath(Handle); 14 | 15 | public Uri Uri => new(Pdfium.FPDFAction_GetURIPath(Document.Handle, Handle)); 16 | 17 | internal PdfAction(PdfDocument doc, FPDF_ACTION actionHandle) 18 | : base(doc, actionHandle) 19 | { 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfBookmark.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using PdfLibCore.Types; 3 | 4 | namespace PdfLibCore 5 | { 6 | public sealed class PdfBookmark : NativeWrapper 7 | { 8 | public IEnumerable Children 9 | { 10 | get 11 | { 12 | var handle = Pdfium.FPDFBookmark_GetFirstChild(Document.Handle, Handle); 13 | while (!handle.IsNull) 14 | { 15 | yield return new PdfBookmark(Document, handle); 16 | handle = Pdfium.FPDFBookmark_GetNextSibling(Document.Handle, handle); 17 | } 18 | } 19 | } 20 | 21 | public string Title => Pdfium.FPDFBookmark_GetTitle(Handle); 22 | 23 | public PdfDestination Destination => new(Document, Pdfium.FPDFBookmark_GetDest(Document.Handle, Handle), null); 24 | 25 | public PdfAction Action => new(Document, Pdfium.FPDFBookmark_GetAction(Handle)); 26 | 27 | internal PdfBookmark(PdfDocument doc, FPDF_BOOKMARK handle) 28 | : base(doc, handle) 29 | { 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfDestination.cs: -------------------------------------------------------------------------------- 1 | using PdfLibCore.Types; 2 | 3 | namespace PdfLibCore 4 | { 5 | public sealed class PdfDestination : NativeWrapper 6 | { 7 | public string Name { get; } 8 | 9 | public int PageIndex => Pdfium.FPDFDest_GetDestPageIndex(Document.Handle, Handle); 10 | 11 | public PdfPageLocation LocationInPage => 12 | Pdfium.FPDFDest_GetLocationInPage(Handle, out var hasX, out var hasY, out var hasZ, out var x, out var y, out var z) 13 | ? PdfPageLocation.Unknown 14 | : new PdfPageLocation(hasX ? x : float.NaN, hasY ? y : float.NaN, hasZ ? z : float.NaN); 15 | 16 | internal PdfDestination(PdfDocument doc, FPDF_DEST handle, string name) 17 | : base(doc, handle) 18 | { 19 | Name = name; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfDestinationCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace PdfLibCore 6 | { 7 | public sealed class PdfDestinationCollection : IEnumerable 8 | { 9 | private readonly PdfDocument _doc; 10 | 11 | public int Count => Pdfium.FPDF_CountNamedDests(_doc.Handle); 12 | 13 | internal PdfDestinationCollection(PdfDocument doc) => 14 | _doc = doc; 15 | 16 | public PdfDestination this[string name] 17 | { 18 | get 19 | { 20 | var handle = Pdfium.FPDF_GetNamedDestByName(_doc.Handle, name); 21 | return handle.IsNull ? null : new PdfDestination(_doc, handle, name); 22 | } 23 | } 24 | 25 | public PdfDestination this[int index] 26 | { 27 | get 28 | { 29 | if (index < 0 || index >= Count) 30 | { 31 | throw new ArgumentOutOfRangeException(nameof(index)); 32 | } 33 | var (handle, name) = Pdfium.FPDF_GetNamedDest(_doc.Handle, index); 34 | return handle.IsNull ? null : new PdfDestination(_doc, handle, name); 35 | } 36 | } 37 | 38 | IEnumerator IEnumerable.GetEnumerator() 39 | { 40 | var count = Count; 41 | for (var i = 0; i < count; i++) 42 | { 43 | yield return this[i]; 44 | } 45 | } 46 | 47 | IEnumerator IEnumerable.GetEnumerator() 48 | { 49 | var count = Count; 50 | for (var i = 0; i < count; i++) 51 | { 52 | yield return this[i]; 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using PdfLibCore.Enums; 5 | using PdfLibCore.Types; 6 | 7 | // ReSharper disable UnusedMember.Global 8 | // ReSharper disable UnusedAutoPropertyAccessor.Global 9 | // ReSharper disable MemberCanBePrivate.Global 10 | namespace PdfLibCore 11 | { 12 | public class PdfDocument : NativeWrapper 13 | { 14 | /// 15 | /// Gets the pages in the current . 16 | /// 17 | public PdfPageCollection Pages { get; } 18 | 19 | public PdfDestinationCollection Destinations { get; } 20 | 21 | /// 22 | /// Gets the PDF file version. File version: 14 for 1.4, 15 for 1.5, ... 23 | /// 24 | public int FileVersion => 25 | Pdfium.FPDF_GetFileVersion(Handle, out var fileVersion) ? fileVersion : 1; 26 | 27 | /// 28 | /// Gets the revision of the security handler. 29 | /// 30 | /// PDF Reference: Table 21 31 | public int SecurityHandlerRevision => Pdfium.FPDF_GetSecurityHandlerRevision(Handle); 32 | 33 | public DocumentPermissions Permissions => Pdfium.FPDF_GetDocPermissions(Handle); 34 | 35 | public bool PrintPrefersScaling => Pdfium.FPDF_VIEWERREF_GetPrintScaling(Handle); 36 | 37 | public int PrintCopyCount => Pdfium.FPDF_VIEWERREF_GetNumCopies(Handle); 38 | 39 | public DuplexTypes DuplexType => Pdfium.FPDF_VIEWERREF_GetDuplex(Handle); 40 | 41 | public PdfPageRange PageRange => new(this, Pdfium.FPDF_VIEWERREF_GetPrintPageRange(Handle)); 42 | 43 | public IEnumerable Bookmarks 44 | { 45 | get 46 | { 47 | var handle = Pdfium.FPDFBookmark_GetFirstChild(Handle, FPDF_BOOKMARK.Null); 48 | while (!handle.IsNull) 49 | { 50 | yield return new PdfBookmark(this, handle); 51 | handle = Pdfium.FPDFBookmark_GetNextSibling(this.Handle, handle); 52 | } 53 | } 54 | } 55 | 56 | public PageModes PageMode => Pdfium.FPDFDoc_GetPageMode(Handle); 57 | 58 | private PdfDocument(FPDF_DOCUMENT doc) 59 | : base(doc) 60 | { 61 | Pages = new PdfPageCollection(this); 62 | Destinations = new PdfDestinationCollection(this); 63 | } 64 | 65 | /// 66 | /// Creates a new . 67 | /// must be called in order to free unmanaged resources. 68 | /// 69 | public PdfDocument() 70 | : this(Pdfium.FPDF_CreateNewDocument()) 71 | { 72 | } 73 | 74 | /// 75 | /// Loads a from the file system. 76 | /// must be called in order to free unmanaged resources. 77 | /// 78 | /// Filepath of the PDF file to load. 79 | /// 80 | public PdfDocument(string fileName, string password = null) 81 | : this(Pdfium.FPDF_LoadDocument(fileName, password)) 82 | { 83 | } 84 | 85 | /// 86 | /// Loads a from memory. 87 | /// must be called in order to free unmanaged resources. 88 | /// 89 | /// Byte array containing the bytes of the PDF document to load. 90 | /// The index of the first byte to be copied from . 91 | /// The number of bytes to copy from or a negative value to copy all bytes. 92 | /// 93 | public PdfDocument(byte[] data, int index = 0, int count = -1, string password = null) 94 | : this(Pdfium.FPDF_LoadDocument(data, index, count, password)) 95 | { 96 | } 97 | 98 | /// 99 | /// Loads a from a . 100 | /// must be called in order to free unmanaged resources. 101 | /// 102 | /// 103 | /// 104 | public PdfDocument(Stream stream, string password = null) 105 | : this(Pdfium.FPDF_LoadDocument(GetBytes(stream), 0, -1, password)) 106 | { 107 | } 108 | 109 | /// 110 | /// Closes the and frees unmanaged resources. 111 | /// 112 | public void Close() => ((IDisposable)this).Dispose(); 113 | 114 | /// 115 | /// Saves the to a . 116 | /// 117 | /// 118 | /// 119 | /// 120 | /// The new PDF file version of the saved file. 121 | /// 14 for 1.4, 15 for 1.5, etc. Values smaller than 10 are ignored. 122 | /// 123 | public bool Save(Stream stream, SaveFlags flags = SaveFlags.None, int version = 0) => 124 | Pdfium.FPDF_SaveAsCopy(Handle, stream, flags, version); 125 | 126 | /// 127 | /// Saves the to the file system. 128 | /// 129 | /// 130 | /// 131 | /// 132 | /// The new PDF file version of the saved file. 133 | /// 14 for 1.4, 15 for 1.5, etc. Values smaller than 10 are ignored. 134 | /// 135 | public bool Save(string filename, SaveFlags flags = SaveFlags.None, int version = 0) 136 | { 137 | using var stream = new FileStream(filename, FileMode.Create); 138 | return Save(stream, flags, version); 139 | } 140 | 141 | public PdfBookmark FindBookmark(string title) 142 | { 143 | var handle = Pdfium.FPDFBookmark_Find(Handle, title); 144 | return handle.IsNull ? null : new PdfBookmark(this, handle); 145 | } 146 | 147 | public string GetMetaText(MetadataTags tag) => 148 | Pdfium.FPDF_GetMetaText(Handle, tag); 149 | 150 | public void CopyViewerPreferencesFrom(PdfDocument srcDoc) => 151 | Pdfium.FPDF_CopyViewerPreferences(Handle, srcDoc.Handle); 152 | 153 | protected override void Dispose(FPDF_DOCUMENT handle) 154 | { 155 | Pages.Dispose(); 156 | Pdfium.FPDF_CloseDocument(handle); 157 | } 158 | 159 | private static byte[] GetBytes(Stream stream) 160 | { 161 | stream.Seek(0, SeekOrigin.Begin); 162 | var ms = new MemoryStream(); 163 | stream.CopyTo(ms); 164 | return ms.ToArray(); 165 | } 166 | } 167 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfLibCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;netstandard2.1 5 | latest 6 | any 7 | true 8 | Jan Baarssen, Jan Kokenberg 9 | PdfLib.Core 10 | PdfLib CORE is a fast library for reading PDF documents for modern .NET core applications. 11 | true 12 | 1701;1702;1591;0649 13 | MIT 14 | LICENSE.txt 15 | https://github.com/jbaarssen/PdfLibCore 16 | https://github.com/jbaarssen/PdfLibCore 17 | PDF; Reader; netstandard; PDFium; JPEG; JPG 18 | icon.png 19 | $(ClientOfficialVersion) 20 | $(ClientPreviewVersion) 21 | nightly-$(CurrentDate) 22 | preview 23 | $(ClientVersion) 24 | $(ClientVersion)-$(VersionSuffix) 25 | $(ClientVersion) 26 | true 27 | false 28 | 29 | 30 | 31 | AnyCPU 32 | 33 | 34 | 35 | AnyCPU 36 | 37 | 38 | 39 | 40 | TextTemplatingFileGenerator 41 | FPDF_TypeDef.cs 42 | 43 | 44 | TextTemplatingFileGenerator 45 | Pdfium.g.cs 46 | 47 | 48 | LICENSE.txt 49 | LICENSE.txt 50 | 51 | 52 | icon.png 53 | icon.png 54 | 55 | 56 | Pdfium.tt 57 | 58 | 59 | Pdfium.tt 60 | 61 | 62 | 63 | 64 | 65 | True 66 | True 67 | Pdfium.tt 68 | 69 | 70 | True 71 | True 72 | FPDF_TypeDef.tt 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | all 82 | runtime; build; native; contentfiles; analyzers; buildtransitive 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/PdfLibCore/PdfPage.cs: -------------------------------------------------------------------------------- 1 | using PdfLibCore.Enums; 2 | using PdfLibCore.Types; 3 | 4 | // ReSharper disable MemberCanBePrivate.Global 5 | // ReSharper disable UnusedMember.Global 6 | namespace PdfLibCore 7 | { 8 | public sealed class PdfPage : NativeWrapper 9 | { 10 | /// 11 | /// Gets the page width (excluding non-displayable area) measured in points. 12 | /// One point is 1/72 inch(around 0.3528 mm). 13 | /// 14 | public double Width => Pdfium.FPDF_GetPageWidth(Handle); 15 | 16 | /// 17 | /// Gets the page height (excluding non-displayable area) measured in points. 18 | /// One point is 1/72 inch(around 0.3528 mm). 19 | /// 20 | public double Height => Pdfium.FPDF_GetPageHeight(Handle); 21 | 22 | /// 23 | /// Gets the page width and height (excluding non-displayable area) measured in points. 24 | /// One point is 1/72 inch(around 0.3528 mm). 25 | /// 26 | public (double Width, double Height) Size => 27 | Pdfium.FPDF_GetPageSizeByIndex(Document.Handle, Index, out var width, out var height) ? (width, height) : throw new PdfiumException(); 28 | 29 | /// 30 | /// Gets the page orientation. 31 | /// 32 | public PageOrientations Orientation 33 | { 34 | get => Pdfium.FPDFPage_GetRotation(Handle); 35 | set => Pdfium.FPDFPage_SetRotation(Handle, value); 36 | } 37 | 38 | /// 39 | /// Get the transparency of the page 40 | /// 41 | public bool HasTransparency => Pdfium.FPDFPage_HasTransparency(Handle); 42 | 43 | /// 44 | /// Gets the zero-based index of the page in the 45 | /// 46 | public int Index { get; internal set; } 47 | 48 | public string Label => Pdfium.FPDF_GetPageLabel(Document.Handle, Index); 49 | 50 | private PdfPage(PdfDocument doc, FPDF_PAGE page, int index) 51 | : base(doc, page) 52 | { 53 | Index = index; 54 | } 55 | 56 | internal static PdfPage Load(PdfDocument doc, int index) => new(doc, Pdfium.FPDF_LoadPage(doc.Handle, index), index); 57 | 58 | internal static PdfPage New(PdfDocument doc, int index, double width, double height) => new(doc, Pdfium.FPDFPage_New(doc.Handle, index, width, height), index); 59 | 60 | /// 61 | /// Renders the page to a 62 | /// 63 | /// The bitmap to which the page is to be rendered. 64 | /// The orientation at which the page is to be rendered. 65 | /// The flags specifying how the page is to be rendered. 66 | public void Render(PdfiumBitmap renderTarget, PageOrientations orientation = PageOrientations.Normal, RenderingFlags flags = RenderingFlags.None) => 67 | Render(renderTarget, (0, 0, renderTarget.Width, renderTarget.Height), orientation, flags); 68 | 69 | /// 70 | /// Renders the page to a 71 | /// 72 | /// The bitmap to which the page is to be rendered. 73 | /// The destination rectangle in . 74 | /// The orientation at which the page is to be rendered. 75 | /// The flags specifying how the page is to be rendered. 76 | public void Render(PdfiumBitmap renderTarget, (int left, int top, int width, int height) rectDest, PageOrientations orientation = PageOrientations.Normal, RenderingFlags flags = RenderingFlags.None) => 77 | Pdfium.FPDF_RenderPageBitmap(renderTarget.Handle, Handle, rectDest.left, rectDest.top, rectDest.width, rectDest.height, orientation, flags); 78 | 79 | public (double X, double Y) DeviceToPage((int left, int top, int width, int height) displayArea, int deviceX, int deviceY, PageOrientations orientation = PageOrientations.Normal) 80 | { 81 | var (left, top, width, height) = displayArea; 82 | Pdfium.FPDF_DeviceToPage(Handle, left, top, width, height, orientation, deviceX, deviceY, out var x, out var y); 83 | return (x, y); 84 | } 85 | 86 | public (int X, int Y) PageToDevice((int left, int top, int width, int height) displayArea, double pageX, double pageY, PageOrientations orientation = PageOrientations.Normal) 87 | { 88 | var (left, top, width, height) = displayArea; 89 | Pdfium.FPDF_PageToDevice(Handle, left, top, width, height, orientation, pageX, pageY, out var x, out var y); 90 | return (x, y); 91 | } 92 | 93 | public FlattenResults Flatten(FlattenFlags flags) => 94 | Pdfium.FPDFPage_Flatten(Handle, flags); 95 | 96 | protected override void Dispose(FPDF_PAGE handle) => 97 | Pdfium.FPDF_ClosePage(handle); 98 | } 99 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfPageCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | // ReSharper disable UnusedMember.Global 6 | // ReSharper disable MemberCanBePrivate.Global 7 | namespace PdfLibCore 8 | { 9 | public sealed class PdfPageCollection : List, IDisposable 10 | { 11 | private readonly PdfDocument _doc; 12 | private readonly object _lock = new(); 13 | 14 | public new int Count => Pdfium.FPDF_GetPageCount(_doc.Handle); 15 | 16 | internal PdfPageCollection(PdfDocument doc) 17 | { 18 | lock (_lock) 19 | { 20 | _doc = doc; 21 | for (var index = 0; index < Count; index++) 22 | { 23 | Add(PdfPage.Load(doc, index)); 24 | } 25 | } 26 | } 27 | 28 | /// 29 | /// Gets the at the zero-based in the . 30 | /// 31 | public new PdfPage this[int index] 32 | { 33 | get 34 | { 35 | if (index >= Count) 36 | { 37 | throw new ArgumentOutOfRangeException(nameof(index)); 38 | } 39 | 40 | if (base[index] == null || base[index].IsDisposed) 41 | { 42 | base[index] = PdfPage.Load(_doc, index); 43 | } 44 | 45 | return base[index]; 46 | } 47 | } 48 | 49 | public void Dispose() 50 | { 51 | foreach (IDisposable page in this) 52 | { 53 | page?.Dispose(); 54 | } 55 | Clear(); 56 | } 57 | 58 | /// 59 | /// Imports pages of into the current . 60 | /// 61 | /// 62 | public bool Add(PdfDocument sourceDocument, params int[] srcPageIndices) => 63 | Insert(Count, sourceDocument, srcPageIndices); 64 | 65 | /// 66 | /// Adds a new page to the end of the document. 67 | /// 68 | public PdfPage Add(double width, double height) => 69 | Insert(Count, width, height); 70 | 71 | /// 72 | /// Imports pages of into the current . 73 | /// 74 | /// 75 | public bool Insert(int index, PdfDocument sourceDocument, params int[] srcPageIndices) 76 | { 77 | if (index <= Count) 78 | { 79 | var result = Pdfium.FPDF_ImportPages(_doc.Handle, sourceDocument.Handle, index, srcPageIndices); 80 | if (!result) 81 | { 82 | return false; 83 | } 84 | InsertRange(index, Enumerable.Repeat(null, srcPageIndices.Length)); 85 | for (var i = index; i < Count; i++) 86 | { 87 | if (this[i] != null) 88 | { 89 | this[i].Index = i; 90 | } 91 | } 92 | } 93 | else 94 | { 95 | throw new ArgumentOutOfRangeException(nameof(index)); 96 | } 97 | 98 | return true; 99 | } 100 | 101 | /// 102 | /// Inserts a new page at . 103 | /// 104 | public PdfPage Insert(int index, double width, double height) 105 | { 106 | PdfPage page; 107 | if (index <= Count) 108 | { 109 | page = PdfPage.New(_doc, index, width, height); 110 | Insert(index, page); 111 | for (var i = index; i < Count; i++) 112 | { 113 | if (this[i] != null) 114 | { 115 | this[i].Index = i; 116 | } 117 | } 118 | } 119 | else 120 | { 121 | throw new ArgumentOutOfRangeException(nameof(index)); 122 | } 123 | 124 | return page; 125 | } 126 | 127 | /// 128 | /// Removes the from the document. 129 | /// 130 | public new void Remove(PdfPage page) => RemoveAt(page.Index); 131 | 132 | /// 133 | /// Removes the page at . 134 | /// 135 | public new void RemoveAt(int index) 136 | { 137 | if (index < Count) 138 | { 139 | ((IDisposable)this[index])?.Dispose(); 140 | for (var i = index; i < Count; i++) 141 | { 142 | if (this[i] != null) 143 | { 144 | this[i].Index = i; 145 | } 146 | } 147 | } 148 | Pdfium.FPDFPage_Delete(_doc.Handle, index); 149 | base.RemoveAt(index); 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfPageLocation.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore 2 | { 3 | public readonly struct PdfPageLocation 4 | { 5 | public float X { get; } 6 | public float Y { get; } 7 | public float Zoom { get; } 8 | 9 | public static PdfPageLocation Unknown => new(float.NaN, float.NaN, float.NaN); 10 | 11 | public PdfPageLocation(float x, float y, float zoom) 12 | { 13 | X = x; 14 | Y = y; 15 | Zoom = zoom; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfPageRange.cs: -------------------------------------------------------------------------------- 1 | using PdfLibCore.Types; 2 | 3 | namespace PdfLibCore 4 | { 5 | public class PdfPageRange : NativeWrapper 6 | { 7 | public uint PrintPageRangeCount => Pdfium.FPDF_VIEWERREF_GetPrintPageRangeCount(Handle); 8 | 9 | public PdfPageRange(PdfDocument document, FPDF_PAGERANGE handle) 10 | : base(document, handle) 11 | { 12 | } 13 | 14 | public int PrintRangeElement(uint index) => Pdfium.FPDF_VIEWERREF_GetPrintPageRangeElement(Handle, index); 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Pdfium.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using PdfLibCore.Enums; 9 | using PdfLibCore.Types; 10 | 11 | // ReSharper disable UnusedMember.Local 12 | namespace PdfLibCore 13 | { 14 | /// 15 | /// Static class containing the native (imported) PDFium functions. 16 | /// In case of missing documentation, refer to the PDFium header files. 17 | /// 18 | // ReSharper disable MemberCanBePrivate.Global 19 | // ReSharper disable UnusedAutoPropertyAccessor.Global 20 | public static partial class Pdfium 21 | { 22 | private delegate int GetStringHandler(ref byte buffer, int length); 23 | 24 | /// 25 | /// Gets a value indicating whether the PDFium library is available. 26 | /// false is returned if the native libraries could not be 27 | /// loaded for some reason. 28 | /// 29 | public static bool IsAvailable { get; } 30 | 31 | static Pdfium() => 32 | IsAvailable = Initialize(); 33 | 34 | private static bool Initialize() 35 | { 36 | try 37 | { 38 | FPDF_InitLibrary(); 39 | } 40 | catch 41 | { 42 | return false; 43 | } 44 | return true; 45 | } 46 | 47 | #region https://pdfium.googlesource.com/pdfium/+/master/public/fpdfview.h 48 | /// 49 | /// Loads a PDF document from memory. 50 | /// 51 | /// The data to load the document from. 52 | /// The index of the first byte to be copied from . 53 | /// The number of bytes to copy from or a negative value to copy all bytes. 54 | /// Pdf password 55 | public static FPDF_DOCUMENT FPDF_LoadDocument(byte[] data, int index = 0, int count = -1, string password = null) 56 | { 57 | if (count < 0) 58 | { 59 | count = data.Length - index; 60 | } 61 | return FPDF_LoadMemDocument(ref data[index], count, password); 62 | } 63 | 64 | /// 65 | /// Loads a PDF document from a stream. 66 | /// 67 | /// 68 | /// Pdf password 69 | public static FPDF_DOCUMENT FPDF_LoadDocument(Stream fileRead, string password = null) => 70 | FPDF_LoadCustomDocument(FPDF_FILEREAD.FromStream(fileRead), password); 71 | 72 | public static string FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document, string key) 73 | { 74 | byte b = 0; 75 | var length = FPDF_VIEWERREF_GetName(document, key, ref b, 0); 76 | if (length == 0) 77 | { 78 | return null; 79 | } 80 | var buffer = new byte[length]; 81 | FPDF_VIEWERREF_GetName(document, key, ref buffer[0], length); 82 | return Encoding.UTF8.GetString(buffer); 83 | } 84 | 85 | /// 86 | /// Get the named destination by index. 87 | /// 88 | /// Handle to a document. 89 | /// The index of a named destination. 90 | /// 91 | /// The destination handle and name for a given index, or (, null) 92 | /// if there is no named destination corresponding to . 93 | /// 94 | /// 95 | /// 96 | public static (FPDF_DEST Destination, string Name) FPDF_GetNamedDest(FPDF_DOCUMENT document, int index) 97 | { 98 | FPDF_GetNamedDest(document, index, IntPtr.Zero, out var length); 99 | if (length < 1) 100 | { 101 | return (FPDF_DEST.Null, null); 102 | } 103 | var buffer = new byte[length]; 104 | return (FPDF_GetNamedDest(document, index, ref buffer[0], ref length), Encoding.Unicode.GetString(buffer, 0, length - 2)); 105 | } 106 | 107 | #endregion 108 | 109 | #region https://pdfium.googlesource.com/pdfium/+/master/public/fpdf_doc.h 110 | 111 | /// 112 | /// Get the title of . 113 | /// 114 | /// Handle to the bookmark. 115 | /// The title of the bookmark. 116 | public static string FPDFBookmark_GetTitle(FPDF_BOOKMARK bookmark) => 117 | GetUtf16String((ref byte buffer, int length) => (int)FPDFBookmark_GetTitle(bookmark, ref buffer, (uint)length), sizeof(byte), true); 118 | 119 | /// 120 | /// Gets the file path of a of type or . 121 | /// 122 | /// Handle to the action. Must be of type or . 123 | /// The file path of . 124 | /// 125 | public static string FPDFAction_GetFilePath(FPDF_ACTION action) => 126 | GetUtf16String((ref byte buffer, int length) => (int)FPDFAction_GetFilePath(action, ref buffer, (uint)length), sizeof(byte), true); 127 | 128 | /// 129 | /// Gets URI path of a of type . 130 | /// 131 | /// Handle to the document. 132 | /// Handle to the action. Must be of type . 133 | /// The URI path of . 134 | /// 135 | public static string FPDFAction_GetURIPath(FPDF_DOCUMENT document, FPDF_ACTION action) => 136 | GetAsciiString((ref byte buffer, int length) => (int)FPDFAction_GetURIPath(document, action, ref buffer, (uint)length)); 137 | 138 | /// 139 | /// Enumerates all the link annotations in . 140 | /// 141 | /// Handle to the page. 142 | /// All the link annotations in . 143 | public static IEnumerable FPDFLink_Enumerate(FPDF_PAGE page) 144 | { 145 | var pos = 0; 146 | while (FPDFLink_Enumerate(page, ref pos, out var link)) 147 | { 148 | yield return link; 149 | } 150 | } 151 | 152 | /// 153 | /// Get meta-data content from . 154 | /// 155 | /// Handle to the document. 156 | /// 157 | /// The tag to retrieve. The tag can be one of: 158 | /// Title, Author, Subject, Keywords, Creator, Producer, 159 | /// CreationDate, or ModDate. 160 | /// 161 | /// The meta-data. 162 | /// 163 | /// For detailed explanations of these tags and their respective 164 | /// values, please refer to PDF Reference 1.6, section 10.2.1, 165 | /// 'Document Information Dictionary'. 166 | /// 167 | /// PDF Reference 168 | /// 169 | public static string FPDF_GetMetaText(FPDF_DOCUMENT document, string tag) => 170 | GetUtf16String((ref byte buffer, int length) => (int)FPDF_GetMetaText(document, tag, ref buffer, (uint)length), sizeof(byte), true); 171 | 172 | /// 173 | /// Get meta-data content from . 174 | /// 175 | /// Handle to the document. 176 | /// The tag to retrieve. 177 | /// The meta-data. 178 | /// 179 | /// For detailed explanations of these tags and their respective 180 | /// values, please refer to PDF Reference 1.6, section 10.2.1, 181 | /// 'Document Information Dictionary'. 182 | /// 183 | /// PDF Reference 184 | /// 185 | public static string FPDF_GetMetaText(FPDF_DOCUMENT document, MetadataTags tag) => 186 | FPDF_GetMetaText(document, tag.ToString()); 187 | 188 | /// 189 | /// Get the page label for from . 190 | /// 191 | /// Handle to the document. 192 | /// The zero-based index of the page. 193 | /// The page label. 194 | /// 195 | public static string FPDF_GetPageLabel(FPDF_DOCUMENT document, int pageIndex) => 196 | GetUtf16String((ref byte buffer, int length) => (int)FPDF_GetPageLabel(document, pageIndex, ref buffer, (uint)length), sizeof(byte), true); 197 | 198 | #endregion 199 | 200 | #region https://pdfium.googlesource.com/pdfium/+/master/public/fpdf_edit.h 201 | 202 | /// 203 | /// Insert into . 204 | /// 205 | /// Handle to a page. 206 | /// Handle to a page object. The will be automatically freed. 207 | public static void FPDFPage_InsertObject(FPDF_PAGE page, ref FPDF_PAGEOBJECT pageObj) 208 | { 209 | FPDFPage_InsertObject(page, pageObj); 210 | pageObj = FPDF_PAGEOBJECT.Null; 211 | } 212 | 213 | /// 214 | /// Load an image from a JPEG image file and then set it into . 215 | /// 216 | /// All loaded pages, may be null. 217 | /// Handle to an image object. 218 | /// Stream which provides access to an JPEG image. 219 | /// The number of bytes to read from or 0 to read to the end. 220 | /// 221 | /// If true, this function loads the JPEG image inline, so the image 222 | /// content is copied to the file. This allows 223 | /// to be closed after this function returns. 224 | /// 225 | /// true on success. 226 | /// 227 | /// The image object might already have an associated image, which is shared and 228 | /// cached by the loaded pages. In that case, we need to clear the cached image 229 | /// for all the loaded pages. Pass to this API 230 | /// to clear the image cache. If the image is not previously shared, null is a 231 | /// valid value. 232 | /// 233 | public static bool FPDFImageObj_LoadJpegFile(FPDF_PAGE[] loadedPages, FPDF_PAGEOBJECT imageObject, Stream stream, int count = 0, bool inline = true) => inline 234 | ? FPDFImageObj_LoadJpegFileInline(ref loadedPages[0], loadedPages.Length, imageObject, FPDF_FILEREAD.FromStream(stream)) 235 | : FPDFImageObj_LoadJpegFile(ref loadedPages[0], loadedPages.Length, imageObject, FPDF_FILEREAD.FromStream(stream)); 236 | 237 | /// 238 | /// Set to . 239 | /// 240 | /// All loaded pages, may be null. 241 | /// Handle to an image object. 242 | /// Handle of the bitmap. 243 | /// true on success. 244 | public static bool FPDFImageObj_SetBitmap(FPDF_PAGE[] loadedPages, FPDF_PAGEOBJECT imageObject, FPDF_BITMAP bitmap) => 245 | FPDFImageObj_SetBitmap(ref loadedPages[0], loadedPages.Length, imageObject, bitmap); 246 | 247 | /// 248 | /// Returns a font object loaded from a stream of data. The font is loaded 249 | /// into the document. The caller does not need to free the returned object. 250 | /// 251 | /// Handle to the document. 252 | /// The font type. 253 | /// A value specifying if the font is a CID font or not. 254 | /// The data, which will be copied by the font object. 255 | /// The index of the first byte to be copied from . 256 | /// The number of bytes to copy from or a negative value to copy all bytes. 257 | /// Returns NULL on failure. 258 | public static FPDF_FONT FPDFText_LoadFont(FPDF_DOCUMENT document, FontTypes fontType, bool cid, byte[] data, int index = -1, int count = 0) 259 | { 260 | if (count < 0) 261 | { 262 | count = data.Length - index; 263 | } 264 | return FPDFText_LoadFont(document, ref data[index], (uint)count, fontType, cid); 265 | } 266 | 267 | #endregion 268 | 269 | #region https://pdfium.googlesource.com/pdfium/+/master/public/fpdf_ppo.h 270 | 271 | /// 272 | /// Imports pages from to 273 | /// 274 | /// 275 | /// 276 | /// Zero-based index of where the imported pages should be inserted in the destination document. 277 | /// Zero-based indices of the pages to import in the source document 278 | public static bool FPDF_ImportPages(FPDF_DOCUMENT destDoc, FPDF_DOCUMENT srcDoc, int index, params int[] srcPageIndices) 279 | { 280 | string pageRange = null; 281 | if (srcPageIndices != null && srcPageIndices.Length > 0) 282 | { 283 | pageRange = string.Join(",", srcPageIndices.Select(p => (p + 1).ToString(CultureInfo.InvariantCulture))); 284 | } 285 | return FPDF_ImportPages(destDoc, srcDoc, pageRange, index); 286 | } 287 | 288 | #endregion 289 | 290 | #region https://pdfium.googlesource.com/pdfium/+/master/public/fpdf_save.h 291 | 292 | /// 293 | /// Saves a PDF document to a stream. 294 | /// 295 | /// 296 | /// 297 | /// 298 | /// 299 | /// The new PDF file version of the saved file. 300 | /// 14 for 1.4, 15 for 1.5, etc. Values smaller than 10 are ignored. 301 | /// 302 | /// 303 | /// 304 | public static bool FPDF_SaveAsCopy(FPDF_DOCUMENT document, Stream stream, SaveFlags flags, int version = 0) 305 | { 306 | byte[] buffer = null; 307 | var fileWrite = new FPDF_FILEWRITE((ignore, data, size) => 308 | { 309 | if (buffer == null || buffer.Length < size) 310 | { 311 | buffer = new byte[size]; 312 | } 313 | Marshal.Copy(data, buffer, 0, size); 314 | stream.Write(buffer, 0, size); 315 | return true; 316 | }); 317 | 318 | return version >= 10 319 | ? FPDF_SaveWithVersion(document, fileWrite, flags, version) 320 | : FPDF_SaveAsCopy(document, fileWrite, flags); 321 | } 322 | 323 | #endregion 324 | 325 | #region https://pdfium.googlesource.com/pdfium/+/master/public/fpdf_structtree.h 326 | 327 | /// 328 | /// Get the alternative text for a given element. 329 | /// 330 | /// Handle to the struct element. 331 | /// The alternative text for . 332 | public static string FPDF_StructElement_GetAltText(FPDF_STRUCTELEMENT structElement) => 333 | GetUtf16String((ref byte buffer, int length) => (int)FPDF_StructElement_GetAltText(structElement, ref buffer, (uint)length), sizeof(byte), true); 334 | 335 | #endregion 336 | 337 | #region https://pdfium.googlesource.com/pdfium/+/master/public/fpdf_text.h 338 | 339 | public static string FPDFText_GetText(FPDF_TEXTPAGE textPage, int startIndex, int count) 340 | { 341 | var buffer = new byte[2 * (count + 1)]; 342 | return Encoding.Unicode.GetString(buffer, 0, (FPDFText_GetText(textPage, startIndex, count, ref buffer[0]) - 1) * 2); 343 | } 344 | 345 | public static string FPDFText_GetBoundedText(FPDF_TEXTPAGE textPage, double left, double top, double right, double bottom) => 346 | GetUtf16String((ref byte buffer, int length) => FPDFText_GetBoundedText(textPage, left, top, right, bottom, ref buffer, length), sizeof(ushort), false); 347 | 348 | public static string FPDFLink_GetURL(FPDF_PAGELINK linkPage, int linkIndex) => 349 | GetUtf16String((ref byte buffer, int length) => FPDFLink_GetURL(linkPage, linkIndex, ref buffer, length), sizeof(ushort), true); 350 | 351 | #endregion 352 | 353 | private static string GetUtf16String(GetStringHandler handler, int lengthUnit, bool lengthIncludesTerminator) 354 | { 355 | var buffer = GetBuffer(handler, out var length); 356 | length *= lengthUnit; 357 | if (lengthIncludesTerminator) 358 | { 359 | length -= 2; 360 | } 361 | return Encoding.Unicode.GetString(buffer, 0, length > 0 ? length : 0); 362 | } 363 | 364 | private static string GetAsciiString(GetStringHandler handler) 365 | { 366 | var buffer = GetBuffer(handler, out var length); 367 | return Encoding.ASCII.GetString(buffer, 0, length - 1); 368 | } 369 | 370 | private static string GetUtf8String(GetStringHandler handler) 371 | { 372 | var buffer = GetBuffer(handler, out var length); 373 | return Encoding.UTF8.GetString(buffer, 0, length - 1); 374 | } 375 | 376 | private static byte[] GetBuffer(GetStringHandler handler, out int length) 377 | { 378 | byte b = 0; 379 | length = handler(ref b, 0); 380 | if (length == 0) 381 | { 382 | return Array.Empty(); 383 | } 384 | var buffer = new byte[length]; 385 | handler(ref buffer[0], length); 386 | return buffer; 387 | } 388 | } 389 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Pdfium.tt: -------------------------------------------------------------------------------- 1 | <# /* 2 | This file is part of PdfCoreLib, a wrapper around the PDFium library for the .NET. 3 | Inspired by the awesome work of PDFiumSharp by Tobias Meyer. 4 | 5 | Copyright (C) 2021 Jan Baarsssen 6 | License: Microsoft Reciprocal License (MS-RL) 7 | */ #> 8 | <#@ template debug="true" hostspecific="false" language="C#" #> 9 | <#@ include file="common.ttinclude" #> 10 | <#@ assembly name="System.Core" #> 11 | <#@ import namespace="System.Linq" #> 12 | <#@ output extension=".g.cs" #> 13 | <#@ import namespace="System.Collections.Generic" #> 14 | using System; 15 | using System.Runtime.InteropServices; 16 | using System.Runtime.ExceptionServices; 17 | using System.Security; 18 | using PdfLibCore.Types; 19 | using PdfLibCore.Enums; 20 | 21 | /* 22 | This file is part of PdfCoreLib, a wrapper around the PDFium library for the .NET. 23 | Inspired by the awesome work of PDFiumSharp by Tobias Meyer. 24 | 25 | Copyright (C) 2021 Jan Baarsssen 26 | License: Microsoft Reciprocal License (MS-RL) 27 | */ 28 | 29 | // AUTOGENERATED FILE - <#=DateTime.Now.ToString("dd-MM-yyyy (HH:MM:ss)")#> 30 | // DO NOT MODIFY 31 | 32 | namespace PdfLibCore 33 | { 34 | public static partial class Pdfium 35 | { 36 | private static readonly object _lock = new object(); 37 | 38 | <# // ------------------------------------------------------ Code Start ------------------------------------------------------ 39 | foreach (var import in _imports.Where(i => !string.IsNullOrEmpty(i.Method))) 40 | { // --------------------------------------------------------- Code End ----------------------------------------------------- #> 41 | #region <#=import.Name#> 42 | 43 | /// <#=import.Documentation.Aggregate(string.Empty, (current, doc) => current + Environment.NewLine + "\t\t/// " + doc)#> 44 | [HandleProcessCorruptedStateExceptions] 45 | <#=import.AccessModifier#> static <#=import.ReturnType#> <#=import.Name#><#=import.ArgumentList#> 46 | { 47 | lock(_lock) 48 | { 49 | try 50 | { 51 | <#=(import.ReturnType == "void" ? string.Empty : "return ")#>PlatformInvoke.<#=import.Name#>(<#=import.Arguments#>); 52 | } 53 | catch(AccessViolationException innerException) 54 | { 55 | throw new PdfiumException(innerException); 56 | } 57 | } 58 | } 59 | 60 | #endregion <#=import.Name#> 61 | 62 | <# // ------------------------------------------------------ Code Start ------------------------------------------------------ 63 | } // --------------------------------------------------------- Code End ----------------------------------------------------- #> 64 | 65 | #region PlatformInvoke 66 | 67 | private static class PlatformInvoke 68 | { 69 | <#foreach (var import in _imports.Where(i => !string.IsNullOrEmpty(i.Method))) 70 | {#> 71 | [SuppressUnmanagedCodeSecurity, DllImport("<#=DllName#>", EntryPoint = "<#=import.Name#>", SetLastError = true)]<#=import.Attributes.Aggregate(string.Empty, (current, attr) => current + Environment.NewLine + "\t\t\t" + attr)#> 72 | internal static extern <#=import.ReturnType#> <#=import.Name#><#=import.ArgumentList#>; 73 | 74 | <#}#> 75 | } 76 | 77 | #endregion 78 | } 79 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Pdfium.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Initialize the PDFium library 6 | 7 | Convenience function to call for 8 | backwards comatibility purposes. 9 | 10 | 11 | 12 | 13 | Initialize the PDFium library 14 | 15 | You have to call this function before you can call any PDF 16 | processing functions. 17 | 18 | 19 | 20 | 21 | Release all resources allocated by the PDFium library. 22 | 23 | You can call this function to release all memory blocks allocated by 24 | the library. 25 | After this function is called, you should not call any PDF 26 | processing functions. 27 | 28 | 29 | 30 | 31 | Open and load a PDF document. 32 | Path to the PDF file (including extension). 33 | 34 | A string used as the password for the PDF file. 35 | If no password is needed, or null can be used. 36 | 37 | A handle to the loaded document, or NULL on failure. 38 | 39 | Loaded document can be closed by . 40 | If this function fails, you can use to retrieve 41 | the reason why it failed. 42 | 43 | 44 | 45 | 46 | Open and load a PDF document from memory. 47 | Pointer to a buffer containing the PDF document. 48 | Number of bytes in the PDF document. 49 | 50 | A string used as the password for the PDF file. 51 | If no password is needed, or null can be used. 52 | 53 | A handle to the loaded document, or NULL on failure. 54 | 55 | 56 | The memory buffer must remain valid when the document is open. 57 | The loaded document can be closed by . 58 | If this function fails, you can use to retrieve 59 | the reason why it failed. 60 | 61 | 62 | If PDFium is built with the XFA module, the application should call 63 | FPDF_LoadXFA function after the PDF document loaded to support XFA 64 | fields defined in the fpdfformfill.h file. 65 | 66 | 67 | 68 | 69 | 70 | Load PDF document from a custom access descriptor. 71 | A structure for accessing the file. 72 | Optional password for decrypting the PDF file. 73 | A handle to the loaded document, or NULL on failure. 74 | 75 | 76 | The application must keep the file resources valid until the PDF 77 | document is closed. 78 | The loaded document can be closed with FPDF_CloseDocument. 79 | 80 | 81 | If PDFium is built with the XFA module, the application should call 82 | FPDF_LoadXFA function after the PDF document loaded to support XFA 83 | fields defined in the fpdfformfill.h file. 84 | 85 | 86 | 87 | 88 | 89 | Get the file version of the given PDF document. 90 | Handle to a document. 91 | The PDF file version. File version: 14 for 1.4, 15 for 1.5, ... 92 | True if succeeds, false otherwise. 93 | 94 | If the document was created by , 95 | then this function will always fail. 96 | 97 | 98 | 99 | 100 | Get last error code when a function fails. 101 | 102 | If the previous SDK call succeeded, the return value of this 103 | function is not defined. 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | Get total number of pages in the document. 113 | Handle to document. 114 | Total number of pages in the document. 115 | 116 | 117 | 118 | Load a page inside the document. 119 | Handle to document. 120 | Zero-based index of the page. 121 | A handle to the loaded page, or NULL if page load fails. 122 | The loaded page can be closed using FPDF_ClosePage. 123 | 124 | 125 | 126 | Get page width. 127 | Handle to the page. 128 | 129 | Page width (excluding non-displayable area) measured in points. 130 | One point is 1/72 inch (around 0.3528 mm). 131 | 132 | 133 | 134 | 135 | Get page height. 136 | Handle to the page. 137 | 138 | Page height (excluding non-displayable area) measured in points. 139 | One point is 1/72 inch (around 0.3528 mm) 140 | 141 | 142 | 143 | 144 | Get the size of the page at the given index. 145 | Handle to document. 146 | Zero-based index of the page. 147 | Pointer to a double to receive the page width (in points). 148 | Pointer to a double to receive the page height (in points). 149 | 150 | 151 | 152 | Render contents of a page to a device independent bitmap. 153 | 154 | Handle to the device independent bitmap (as the 155 | output buffer). The bitmap handle can be created 156 | by . 157 | 158 | Handle to the page. 159 | Left pixel position of the display area in bitmap coordinates. 160 | Top pixel position of the display area in bitmap coordinates. 161 | Horizontal size (in pixels) for displaying the page. 162 | Vertical size (in pixels) for displaying the page. 163 | Page orientation. 164 | 165 | for normal display, or combination of the Page 166 | Rendering flags defined above. With the 167 | flag, it renders all annotations that do not require 168 | user-interaction, which are all annotations except 169 | widget and popup annotations. 170 | 171 | 172 | 173 | 174 | Render contents of a page to a device independent bitmap. 175 | 176 | Handle to the device independent bitmap (as the 177 | output buffer). The bitmap handle can be created 178 | by . 179 | 180 | Handle to the page. 181 | The transform matrix. 182 | The rect to clip to. 183 | 184 | for normal display, or combination of the Page 185 | Rendering flags defined above. With the 186 | flag, it renders all annotations that do not require 187 | user-interaction, which are all annotations except 188 | widget and popup annotations. 189 | 190 | 191 | 192 | 193 | Close a loaded PDF page. 194 | Handle to the loaded page. 195 | 196 | 197 | 198 | Close a loaded PDF document. 199 | Handle to the loaded document. 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | Create a device independent bitmap (FXDIB). 208 | The number of pixels in width for the bitmap. Must be greater than 0. 209 | The number of pixels in height for the bitmap. Must be greater than 0. 210 | A value indicating whether the alpha channel is used. 211 | The bitmap handle, or if parameter error or out of memory. 212 | 213 | The bitmap always uses 4 bytes per pixel. The first byte is always 214 | double word aligned. 215 | 216 | The byte order is BGRx (the last byte unused if no alpha channel) or BGRA. 217 | 218 | The pixels in a horizontal line are stored side by side, with the 219 | left most pixel stored first (with lower memory address). 220 | Each line uses width * 4 bytes. 221 | 222 | Lines are stored one after another, with the top most line stored 223 | first. There is no gap between adjacent lines. 224 | 225 | This function allocates enough memory for holding all pixels in the 226 | bitmap, but it doesn't initialize the buffer. Applications can use 227 | to fill the bitmap using any color. 228 | 229 | 230 | 231 | 232 | Create a device independent bitmap (FXDIB). 233 | The number of pixels in width for the bitmap. Must be greater than 0. 234 | The number of pixels in height for the bitmap. Must be greater than 0. 235 | 236 | 237 | A pointer to the first byte of the first line if 238 | using an external buffer. If this parameter is 239 | then the a new buffer will be created. 240 | 241 | Number of bytes for each scan line, for external buffer only. 242 | The bitmap handle, or if parameter error or out of memory. 243 | 244 | Similar to function, but allows for more formats 245 | and an external buffer is supported. The bitmap created by this 246 | function can be used in any place that a handle is required. 247 | 248 | If an external buffer is used, then the application should destroy 249 | the buffer by itself. function will not destroy 250 | the buffer. 251 | 252 | 253 | 254 | 255 | Fill a rectangle in a bitmap. 256 | The handle to the bitmap. 257 | The left position. Starting from 0 at the left-most pixel. 258 | The top position. Starting from 0 at the top-most line. 259 | Width in pixels to be filled. 260 | Height in pixels to be filled. 261 | 262 | 263 | This function sets the color and (optionally) alpha value in the 264 | specified region of the bitmap. 265 | 266 | NOTE: If the alpha channel is used, this function does NOT 267 | composite the background with the source color, instead the 268 | background will be replaced by the source color and the alpha. 269 | 270 | If the alpha channel is not used, the alpha parameter is ignored. 271 | 272 | 273 | 274 | 275 | Get data buffer of a bitmap. 276 | Handle to the bitmap as returned by . 277 | The pointer to the first byte of the bitmap buffer. 278 | 279 | The stride may be more than width * number of bytes per pixel 280 | 281 | Applications can use this function to get the bitmap buffer pointer, 282 | then manipulate any color and/or alpha values for any pixels in the bitmap. 283 | 284 | The data is in BGRA format. Where the A maybe unused if alpha was 285 | not specified. 286 | 287 | 288 | 289 | 290 | Get width of a bitmap. 291 | Handle to the bitmap as returned by . 292 | The width of the bitmap in pixels. 293 | 294 | 295 | 296 | Get height of a bitmap. 297 | Handle to the bitmap as returned by . 298 | The height of the bitmap in pixels. 299 | 300 | 301 | 302 | Get number of bytes for each line in the bitmap buffer. 303 | Handle to the bitmap as returned by . 304 | The number of bytes for each line in the bitmap buffer. 305 | The stride may be more than width * number of bytes per pixel. 306 | 307 | 308 | 309 | Destroy a bitmap and release all related buffers. 310 | Handle to the bitmap as returned by . 311 | 312 | This function will not destroy any external buffers provided when 313 | the bitmap was created. 314 | 315 | 316 | 317 | 318 | Whether the PDF document prefers to be scaled or not. 319 | Handle to the loaded document. 320 | 321 | 322 | 323 | Returns the number of copies to be printed. 324 | Handle to the loaded document. 325 | The number of copies to be printed. 326 | 327 | 328 | 329 | Page numbers to initialize print dialog box when file is printed. 330 | Handle to the loaded document. 331 | The print page range to be used for printing. 332 | 333 | 334 | 335 | Returns the paper handling option to be used when printing from the print dialog. 336 | Handle to the loaded document. 337 | The paper handling option to be used when printing. 338 | 339 | 340 | 341 | Gets the contents for a viewer ref, with a given key. The value must be of type "name". 342 | Handle to the loaded document. 343 | Name of the key in the viewer pref dictionary. 344 | A string to write the contents of the key to. 345 | Length of the buffer. 346 | 347 | The number of bytes in the contents, including the NULL terminator. 348 | Thus if the return value is 0, then that indicates an error, such 349 | as when |document| is invalid or |buffer| is NULL. If |length| is 350 | less than the returned length, or |buffer| is NULL, |buffer| will 351 | not be modified. 352 | 353 | 354 | 355 | 356 | Get the count of named destinations in the PDF document. 357 | Handle to the loaded document. 358 | The count of named destinations. 359 | 360 | 361 | 362 | Get a the destination handle for the given name. 363 | Handle to the loaded document. 364 | The name of a destination. 365 | The handle to the destination. 366 | 367 | 368 | 369 | 370 | Create a new PDF document. 371 | Returns a handle to a new document, or NULL on failure. 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 | -------------------------------------------------------------------------------- /src/PdfLibCore/PdfiumBitmap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using PdfLibCore.Enums; 4 | using PdfLibCore.Types; 5 | 6 | // ReSharper disable MemberCanBePrivate.Global 7 | namespace PdfLibCore 8 | { 9 | /// 10 | /// A bitmap to which a can be rendered. 11 | /// 12 | public sealed class PdfiumBitmap : NativeWrapper 13 | { 14 | public int Width => Pdfium.FPDFBitmap_GetWidth(Handle); 15 | public int Height => Pdfium.FPDFBitmap_GetHeight(Handle); 16 | public int Stride => Pdfium.FPDFBitmap_GetStride(Handle); 17 | public IntPtr Scan0 => Pdfium.FPDFBitmap_GetBuffer(Handle); 18 | public int BytesPerPixel => GetBytesPerPixel(Format); 19 | public BitmapFormats Format { get; } 20 | 21 | private PdfiumBitmap(FPDF_BITMAP bitmap, BitmapFormats format) 22 | : base(bitmap.IsNull ? throw new Exception() : bitmap) => 23 | GetBytesPerPixel(Format = format); 24 | 25 | /// 26 | /// Creates a new . Unmanaged memory is allocated which must 27 | /// be freed by calling . 28 | /// 29 | /// The width of the new bitmap. 30 | /// The height of the new bitmap. 31 | /// A value indicating wheter the new bitmap has an alpha channel. 32 | /// 33 | /// A bitmap created with this overload always uses 4 bytes per pixel. 34 | /// Depending on the is then either 35 | /// or . 36 | /// 37 | public PdfiumBitmap(int width, int height, bool hasAlpha) 38 | : this(Pdfium.FPDFBitmap_Create(width, height, hasAlpha), hasAlpha ? BitmapFormats.RGBA : BitmapFormats.RGBx) => 39 | FillRectangle(0, 0, width, height, 0xFFFFFFFF); 40 | 41 | /// 42 | /// Creates a new using memory allocated by the caller. 43 | /// The caller is responsible for freeing the memory and that the adress stays 44 | /// valid during the lifetime of the returned . To free 45 | /// unmanaged resources, must be called. 46 | /// 47 | /// The width of the new bitmap. 48 | /// The height of the new bitmap. 49 | /// The format of the new bitmap. 50 | /// The adress of the memory block which holds the pixel values. 51 | /// The number of bytes per image row. 52 | public PdfiumBitmap(int width, int height, BitmapFormats format, IntPtr scan0, int stride) 53 | : this(Pdfium.FPDFBitmap_CreateEx(width, height, format, scan0, stride), format) => 54 | FillRectangle(0, 0, width, height, 0xFFFFFFFF); 55 | 56 | /// 57 | /// Fills a rectangle in the with . 58 | /// The pixel values in the rectangle are replaced and not blended. 59 | /// 60 | public void FillRectangle(int left, int top, int width, int height, FPDF_COLOR color) => 61 | Pdfium.FPDFBitmap_FillRect(Handle, left, top, width, height, color); 62 | 63 | /// 64 | /// Exposes the underlying image data directly as read-only stream in the 65 | /// BMP file format. 66 | /// 67 | public Stream AsBmpStream(double dpiX = 72, double dpiY = 72) 68 | { 69 | var stream = new BmpStream(this, dpiX, dpiY); 70 | stream.Position = 0; 71 | return stream; 72 | } 73 | 74 | /// 75 | /// Fills the whole with . 76 | /// The pixel values in the rectangle are replaced and not blended. 77 | /// 78 | /// 79 | public void Fill(FPDF_COLOR color) => 80 | FillRectangle(0, 0, Width, Height, color); 81 | 82 | public void Dispose() => 83 | ((IDisposable)this).Dispose(); 84 | 85 | protected override void Dispose(FPDF_BITMAP handle) => 86 | Pdfium.FPDFBitmap_Destroy(handle); 87 | 88 | private static int GetBytesPerPixel(BitmapFormats format) => format switch 89 | { 90 | BitmapFormats.RGB => 3, 91 | BitmapFormats.RGBA => 4, 92 | BitmapFormats.RGBx => 4, 93 | BitmapFormats.Gray => 1, 94 | _ => throw new ArgumentOutOfRangeException(nameof(format)) 95 | }; 96 | } 97 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PdfiumException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using PdfLibCore.Types; 3 | 4 | namespace PdfLibCore 5 | { 6 | public sealed class PdfiumException : Exception 7 | { 8 | public PdfiumException(Exception innerException) 9 | : base(innerException.Message, innerException) 10 | { 11 | } 12 | 13 | public PdfiumException() 14 | : base($"PDFium Error: {Pdfium.FPDF_GetLastError().GetDescription()}") 15 | { 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PdfLibCore/PinnedGCHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | // ReSharper disable UnusedType.Global 5 | // ReSharper disable UnusedMember.Global 6 | namespace PdfLibCore 7 | { 8 | public struct PinnedGcHandle : IDisposable 9 | { 10 | private GCHandle _handle; 11 | 12 | public IntPtr Pointer => _handle.AddrOfPinnedObject(); 13 | public bool IsAllocated => _handle.IsAllocated; 14 | public object Target => _handle.Target; 15 | 16 | private PinnedGcHandle(GCHandle handle) 17 | { 18 | _handle = handle; 19 | } 20 | 21 | public static PinnedGcHandle Pin(object obj) => new(GCHandle.Alloc(obj, GCHandleType.Pinned)); 22 | 23 | public void Free() => _handle.Free(); 24 | 25 | void IDisposable.Dispose() 26 | { 27 | if (_handle.IsAllocated) 28 | { 29 | _handle.Free(); 30 | } 31 | } 32 | 33 | public override string ToString() => _handle.ToString(); 34 | } 35 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_COLOR.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // ReSharper disable InconsistentNaming 4 | // ReSharper disable UnusedAutoPropertyAccessor.Global 5 | // ReSharper disable MemberCanBePrivate.Global 6 | namespace PdfLibCore.Types 7 | { 8 | [StructLayout(LayoutKind.Explicit)] 9 | public readonly struct FPDF_COLOR 10 | { 11 | [field: FieldOffset(0)] 12 | public byte A { get; } 13 | 14 | [field: FieldOffset(1)] 15 | public byte R { get; } 16 | 17 | [field: FieldOffset(2)] 18 | public byte G { get; } 19 | 20 | [field: FieldOffset(3)] 21 | public byte B { get; } 22 | 23 | [field: FieldOffset(0)] 24 | public uint ARGB { get; } 25 | 26 | public FPDF_COLOR(byte r, byte g, byte b, byte a = 255) 27 | { 28 | A = a; 29 | R = r; 30 | G = g; 31 | B = b; 32 | ARGB = a; 33 | } 34 | 35 | public FPDF_COLOR(uint argb) 36 | { 37 | A = 0; 38 | R = 0; 39 | G = 0; 40 | B = 0; 41 | ARGB = argb; 42 | } 43 | 44 | public static implicit operator FPDF_COLOR(uint argb) => new(argb); 45 | } 46 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_ERR.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Types 2 | { 3 | public enum FPDF_ERR : uint 4 | { 5 | /// 6 | /// No error. 7 | /// 8 | SUCCESS = 0, 9 | 10 | /// 11 | /// Unknown error. 12 | /// 13 | UNKNOWN = 1, 14 | 15 | /// 16 | /// File not found or could not be opened. 17 | /// 18 | FILE = 2, 19 | 20 | /// 21 | /// File not in PDF format or corrupted. 22 | /// 23 | FORMAT = 3, 24 | 25 | /// 26 | /// Password required or incorrect password. 27 | /// 28 | PASSWORD = 4, 29 | 30 | /// 31 | /// Unsupported security scheme. 32 | /// 33 | SECURITY = 5, 34 | 35 | /// 36 | /// Page not found or content error. 37 | /// 38 | PAGE = 6, 39 | 40 | /// 41 | /// Load XFA error. 42 | /// 43 | XFALOAD = 7, 44 | 45 | /// 46 | /// Layout XFA error. 47 | /// 48 | XFALAYOUT = 8 49 | } 50 | 51 | public static class FpdfErrorExtension 52 | { 53 | public static string GetDescription(this FPDF_ERR err) => err switch 54 | { 55 | FPDF_ERR.SUCCESS => "No error.", 56 | FPDF_ERR.UNKNOWN => "Unkown error.", 57 | FPDF_ERR.FILE => "File not found or could not be opened.", 58 | FPDF_ERR.FORMAT => "File not in PDF format or corrupted.", 59 | FPDF_ERR.PASSWORD => "Password required or incorrect password.", 60 | FPDF_ERR.SECURITY => "Unsupported security scheme.", 61 | FPDF_ERR.PAGE => "Page not found or content error.", 62 | FPDF_ERR.XFALOAD => "Load XFA error.", 63 | FPDF_ERR.XFALAYOUT => "Layout XFA error.", 64 | _ => $"{err} (No description available)." 65 | }; 66 | } 67 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_FILE_ACCESS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace PdfLibCore.Types 6 | { 7 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 8 | public delegate bool FileReadBlockHandler(IntPtr ignore, int position, IntPtr buffer, int size); 9 | 10 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 11 | public delegate bool FileWriteBlockHandler(IntPtr ignore, IntPtr data, int size); 12 | 13 | [StructLayout(LayoutKind.Sequential)] 14 | public class FPDF_FILEREAD 15 | { 16 | // ReSharper disable once ArrangeTypeMemberModifiers 17 | // ReSharper disable once NotAccessedField.Local 18 | [MarshalAs(UnmanagedType.FunctionPtr)] readonly FileReadBlockHandler _readBlock; 19 | 20 | private FPDF_FILEREAD(FileReadBlockHandler readBlock) 21 | { 22 | _readBlock = readBlock; 23 | } 24 | 25 | public static FPDF_FILEREAD FromStream(Stream stream) 26 | { 27 | var start = stream.Position; 28 | byte[] data = null; 29 | var fileread = new FPDF_FILEREAD((ignore, position, buffer, size) => 30 | { 31 | stream.Position = start + position; 32 | if (data == null || data.Length < size) 33 | { 34 | data = new byte[size]; 35 | } 36 | if (stream.Read(data, 0, size) != size) 37 | { 38 | return false; 39 | } 40 | Marshal.Copy(data, 0, buffer, size); 41 | return true; 42 | }); 43 | return fileread; 44 | } 45 | } 46 | 47 | [StructLayout(LayoutKind.Sequential)] 48 | public class FPDF_FILEWRITE 49 | { 50 | private int Version = 1; 51 | 52 | // ReSharper disable once ArrangeTypeMemberModifiers 53 | // ReSharper disable once NotAccessedField.Local 54 | // ReSharper disable once MemberCanBePrivate.Global 55 | [MarshalAs(UnmanagedType.FunctionPtr)] public FileWriteBlockHandler WriteBlock; 56 | 57 | public FPDF_FILEWRITE(FileWriteBlockHandler writeBlock) 58 | { 59 | WriteBlock = writeBlock; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_IMAGEOBJ_METADATA.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // ReSharper disable UnusedAutoPropertyAccessor.Global 4 | // ReSharper disable MemberCanBePrivate.Global 5 | namespace PdfLibCore.Types 6 | { 7 | [StructLayout(LayoutKind.Sequential)] 8 | public readonly struct FPDF_IMAGEOBJ_METADATA 9 | { 10 | // The image width in pixels. 11 | public uint Width { get; } 12 | 13 | // The image height in pixels. 14 | public uint Height { get; } 15 | 16 | // The image's horizontal pixel-per-inch. 17 | public float HorizontalDpi { get; } 18 | 19 | // The image's vertical pixel-per-inch. 20 | public float VerticalDpi { get; } 21 | 22 | // The number of bits used to represent each pixel. 23 | public uint BitsPerPixel { get; } 24 | 25 | // The image's colorspace. See above for the list of FPDF_COLORSPACE_*. 26 | public int Colorspace { get; } 27 | 28 | // The image's marked content ID. Useful for pairing with associated alt-text. 29 | // A value of -1 indicates no ID. 30 | public int MarkedContentId { get; } 31 | 32 | public FPDF_IMAGEOBJ_METADATA(uint width, uint height, float horizontalDpi, float verticalDpi, uint bitsPerPixel, int colorspace, int markedContentId) 33 | { 34 | Width = width; 35 | Height = height; 36 | HorizontalDpi = horizontalDpi; 37 | VerticalDpi = verticalDpi; 38 | BitsPerPixel = bitsPerPixel; 39 | Colorspace = colorspace; 40 | MarkedContentId = markedContentId; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_LIBRARY_CONFIG.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace PdfLibCore.Types 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | public readonly struct FPDF_LIBRARY_CONFIG 8 | { 9 | private readonly int _version; 10 | private readonly IntPtr _userFontPaths; 11 | private readonly IntPtr _v8Isolate; 12 | private readonly uint _v8EmbedderSlot; 13 | 14 | // ReSharper disable once UnusedMember.Global 15 | // ReSharper disable once ConvertToAutoProperty 16 | public int Version => _version; 17 | } 18 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_TEXT_RENDERMODE.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable InconsistentNaming 2 | namespace PdfLibCore.Types 3 | { 4 | /// 5 | /// PDF text rendering modes 6 | /// 7 | public enum FPDF_TEXT_RENDERMODE 8 | { 9 | UNKNOWN = -1, 10 | FILL = 0, 11 | STROKE = 1, 12 | FILL_STROKE = 2, 13 | INVISIBLE = 3, 14 | FILL_CLIP = 4, 15 | STROKE_CLIP = 5, 16 | FILL_STROKE_CLIP = 6, 17 | CLIP = 7, 18 | LAST = CLIP, 19 | } 20 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_TypeDef.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of PdfCoreLib, a wrapper around the PDFium library for the .NET. 3 | Inspired by the awesome work of PDFiumSharp by Tobias Meyer. 4 | 5 | Copyright (C) 2021 Jan Baarsssen 6 | License: Microsoft Reciprocal License (MS-RL) 7 | */ 8 | 9 | // AUTOGENERATED FILE 10 | // DO NOT MODIFY 11 | using System; 12 | using System.Runtime.InteropServices; 13 | using System.Threading; 14 | 15 | namespace PdfLibCore.Types 16 | { 17 | 18 | /// Handle to a FPDF_ACTION 19 | [StructLayout(LayoutKind.Sequential)] 20 | public struct FPDF_ACTION : IHandle 21 | { 22 | private IntPtr _pointer; 23 | 24 | /// Gets a value indicating whether the handle is null. 25 | public bool IsNull => _pointer == IntPtr.Zero; 26 | 27 | /// Gets a handle representing null. 28 | public static FPDF_ACTION Null => new(); 29 | 30 | private FPDF_ACTION(IntPtr ptr) => _pointer = ptr; 31 | 32 | FPDF_ACTION IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 33 | 34 | public override string ToString() => $"FPDF_ACTION: 0x{_pointer.ToString("X16")}"; 35 | } 36 | 37 | /// Handle to a FPDF_ANNOTATION 38 | [StructLayout(LayoutKind.Sequential)] 39 | public struct FPDF_ANNOTATION : IHandle 40 | { 41 | private IntPtr _pointer; 42 | 43 | /// Gets a value indicating whether the handle is null. 44 | public bool IsNull => _pointer == IntPtr.Zero; 45 | 46 | /// Gets a handle representing null. 47 | public static FPDF_ANNOTATION Null => new(); 48 | 49 | private FPDF_ANNOTATION(IntPtr ptr) => _pointer = ptr; 50 | 51 | FPDF_ANNOTATION IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 52 | 53 | public override string ToString() => $"FPDF_ANNOTATION: 0x{_pointer.ToString("X16")}"; 54 | } 55 | 56 | /// Handle to a FPDF_ATTACHMENT 57 | [StructLayout(LayoutKind.Sequential)] 58 | public struct FPDF_ATTACHMENT : IHandle 59 | { 60 | private IntPtr _pointer; 61 | 62 | /// Gets a value indicating whether the handle is null. 63 | public bool IsNull => _pointer == IntPtr.Zero; 64 | 65 | /// Gets a handle representing null. 66 | public static FPDF_ATTACHMENT Null => new(); 67 | 68 | private FPDF_ATTACHMENT(IntPtr ptr) => _pointer = ptr; 69 | 70 | FPDF_ATTACHMENT IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 71 | 72 | public override string ToString() => $"FPDF_ATTACHMENT: 0x{_pointer.ToString("X16")}"; 73 | } 74 | 75 | /// Handle to a FPDF_BITMAP 76 | [StructLayout(LayoutKind.Sequential)] 77 | public struct FPDF_BITMAP : IHandle 78 | { 79 | private IntPtr _pointer; 80 | 81 | /// Gets a value indicating whether the handle is null. 82 | public bool IsNull => _pointer == IntPtr.Zero; 83 | 84 | /// Gets a handle representing null. 85 | public static FPDF_BITMAP Null => new(); 86 | 87 | private FPDF_BITMAP(IntPtr ptr) => _pointer = ptr; 88 | 89 | FPDF_BITMAP IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 90 | 91 | public override string ToString() => $"FPDF_BITMAP: 0x{_pointer.ToString("X16")}"; 92 | } 93 | 94 | /// Handle to a FPDF_BOOKMARK 95 | [StructLayout(LayoutKind.Sequential)] 96 | public struct FPDF_BOOKMARK : IHandle 97 | { 98 | private IntPtr _pointer; 99 | 100 | /// Gets a value indicating whether the handle is null. 101 | public bool IsNull => _pointer == IntPtr.Zero; 102 | 103 | /// Gets a handle representing null. 104 | public static FPDF_BOOKMARK Null => new(); 105 | 106 | private FPDF_BOOKMARK(IntPtr ptr) => _pointer = ptr; 107 | 108 | FPDF_BOOKMARK IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 109 | 110 | public override string ToString() => $"FPDF_BOOKMARK: 0x{_pointer.ToString("X16")}"; 111 | } 112 | 113 | /// Handle to a FPDF_CLIPPATH 114 | [StructLayout(LayoutKind.Sequential)] 115 | public struct FPDF_CLIPPATH : IHandle 116 | { 117 | private IntPtr _pointer; 118 | 119 | /// Gets a value indicating whether the handle is null. 120 | public bool IsNull => _pointer == IntPtr.Zero; 121 | 122 | /// Gets a handle representing null. 123 | public static FPDF_CLIPPATH Null => new(); 124 | 125 | private FPDF_CLIPPATH(IntPtr ptr) => _pointer = ptr; 126 | 127 | FPDF_CLIPPATH IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 128 | 129 | public override string ToString() => $"FPDF_CLIPPATH: 0x{_pointer.ToString("X16")}"; 130 | } 131 | 132 | /// Handle to a FPDF_DEST 133 | [StructLayout(LayoutKind.Sequential)] 134 | public struct FPDF_DEST : IHandle 135 | { 136 | private IntPtr _pointer; 137 | 138 | /// Gets a value indicating whether the handle is null. 139 | public bool IsNull => _pointer == IntPtr.Zero; 140 | 141 | /// Gets a handle representing null. 142 | public static FPDF_DEST Null => new(); 143 | 144 | private FPDF_DEST(IntPtr ptr) => _pointer = ptr; 145 | 146 | FPDF_DEST IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 147 | 148 | public override string ToString() => $"FPDF_DEST: 0x{_pointer.ToString("X16")}"; 149 | } 150 | 151 | /// Handle to a FPDF_DOCUMENT 152 | [StructLayout(LayoutKind.Sequential)] 153 | public struct FPDF_DOCUMENT : IHandle 154 | { 155 | private IntPtr _pointer; 156 | 157 | /// Gets a value indicating whether the handle is null. 158 | public bool IsNull => _pointer == IntPtr.Zero; 159 | 160 | /// Gets a handle representing null. 161 | public static FPDF_DOCUMENT Null => new(); 162 | 163 | private FPDF_DOCUMENT(IntPtr ptr) => _pointer = ptr; 164 | 165 | FPDF_DOCUMENT IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 166 | 167 | public override string ToString() => $"FPDF_DOCUMENT: 0x{_pointer.ToString("X16")}"; 168 | } 169 | 170 | /// Handle to a FPDF_FONT 171 | [StructLayout(LayoutKind.Sequential)] 172 | public struct FPDF_FONT : IHandle 173 | { 174 | private IntPtr _pointer; 175 | 176 | /// Gets a value indicating whether the handle is null. 177 | public bool IsNull => _pointer == IntPtr.Zero; 178 | 179 | /// Gets a handle representing null. 180 | public static FPDF_FONT Null => new(); 181 | 182 | private FPDF_FONT(IntPtr ptr) => _pointer = ptr; 183 | 184 | FPDF_FONT IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 185 | 186 | public override string ToString() => $"FPDF_FONT: 0x{_pointer.ToString("X16")}"; 187 | } 188 | 189 | /// Handle to a FPDF_FORMHANDLE 190 | [StructLayout(LayoutKind.Sequential)] 191 | public struct FPDF_FORMHANDLE : IHandle 192 | { 193 | private IntPtr _pointer; 194 | 195 | /// Gets a value indicating whether the handle is null. 196 | public bool IsNull => _pointer == IntPtr.Zero; 197 | 198 | /// Gets a handle representing null. 199 | public static FPDF_FORMHANDLE Null => new(); 200 | 201 | private FPDF_FORMHANDLE(IntPtr ptr) => _pointer = ptr; 202 | 203 | FPDF_FORMHANDLE IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 204 | 205 | public override string ToString() => $"FPDF_FORMHANDLE: 0x{_pointer.ToString("X16")}"; 206 | } 207 | 208 | /// Handle to a FPDF_JAVASCRIPT_ACTION 209 | [StructLayout(LayoutKind.Sequential)] 210 | public struct FPDF_JAVASCRIPT_ACTION : IHandle 211 | { 212 | private IntPtr _pointer; 213 | 214 | /// Gets a value indicating whether the handle is null. 215 | public bool IsNull => _pointer == IntPtr.Zero; 216 | 217 | /// Gets a handle representing null. 218 | public static FPDF_JAVASCRIPT_ACTION Null => new(); 219 | 220 | private FPDF_JAVASCRIPT_ACTION(IntPtr ptr) => _pointer = ptr; 221 | 222 | FPDF_JAVASCRIPT_ACTION IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 223 | 224 | public override string ToString() => $"FPDF_JAVASCRIPT_ACTION: 0x{_pointer.ToString("X16")}"; 225 | } 226 | 227 | /// Handle to a FPDF_LINK 228 | [StructLayout(LayoutKind.Sequential)] 229 | public struct FPDF_LINK : IHandle 230 | { 231 | private IntPtr _pointer; 232 | 233 | /// Gets a value indicating whether the handle is null. 234 | public bool IsNull => _pointer == IntPtr.Zero; 235 | 236 | /// Gets a handle representing null. 237 | public static FPDF_LINK Null => new(); 238 | 239 | private FPDF_LINK(IntPtr ptr) => _pointer = ptr; 240 | 241 | FPDF_LINK IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 242 | 243 | public override string ToString() => $"FPDF_LINK: 0x{_pointer.ToString("X16")}"; 244 | } 245 | 246 | /// Handle to a FPDF_PAGE 247 | [StructLayout(LayoutKind.Sequential)] 248 | public struct FPDF_PAGE : IHandle 249 | { 250 | private IntPtr _pointer; 251 | 252 | /// Gets a value indicating whether the handle is null. 253 | public bool IsNull => _pointer == IntPtr.Zero; 254 | 255 | /// Gets a handle representing null. 256 | public static FPDF_PAGE Null => new(); 257 | 258 | private FPDF_PAGE(IntPtr ptr) => _pointer = ptr; 259 | 260 | FPDF_PAGE IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 261 | 262 | public override string ToString() => $"FPDF_PAGE: 0x{_pointer.ToString("X16")}"; 263 | } 264 | 265 | /// Handle to a FPDF_PAGELINK 266 | [StructLayout(LayoutKind.Sequential)] 267 | public struct FPDF_PAGELINK : IHandle 268 | { 269 | private IntPtr _pointer; 270 | 271 | /// Gets a value indicating whether the handle is null. 272 | public bool IsNull => _pointer == IntPtr.Zero; 273 | 274 | /// Gets a handle representing null. 275 | public static FPDF_PAGELINK Null => new(); 276 | 277 | private FPDF_PAGELINK(IntPtr ptr) => _pointer = ptr; 278 | 279 | FPDF_PAGELINK IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 280 | 281 | public override string ToString() => $"FPDF_PAGELINK: 0x{_pointer.ToString("X16")}"; 282 | } 283 | 284 | /// Handle to a FPDF_PAGEOBJECT 285 | [StructLayout(LayoutKind.Sequential)] 286 | public struct FPDF_PAGEOBJECT : IHandle 287 | { 288 | private IntPtr _pointer; 289 | 290 | /// Gets a value indicating whether the handle is null. 291 | public bool IsNull => _pointer == IntPtr.Zero; 292 | 293 | /// Gets a handle representing null. 294 | public static FPDF_PAGEOBJECT Null => new(); 295 | 296 | private FPDF_PAGEOBJECT(IntPtr ptr) => _pointer = ptr; 297 | 298 | FPDF_PAGEOBJECT IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 299 | 300 | public override string ToString() => $"FPDF_PAGEOBJECT: 0x{_pointer.ToString("X16")}"; 301 | } 302 | 303 | /// Handle to a FPDF_PAGEOBJECTMARK 304 | [StructLayout(LayoutKind.Sequential)] 305 | public struct FPDF_PAGEOBJECTMARK : IHandle 306 | { 307 | private IntPtr _pointer; 308 | 309 | /// Gets a value indicating whether the handle is null. 310 | public bool IsNull => _pointer == IntPtr.Zero; 311 | 312 | /// Gets a handle representing null. 313 | public static FPDF_PAGEOBJECTMARK Null => new(); 314 | 315 | private FPDF_PAGEOBJECTMARK(IntPtr ptr) => _pointer = ptr; 316 | 317 | FPDF_PAGEOBJECTMARK IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 318 | 319 | public override string ToString() => $"FPDF_PAGEOBJECTMARK: 0x{_pointer.ToString("X16")}"; 320 | } 321 | 322 | /// Handle to a FPDF_PAGERANGE 323 | [StructLayout(LayoutKind.Sequential)] 324 | public struct FPDF_PAGERANGE : IHandle 325 | { 326 | private IntPtr _pointer; 327 | 328 | /// Gets a value indicating whether the handle is null. 329 | public bool IsNull => _pointer == IntPtr.Zero; 330 | 331 | /// Gets a handle representing null. 332 | public static FPDF_PAGERANGE Null => new(); 333 | 334 | private FPDF_PAGERANGE(IntPtr ptr) => _pointer = ptr; 335 | 336 | FPDF_PAGERANGE IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 337 | 338 | public override string ToString() => $"FPDF_PAGERANGE: 0x{_pointer.ToString("X16")}"; 339 | } 340 | 341 | /// Handle to a FPDF_PATHSEGMENT 342 | [StructLayout(LayoutKind.Sequential)] 343 | public struct FPDF_PATHSEGMENT : IHandle 344 | { 345 | private IntPtr _pointer; 346 | 347 | /// Gets a value indicating whether the handle is null. 348 | public bool IsNull => _pointer == IntPtr.Zero; 349 | 350 | /// Gets a handle representing null. 351 | public static FPDF_PATHSEGMENT Null => new(); 352 | 353 | private FPDF_PATHSEGMENT(IntPtr ptr) => _pointer = ptr; 354 | 355 | FPDF_PATHSEGMENT IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 356 | 357 | public override string ToString() => $"FPDF_PATHSEGMENT: 0x{_pointer.ToString("X16")}"; 358 | } 359 | 360 | /// Handle to a FPDF_RECORDER 361 | [StructLayout(LayoutKind.Sequential)] 362 | public struct FPDF_RECORDER : IHandle 363 | { 364 | private IntPtr _pointer; 365 | 366 | /// Gets a value indicating whether the handle is null. 367 | public bool IsNull => _pointer == IntPtr.Zero; 368 | 369 | /// Gets a handle representing null. 370 | public static FPDF_RECORDER Null => new(); 371 | 372 | private FPDF_RECORDER(IntPtr ptr) => _pointer = ptr; 373 | 374 | FPDF_RECORDER IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 375 | 376 | public override string ToString() => $"FPDF_RECORDER: 0x{_pointer.ToString("X16")}"; 377 | } 378 | 379 | /// Handle to a FPDF_SCHHANDLE 380 | [StructLayout(LayoutKind.Sequential)] 381 | public struct FPDF_SCHHANDLE : IHandle 382 | { 383 | private IntPtr _pointer; 384 | 385 | /// Gets a value indicating whether the handle is null. 386 | public bool IsNull => _pointer == IntPtr.Zero; 387 | 388 | /// Gets a handle representing null. 389 | public static FPDF_SCHHANDLE Null => new(); 390 | 391 | private FPDF_SCHHANDLE(IntPtr ptr) => _pointer = ptr; 392 | 393 | FPDF_SCHHANDLE IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 394 | 395 | public override string ToString() => $"FPDF_SCHHANDLE: 0x{_pointer.ToString("X16")}"; 396 | } 397 | 398 | /// Handle to a FPDF_STRUCTELEMENT 399 | [StructLayout(LayoutKind.Sequential)] 400 | public struct FPDF_STRUCTELEMENT : IHandle 401 | { 402 | private IntPtr _pointer; 403 | 404 | /// Gets a value indicating whether the handle is null. 405 | public bool IsNull => _pointer == IntPtr.Zero; 406 | 407 | /// Gets a handle representing null. 408 | public static FPDF_STRUCTELEMENT Null => new(); 409 | 410 | private FPDF_STRUCTELEMENT(IntPtr ptr) => _pointer = ptr; 411 | 412 | FPDF_STRUCTELEMENT IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 413 | 414 | public override string ToString() => $"FPDF_STRUCTELEMENT: 0x{_pointer.ToString("X16")}"; 415 | } 416 | 417 | /// Handle to a FPDF_STRUCTTREE 418 | [StructLayout(LayoutKind.Sequential)] 419 | public struct FPDF_STRUCTTREE : IHandle 420 | { 421 | private IntPtr _pointer; 422 | 423 | /// Gets a value indicating whether the handle is null. 424 | public bool IsNull => _pointer == IntPtr.Zero; 425 | 426 | /// Gets a handle representing null. 427 | public static FPDF_STRUCTTREE Null => new(); 428 | 429 | private FPDF_STRUCTTREE(IntPtr ptr) => _pointer = ptr; 430 | 431 | FPDF_STRUCTTREE IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 432 | 433 | public override string ToString() => $"FPDF_STRUCTTREE: 0x{_pointer.ToString("X16")}"; 434 | } 435 | 436 | /// Handle to a FPDF_TEXTPAGE 437 | [StructLayout(LayoutKind.Sequential)] 438 | public struct FPDF_TEXTPAGE : IHandle 439 | { 440 | private IntPtr _pointer; 441 | 442 | /// Gets a value indicating whether the handle is null. 443 | public bool IsNull => _pointer == IntPtr.Zero; 444 | 445 | /// Gets a handle representing null. 446 | public static FPDF_TEXTPAGE Null => new(); 447 | 448 | private FPDF_TEXTPAGE(IntPtr ptr) => _pointer = ptr; 449 | 450 | FPDF_TEXTPAGE IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 451 | 452 | public override string ToString() => $"FPDF_TEXTPAGE: 0x{_pointer.ToString("X16")}"; 453 | } 454 | 455 | /// Handle to a FPDF_WIDGET 456 | [StructLayout(LayoutKind.Sequential)] 457 | public struct FPDF_WIDGET : IHandle 458 | { 459 | private IntPtr _pointer; 460 | 461 | /// Gets a value indicating whether the handle is null. 462 | public bool IsNull => _pointer == IntPtr.Zero; 463 | 464 | /// Gets a handle representing null. 465 | public static FPDF_WIDGET Null => new(); 466 | 467 | private FPDF_WIDGET(IntPtr ptr) => _pointer = ptr; 468 | 469 | FPDF_WIDGET IHandle.SetToNull() => new(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 470 | 471 | public override string ToString() => $"FPDF_WIDGET: 0x{_pointer.ToString("X16")}"; 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FPDF_TypeDef.tt: -------------------------------------------------------------------------------- 1 | <#@ template language="C#" hostspecific="true"#> 2 | <#@ output extension=".cs" #> 3 | <# // ------------------------------------------------------ Code Start ------------------------------------------------------ 4 | //This types from here: https://pdfium.googlesource.com/pdfium/+/master/public/fpdfview.h from line: // PDF types - use incomplete types (never completed) just for API type safety. 5 | string[] names = 6 | { 7 | "FPDF_ACTION", 8 | "FPDF_ANNOTATION", 9 | "FPDF_ATTACHMENT", 10 | "FPDF_BITMAP", 11 | "FPDF_BOOKMARK", 12 | "FPDF_CLIPPATH", 13 | "FPDF_DEST", 14 | "FPDF_DOCUMENT", 15 | "FPDF_FONT", 16 | "FPDF_FORMHANDLE", 17 | "FPDF_JAVASCRIPT_ACTION", 18 | "FPDF_LINK", 19 | "FPDF_PAGE", 20 | "FPDF_PAGELINK", 21 | "FPDF_PAGEOBJECT", // Page object(text, path, etc) 22 | "FPDF_PAGEOBJECTMARK", 23 | "FPDF_PAGERANGE", 24 | "FPDF_PATHSEGMENT", 25 | "FPDF_RECORDER", 26 | "FPDF_SCHHANDLE", 27 | "FPDF_STRUCTELEMENT", 28 | "FPDF_STRUCTTREE", 29 | "FPDF_TEXTPAGE", 30 | "FPDF_WIDGET" 31 | }; 32 | // --------------------------------------------------------- Code End ----------------------------------------------------- #> 33 | /* 34 | This file is part of PdfCoreLib, a wrapper around the PDFium library for the .NET. 35 | Inspired by the awesome work of PDFiumSharp by Tobias Meyer. 36 | 37 | Copyright (C) 2021 Jan Baarsssen 38 | License: Microsoft Reciprocal License (MS-RL) 39 | */ 40 | 41 | // AUTOGENERATED FILE 42 | // DO NOT MODIFY 43 | using System; 44 | using System.Runtime.InteropServices; 45 | using System.Threading; 46 | 47 | namespace PdfLibCore.Types 48 | { 49 | <# // ------------------------------------------------------ Code Start ------------------------------------------------------ 50 | foreach (var name in names) 51 | { 52 | // --------------------------------------------------------- Code End ----------------------------------------------------- #> 53 | 54 | /// Handle to a <#= name #> 55 | [StructLayout(LayoutKind.Sequential)] 56 | public struct <#= name #> : IHandle<<#= name #>> 57 | { 58 | private IntPtr _pointer; 59 | 60 | /// Gets a value indicating whether the handle is null. 61 | public bool IsNull => _pointer == IntPtr.Zero; 62 | 63 | /// Gets a handle representing null. 64 | public static <#= name #> Null => new <#= name #>(); 65 | 66 | private <#= name #>(IntPtr ptr) => _pointer = ptr; 67 | 68 | <#= name #> IHandle<<#= name #>>.SetToNull() => new <#= name #>(Interlocked.Exchange(ref _pointer, IntPtr.Zero)); 69 | 70 | public override string ToString() => $"<#= name #>: 0x{_pointer.ToString("X16")}"; 71 | } 72 | <# // ------------------------------------------------------ Code Start ------------------------------------------------------ 73 | } 74 | // --------------------------------------------------------- Code End ----------------------------------------------------- #> 75 | } 76 | -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FS_MATRIX.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace PdfLibCore.Types 4 | { 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedAutoPropertyAccessor.Global 7 | [StructLayout(LayoutKind.Sequential)] 8 | public readonly struct FS_MATRIX 9 | { 10 | public float A { get; } 11 | public float B { get; } 12 | public float C { get; } 13 | public float D { get; } 14 | public float E { get; } 15 | public float F { get; } 16 | 17 | public FS_MATRIX(float a, float b, float c, float d, float e, float f) 18 | { 19 | A = a; 20 | B = b; 21 | C = c; 22 | D = d; 23 | E = e; 24 | F = f; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FS_QUADPOINTSF.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace PdfLibCore.Types 4 | { 5 | // ReSharper disable MemberCanBePrivate.Global 6 | // ReSharper disable UnusedAutoPropertyAccessor.Global 7 | [StructLayout(LayoutKind.Sequential)] 8 | public readonly struct FS_QUADPOINTSF 9 | { 10 | public float X1 { get; } 11 | public float Y1 { get; } 12 | public float X2 { get; } 13 | public float Y2 { get; } 14 | public float X3 { get; } 15 | public float Y3 { get; } 16 | public float X4 { get; } 17 | public float Y4 { get; } 18 | 19 | public FS_QUADPOINTSF(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) 20 | { 21 | X1 = x1; 22 | Y1 = y1; 23 | X2 = x2; 24 | Y2 = y2; 25 | X3 = x3; 26 | Y3 = y3; 27 | X4 = x4; 28 | Y4 = y4; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FS_RECTF.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace PdfLibCore.Types 4 | { 5 | /// 6 | /// Rectangle area(float) in device or page coordinate system. 7 | /// 8 | // ReSharper disable MemberCanBePrivate.Global 9 | // ReSharper disable UnusedAutoPropertyAccessor.Global 10 | [StructLayout(LayoutKind.Sequential)] 11 | public readonly struct FS_RECTF 12 | { 13 | /// 14 | /// The x-coordinate of the left-top corner. 15 | /// 16 | public float Left { get; } 17 | 18 | /// 19 | /// The y-coordinate of the left-top corner. 20 | /// 21 | public float Top { get; } 22 | 23 | /// 24 | /// The x-coordinate of the right-bottom corner. 25 | /// 26 | public float Right { get; } 27 | 28 | /// 29 | /// The y-coordinate of the right-bottom corner. 30 | /// 31 | public float Bottom { get; } 32 | 33 | public FS_RECTF(float left, float top, float right, float bottom) 34 | { 35 | Left = left; 36 | Top = top; 37 | Right = right; 38 | Bottom = bottom; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/FS_SIZEF.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace PdfLibCore.Types 4 | { 5 | /// 6 | /// Rectangle size. Coordinate system agnostic. 7 | /// 8 | // ReSharper disable MemberCanBePrivate.Global 9 | // ReSharper disable UnusedAutoPropertyAccessor.Global 10 | [StructLayout(LayoutKind.Sequential)] 11 | public readonly struct FS_SIZEF 12 | { 13 | public float Width { get; } 14 | public float Height { get; } 15 | 16 | public FS_SIZEF(float width, float height) 17 | { 18 | Width = width; 19 | Height = height; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/IFSDK_PAUSE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | // ReSharper disable NotAccessedField.Local 5 | namespace PdfLibCore.Types 6 | { 7 | // ReSharper disable MemberCanBePrivate.Global 8 | // ReSharper disable UnusedAutoPropertyAccessor.Global 9 | [StructLayout(LayoutKind.Sequential)] 10 | public abstract class IFSDK_PAUSE 11 | { 12 | [MarshalAs(UnmanagedType.FunctionPtr)] 13 | private readonly Func _needToPauseCore; 14 | private readonly IntPtr _userData; 15 | private readonly Func _needToPause; 16 | 17 | protected IFSDK_PAUSE(Func needToPause) 18 | { 19 | _needToPause = needToPause ?? throw new ArgumentNullException(nameof(needToPause)); 20 | _needToPauseCore = ignore => needToPause(); 21 | _userData = IntPtr.Zero; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/IHandle.cs: -------------------------------------------------------------------------------- 1 | namespace PdfLibCore.Types 2 | { 3 | public interface IHandle 4 | { 5 | bool IsNull { get; } 6 | 7 | T SetToNull(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PdfLibCore/Types/NativeWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PdfLibCore.Types 4 | { 5 | public class NativeWrapper : IDisposable 6 | where T : struct, IHandle 7 | { 8 | protected PdfDocument Document { get; } 9 | 10 | private T _handle; 11 | 12 | /// 13 | /// Handle which can be used with the native functions. 14 | /// 15 | public T Handle => IsDisposed ? throw new ObjectDisposedException(GetType().FullName) : _handle; 16 | 17 | /// 18 | /// Gets a value indicating whether was already 19 | /// called on this instance. 20 | /// 21 | public bool IsDisposed => _handle.IsNull; 22 | 23 | protected NativeWrapper(PdfDocument document, T handle) 24 | : this(handle) => 25 | Document = document ?? throw new PdfiumException(); 26 | 27 | protected NativeWrapper(T handle) => 28 | _handle = handle.IsNull ? throw new PdfiumException() : handle; 29 | 30 | /// 31 | /// Implementors should clean up here. This method is guaranteed to only be called once. 32 | /// 33 | protected virtual void Dispose(T handle) 34 | { 35 | } 36 | 37 | void IDisposable.Dispose() 38 | { 39 | var oldHandle = _handle.SetToNull(); 40 | if (!oldHandle.IsNull) 41 | { 42 | Dispose(oldHandle); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/UnitTests/PdfLibCore.UnitTests/Data/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbaarssen/PdfLibCore/6b155b257e49ac02da2bc33f52c4f199d50cf5bf/src/UnitTests/PdfLibCore.UnitTests/Data/test.pdf -------------------------------------------------------------------------------- /src/UnitTests/PdfLibCore.UnitTests/PdfDocumentTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.IO; 4 | using FluentAssertions; 5 | using PdfLibCore.Enums; 6 | using Xunit; 7 | 8 | namespace PdfLibCore.UnitTests 9 | { 10 | [ExcludeFromCodeCoverage] 11 | public class PdfDocumentTests 12 | { 13 | private readonly string _path; 14 | 15 | public PdfDocumentTests() 16 | { 17 | _path = Path.Combine(Environment.CurrentDirectory, "Data", "test.pdf"); 18 | } 19 | 20 | [Fact] 21 | public void Create_New_PdfDocument() 22 | { 23 | using var pdfDocument = new PdfDocument(); 24 | pdfDocument.Should().NotBeNull(); 25 | pdfDocument.Pages.Count.Should().Be(0); 26 | } 27 | 28 | [Fact] 29 | public void Open_PdfDocument_Filepath() 30 | { 31 | using var pdfDocument = new PdfDocument(_path); 32 | pdfDocument.Should().NotBeNull(); 33 | pdfDocument.Pages.Count.Should().Be(1); 34 | } 35 | 36 | [Fact] 37 | public void Open_PdfDocument_Filepath_Non_Existant_File() 38 | { 39 | var path = Path.Combine(Environment.CurrentDirectory, "Data", "test2.pdf"); 40 | Assert.Throws(() => new PdfDocument(path)); 41 | } 42 | 43 | [Fact] 44 | public void Open_PdfDocument_Stream() 45 | { 46 | using var stream = new FileStream(_path, FileMode.Open, FileAccess.Read); 47 | using var pdfDocument = new PdfDocument(stream); 48 | pdfDocument.Should().NotBeNull(); 49 | pdfDocument.Pages.Count.Should().Be(1); 50 | } 51 | 52 | [Fact] 53 | public void Open_PdfDocument_Bytes() 54 | { 55 | using var pdfDocument = new PdfDocument(File.ReadAllBytes(_path)); 56 | pdfDocument.Should().NotBeNull(); 57 | pdfDocument.Pages.Count.Should().Be(1); 58 | } 59 | 60 | [Fact(Skip = "PageRange not present...")] 61 | public void Get_PageRange_From_Document() 62 | { 63 | using var pdfDocument = new PdfDocument(_path); 64 | 65 | pdfDocument.PageRange.Should().NotBeNull(); 66 | pdfDocument.PageRange.PrintRangeElement(0).Should().Be(0); 67 | pdfDocument.PageRange.PrintPageRangeCount.Should().Be(1); 68 | } 69 | 70 | [Fact] 71 | public void Get_PageMode_From_Document() 72 | { 73 | using var pdfDocument = new PdfDocument(_path); 74 | pdfDocument.PageMode.Should().Be(PageModes.UseNone); 75 | } 76 | 77 | [Fact] 78 | public void Get_FileVersion_From_Document() 79 | { 80 | using var pdfDocument = new PdfDocument(_path); 81 | pdfDocument.FileVersion.Should().Be(15); 82 | using var ms = new MemoryStream(); 83 | pdfDocument.Save(ms, SaveFlags.None, 14); 84 | using var result = new PdfDocument(ms); 85 | result.FileVersion.Should().Be(14); 86 | } 87 | 88 | [Fact] 89 | public void Get_Permissions_From_Document() 90 | { 91 | using var pdfDocument = new PdfDocument(_path); 92 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.Modify); 93 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.Print); 94 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.AssembleDocument); 95 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.ModfiyAnnotations); 96 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.FillInForms); 97 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.PrintHighQuality); 98 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.ExtractTextAndGraphics); 99 | pdfDocument.Permissions.Should().HaveFlag(DocumentPermissions.ExtractTextAndGraphics2); 100 | } 101 | 102 | [Fact] 103 | public void Get_Page_From_Document() 104 | { 105 | using var pdfDocument = new PdfDocument(_path); 106 | pdfDocument.Pages.Should().HaveCount(1); 107 | pdfDocument.Pages.Count.Should().Be(1); 108 | 109 | using var page = pdfDocument.Pages[0]; 110 | page.Should().NotBeNull(); 111 | page.Width.Should().BeApproximately(612D, 1D); 112 | page.Height.Should().BeApproximately(792D, 1D); 113 | page.Size.Width.Should().BeApproximately(612D, 1D); 114 | page.Size.Height.Should().BeApproximately(792D, 1D); 115 | page.Orientation.Should().Be(PageOrientations.Normal); 116 | page.HasTransparency.Should().BeFalse(); 117 | page.Label.Should().BeEquivalentTo(string.Empty); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /src/UnitTests/PdfLibCore.UnitTests/PdfLibCore.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 9 6 | 7 | 8 | 9 | AnyCPU 10 | 11 | 12 | 13 | AnyCPU 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Always 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/UnitTests/PdfLibCore.UnitTests/PdfPageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.IO; 4 | using FluentAssertions; 5 | using PdfLibCore.Enums; 6 | using Xunit; 7 | 8 | namespace PdfLibCore.UnitTests 9 | { 10 | [ExcludeFromCodeCoverage] 11 | public class PdfPageTests 12 | { 13 | private readonly string _path; 14 | 15 | public PdfPageTests() 16 | { 17 | _path = Path.Combine(Environment.CurrentDirectory, "Data", "test.pdf"); 18 | } 19 | 20 | [Fact] 21 | public void Add_New_Page_To_Collection_From_Different_pdf() 22 | { 23 | var srcDocument = new PdfDocument(_path); 24 | var pdfDocument = new PdfDocument(); 25 | pdfDocument.Pages.Should().HaveCount(0); 26 | pdfDocument.Pages.Add(srcDocument, 0).Should().BeTrue(); 27 | pdfDocument.Pages.Should().HaveCount(1); 28 | } 29 | 30 | [Fact] 31 | public void Add_New_Page_To_Collection() 32 | { 33 | var pdfDocument = new PdfDocument(); 34 | pdfDocument.Pages.Should().HaveCount(0); 35 | pdfDocument.Pages.Add(595D, 841D).Should().NotBeNull(); 36 | pdfDocument.Pages.Should().HaveCount(1); 37 | pdfDocument.Pages[0].Orientation.Should().Be(PageOrientations.Normal); 38 | pdfDocument.Pages[0].Orientation = PageOrientations.Rotated90CW; 39 | pdfDocument.Pages[0].Orientation.Should().Be(PageOrientations.Rotated90CW); 40 | } 41 | 42 | [Fact] 43 | public void Add_New_Page_To_Collection_And_Remove_It_Again() 44 | { 45 | var pdfDocument = new PdfDocument(); 46 | pdfDocument.Pages.Should().HaveCount(0); 47 | pdfDocument.Pages.Add(595D, 841D).Should().NotBeNull(); 48 | pdfDocument.Pages.Should().HaveCount(1); 49 | pdfDocument.Pages.RemoveAt(0); 50 | pdfDocument.Pages.Should().HaveCount(0); 51 | } 52 | 53 | [Fact] 54 | public void Remove_Non_Existing_Page_Results_In_ArgumentOutOfRange_Exception() 55 | { 56 | var pdfDocument = new PdfDocument(); 57 | pdfDocument.Pages.Should().HaveCount(0); 58 | pdfDocument.Pages.Add(595D, 841D).Should().NotBeNull(); 59 | pdfDocument.Pages.Should().HaveCount(1); 60 | Assert.Throws(() => pdfDocument.Pages.RemoveAt(1)); 61 | } 62 | 63 | [Fact] 64 | public void Add_New_Page_To_Collection_And_Remove_It_Again_By_Page() 65 | { 66 | var pdfDocument = new PdfDocument(); 67 | pdfDocument.Pages.Should().HaveCount(0); 68 | var page = pdfDocument.Pages.Add(595D, 841D); 69 | page.Should().NotBeNull(); 70 | pdfDocument.Pages.Should().HaveCount(1); 71 | pdfDocument.Pages.Remove(page); 72 | pdfDocument.Pages.Should().HaveCount(0); 73 | } 74 | 75 | [Fact] 76 | public void Try_Get_Non_Existing_Page_Results_In_ArgumentOutOfRange_Exception() 77 | { 78 | var pdfDocument = new PdfDocument(); 79 | pdfDocument.Pages.Should().HaveCount(0); 80 | pdfDocument.Pages.Add(595D, 841D).Should().NotBeNull(); 81 | pdfDocument.Pages.Should().HaveCount(1); 82 | Assert.Throws(() => pdfDocument.Pages[1]); 83 | } 84 | 85 | [Fact] 86 | public void Render_Page() 87 | { 88 | var pdfDocument = new PdfDocument(); 89 | pdfDocument.Pages.Should().HaveCount(0); 90 | var page = pdfDocument.Pages.Add(595D, 841D); 91 | pdfDocument.Pages.Should().HaveCount(1); 92 | page.Should().NotBeNull(); 93 | 94 | var bitmap = new PdfiumBitmap((int)page.Width, (int)page.Height, false); 95 | page.Render(bitmap); 96 | bitmap.Should().NotBeNull(); 97 | bitmap.Format.Should().Be(BitmapFormats.RGBx); 98 | bitmap.Width.Should().Be((int)page.Width); 99 | bitmap.Height.Should().Be((int)page.Height); 100 | } 101 | 102 | [Fact] 103 | public void DeviceToPage() 104 | { 105 | var pdfDocument = new PdfDocument(); 106 | pdfDocument.Pages.Should().HaveCount(0); 107 | var page = pdfDocument.Pages.Add(595D, 841D); 108 | pdfDocument.Pages.Should().HaveCount(1); 109 | page.Should().NotBeNull(); 110 | 111 | var result = page.DeviceToPage((0, 0, 10, 10), 0, 0); 112 | result.Should().NotBeNull(); 113 | result.X.Should().Be(0); 114 | result.Y.Should().BeApproximately(841D, 1D); 115 | } 116 | 117 | [Fact] 118 | public void PageToDevice() 119 | { 120 | var pdfDocument = new PdfDocument(); 121 | pdfDocument.Pages.Should().HaveCount(0); 122 | var page = pdfDocument.Pages.Add(595D, 841D); 123 | pdfDocument.Pages.Should().HaveCount(1); 124 | page.Should().NotBeNull(); 125 | 126 | var result = page.PageToDevice((0, 0, 10, 10), 0, 0); 127 | result.Should().NotBeNull(); 128 | result.X.Should().Be(0); 129 | result.Y.Should().Be(10); 130 | } 131 | 132 | [Fact] 133 | public void Flatten() 134 | { 135 | var pdfDocument = new PdfDocument(); 136 | pdfDocument.Pages.Should().HaveCount(0); 137 | var page = pdfDocument.Pages.Add(595D, 841D); 138 | pdfDocument.Pages.Should().HaveCount(1); 139 | page.Should().NotBeNull(); 140 | 141 | var result = page.Flatten(FlattenFlags.NormalDisplay); 142 | result.Should().Be(FlattenResults.NothingToDo); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /src/UnitTests/PdfLibCore.UnitTests/PdfPageToImageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.IO; 4 | using FluentAssertions; 5 | using PdfLibCore.Enums; 6 | using PdfLibCore.ImageSharp; 7 | using SixLabors.ImageSharp; 8 | using Xunit; 9 | 10 | namespace PdfLibCore.UnitTests 11 | { 12 | [ExcludeFromCodeCoverage] 13 | public class PdfPageToImageTests : IDisposable 14 | { 15 | private readonly PdfDocument _pdfDocument; 16 | 17 | public PdfPageToImageTests() 18 | { 19 | var path = Path.Combine(Environment.CurrentDirectory, "Data", "test.pdf"); 20 | _pdfDocument = new PdfDocument(path); 21 | _pdfDocument.Should().NotBeNull(); 22 | _pdfDocument.Pages.Count.Should().Be(1); 23 | } 24 | 25 | [Fact] 26 | public void Render_Page_To_PdfiumBitmap_With_Alpha() 27 | { 28 | var page = _pdfDocument.Pages[0]; 29 | page.Should().NotBeNull(); 30 | 31 | using var bitmap = new PdfiumBitmap((int)page.Width, (int)page.Height, true); 32 | page.Render(bitmap, PageOrientations.Normal, RenderingFlags.LcdText); 33 | bitmap.Format.Should().Be(BitmapFormats.RGBA); 34 | bitmap.Width.Should().Be((int)page.Width); 35 | bitmap.Height.Should().Be((int)page.Height); 36 | bitmap.Stride.Should().BeGreaterThanOrEqualTo(0); 37 | bitmap.Scan0.Should().BeGreaterThanOrEqualTo(0); 38 | bitmap.BytesPerPixel.Should().Be(4); 39 | bitmap.Dispose(); 40 | } 41 | 42 | [Fact] 43 | public void Render_Page_To_PdfiumBitmap_Without_Alpha() 44 | { 45 | var page = _pdfDocument.Pages[0]; 46 | page.Should().NotBeNull(); 47 | 48 | using var bitmap = new PdfiumBitmap((int)page.Width, (int)page.Height, false); 49 | page.Render(bitmap, PageOrientations.Normal, RenderingFlags.LcdText); 50 | bitmap.Format.Should().Be(BitmapFormats.RGBx); 51 | bitmap.Width.Should().Be((int)page.Width); 52 | bitmap.Height.Should().Be((int)page.Height); 53 | bitmap.Stride.Should().BeGreaterThanOrEqualTo(0); 54 | bitmap.Scan0.Should().BeGreaterThanOrEqualTo(0); 55 | bitmap.BytesPerPixel.Should().Be(4); 56 | bitmap.Dispose(); 57 | } 58 | 59 | [Fact] 60 | public void Get_BmpStream_From_PdfiumBitmap() 61 | { 62 | var page = _pdfDocument.Pages[0]; 63 | page.Should().NotBeNull(); 64 | 65 | using var bitmap = new PdfiumBitmap((int)page.Width, (int)page.Height, true); 66 | page.Render(bitmap, PageOrientations.Normal, RenderingFlags.LcdText); 67 | var stream = bitmap.AsBmpStream(); 68 | stream.Should().NotBeNull(); 69 | } 70 | [Fact] 71 | public void Get_PngStream_From_PdfiumBitmap() 72 | { 73 | var page = _pdfDocument.Pages[0]; 74 | page.Should().NotBeNull(); 75 | 76 | using var bitmap = new PdfiumBitmap((int)page.Width * 3, (int)page.Height * 3, true); 77 | page.Render(bitmap, PageOrientations.Normal, RenderingFlags.LcdText); 78 | var stream = bitmap.AsBmpStream(); 79 | stream.Should().NotBeNull(); 80 | 81 | var path = Path.Combine(Environment.CurrentDirectory, "Data", "test.png"); 82 | 83 | bitmap.AsImage().SaveAsPng(path); 84 | Image.Load(path).Height.Should().Be(792*3); 85 | } 86 | 87 | public void Dispose() 88 | { 89 | ((IDisposable)_pdfDocument)?.Dispose(); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /tools/build_nuget.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dotnet pack -c Release --version-suffix $1 -o ../../nuget ../src/PdfLibCore/ -------------------------------------------------------------------------------- /tools/get_pdfium.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # source of mac universal 3 | # https://github.com/bblanchon/pdfium-binaries/blob/master/mac_create_universal.sh 4 | 5 | set -e 6 | 7 | SRC_DIR="$PWD" 8 | STAGE_DIR="$PWD/mac" 9 | DEST_DIR="../src/PdfLibCore/runtimes" 10 | 11 | create_univ () { 12 | if [ -e "pdfium-mac-x64$1.tgz" ] && [ -e "pdfium-mac-arm64$1.tgz" ]; then 13 | echo "Extracting x64..." 14 | mkdir -p $STAGE_DIR/x64 15 | cd $STAGE_DIR/x64 16 | tar xf $SRC_DIR/pdfium-mac-x64$1.tgz 17 | 18 | echo "Extracting arm64..." 19 | mkdir -p $STAGE_DIR/arm64 20 | cd $STAGE_DIR/arm64 21 | tar xf $SRC_DIR/pdfium-mac-arm64$1.tgz 22 | 23 | echo "Creating universal..." 24 | mkdir -p $STAGE_DIR/univ 25 | cp -r $STAGE_DIR/x64/* $STAGE_DIR/univ 26 | lipo -create \ 27 | $STAGE_DIR/x64/lib/libpdfium.dylib \ 28 | $STAGE_DIR/arm64/lib/libpdfium.dylib \ 29 | -output $STAGE_DIR/univ/lib/libpdfium.dylib 30 | 31 | # echo "Creating target..." 32 | # cd $STAGE_DIR/univ 33 | # tar cf "$SRC_DIR/pdfium-mac-universal$1.tgz" -- * 34 | 35 | cd $SRC_DIR 36 | fi 37 | } 38 | 39 | wget https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F5268/pdfium-linux-x64.tgz 40 | wget https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F5268/pdfium-win-x64.tgz 41 | wget https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F5268/pdfium-mac-x64.tgz 42 | wget https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F5268/pdfium-mac-arm64.tgz 43 | 44 | mkdir linux 45 | mkdir windows 46 | mkdir mac 47 | 48 | tar -xvf pdfium-linux-x64.tgz -C linux 49 | tar -xvf pdfium-win-x64.tgz -C windows 50 | 51 | create_univ "" 52 | 53 | mkdir -p $DEST_DIR/linux-x64/native/ 54 | mkdir -p $DEST_DIR/mac-univ/native/ 55 | mkdir -p $DEST_DIR/win-x64/native/ 56 | 57 | cp linux/lib/libpdfium.so $DEST_DIR/linux-x64/native/pdfium.so 58 | cp linux/LICENSE $DEST_DIR/linux-x64/native/LICENSE 59 | 60 | cp $STAGE_DIR/univ/lib/libpdfium.dylib $DEST_DIR/mac-univ/native/pdfium.dylib 61 | cp $STAGE_DIR/univ/LICENSE $DEST_DIR/mac-univ/native/LICENSE 62 | 63 | cp windows/bin/pdfium.dll $DEST_DIR/win-x64/native/pdfium.dll 64 | cp windows/LICENSE $DEST_DIR/win-x64/native/LICENSE 65 | 66 | rm pdfium-linux-x64.tgz pdfium-win-x64.tgz pdfium-mac-x64.tgz pdfium-mac-arm64.tgz 67 | rm -rf linux windows mac --------------------------------------------------------------------------------