├── .gitignore ├── App.config ├── LICENSE ├── NativeMethods.Pdfium.cs ├── NativeMethods.cs ├── PdfBookmarkCollection.cs ├── PdfDocument.cs ├── PdfError.cs ├── PdfException.cs ├── PdfInformation.cs ├── PdfLibrary.cs ├── PdfMatches.cs ├── PdfPage.cs ├── PdfPageLink.cs ├── PdfPageLinks.cs ├── PdfPoint.cs ├── PdfRectangle.cs ├── PdfRenderFlags.cs ├── PdfRotation.cs ├── PdfTextSpan.cs ├── PdfiumLight.csproj ├── PdfiumLight.nuspec ├── PdfiumLight.sln ├── README.md ├── StreamExtensions.cs ├── StreamManager.cs └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/csharp 3 | 4 | ### Csharp ### 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Ll]og/ 30 | 31 | # Visual Studio 2015 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # .NET Core 50 | project.lock.json 51 | project.fragment.lock.json 52 | artifacts/ 53 | **/Properties/launchSettings.json 54 | 55 | *_i.c 56 | *_p.c 57 | *_i.h 58 | *.ilk 59 | *.meta 60 | *.obj 61 | *.pch 62 | *.pdb 63 | *.pgc 64 | *.pgd 65 | *.rsp 66 | *.sbr 67 | *.tlb 68 | *.tli 69 | *.tlh 70 | *.tmp 71 | *.tmp_proj 72 | *.log 73 | *.vspscc 74 | *.vssscc 75 | .builds 76 | *.pidb 77 | *.svclog 78 | *.scc 79 | 80 | # Chutzpah Test files 81 | _Chutzpah* 82 | 83 | # Visual C++ cache files 84 | ipch/ 85 | *.aps 86 | *.ncb 87 | *.opendb 88 | *.opensdf 89 | *.sdf 90 | *.cachefile 91 | *.VC.db 92 | *.VC.VC.opendb 93 | 94 | # Visual Studio profiler 95 | *.psess 96 | *.vsp 97 | *.vspx 98 | *.sap 99 | 100 | # TFS 2012 Local Workspace 101 | $tf/ 102 | 103 | # Guidance Automation Toolkit 104 | *.gpState 105 | 106 | # ReSharper is a .NET coding add-in 107 | _ReSharper*/ 108 | *.[Rr]e[Ss]harper 109 | *.DotSettings.user 110 | 111 | # JustCode is a .NET coding add-in 112 | .JustCode 113 | 114 | # TeamCity is a build add-in 115 | _TeamCity* 116 | 117 | # DotCover is a Code Coverage Tool 118 | *.dotCover 119 | 120 | # Visual Studio code coverage results 121 | *.coverage 122 | *.coveragexml 123 | 124 | # NCrunch 125 | _NCrunch_* 126 | .*crunch*.local.xml 127 | nCrunchTemp_* 128 | 129 | # MightyMoose 130 | *.mm.* 131 | AutoTest.Net/ 132 | 133 | # Web workbench (sass) 134 | .sass-cache/ 135 | 136 | # Installshield output folder 137 | [Ee]xpress/ 138 | 139 | # DocProject is a documentation generator add-in 140 | DocProject/buildhelp/ 141 | DocProject/Help/*.HxT 142 | DocProject/Help/*.HxC 143 | DocProject/Help/*.hhc 144 | DocProject/Help/*.hhk 145 | DocProject/Help/*.hhp 146 | DocProject/Help/Html2 147 | DocProject/Help/html 148 | 149 | # Click-Once directory 150 | publish/ 151 | 152 | # Publish Web Output 153 | *.[Pp]ublish.xml 154 | *.azurePubxml 155 | # TODO: Uncomment the next line to ignore your web deploy settings. 156 | # By default, sensitive information, such as encrypted password 157 | # should be stored in the .pubxml.user file. 158 | #*.pubxml 159 | *.pubxml.user 160 | *.publishproj 161 | 162 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 163 | # checkin your Azure Web App publish settings, but sensitive information contained 164 | # in these scripts will be unencrypted 165 | PublishScripts/ 166 | 167 | # NuGet Packages 168 | *.nupkg 169 | # The packages folder can be ignored because of Package Restore 170 | **/packages/* 171 | # except build/, which is used as an MSBuild target. 172 | !**/packages/build/ 173 | # Uncomment if necessary however generally it will be regenerated when needed 174 | #!**/packages/repositories.config 175 | # NuGet v3's project.json files produces more ignorable files 176 | *.nuget.props 177 | *.nuget.targets 178 | 179 | # Microsoft Azure Build Output 180 | csx/ 181 | *.build.csdef 182 | 183 | # Microsoft Azure Emulator 184 | ecf/ 185 | rcf/ 186 | 187 | # Windows Store app package directories and files 188 | AppPackages/ 189 | BundleArtifacts/ 190 | Package.StoreAssociation.xml 191 | _pkginfo.txt 192 | 193 | # Visual Studio cache files 194 | # files ending in .cache can be ignored 195 | *.[Cc]ache 196 | # but keep track of directories ending in .cache 197 | !*.[Cc]ache/ 198 | 199 | # Others 200 | ClientBin/ 201 | ~$* 202 | *~ 203 | *.dbmdl 204 | *.dbproj.schemaview 205 | *.jfm 206 | *.pfx 207 | *.publishsettings 208 | orleans.codegen.cs 209 | 210 | # Since there are multiple workflows, uncomment next line to ignore bower_components 211 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 212 | #bower_components/ 213 | 214 | # RIA/Silverlight projects 215 | Generated_Code/ 216 | 217 | # Backup & report files from converting an old project file 218 | # to a newer Visual Studio version. Backup files are not needed, 219 | # because we have git ;-) 220 | _UpgradeReport_Files/ 221 | Backup*/ 222 | UpgradeLog*.XML 223 | UpgradeLog*.htm 224 | 225 | # SQL Server files 226 | *.mdf 227 | *.ldf 228 | *.ndf 229 | 230 | # Business Intelligence projects 231 | *.rdl.data 232 | *.bim.layout 233 | *.bim_*.settings 234 | 235 | # Microsoft Fakes 236 | FakesAssemblies/ 237 | 238 | # GhostDoc plugin setting file 239 | *.GhostDoc.xml 240 | 241 | # Node.js Tools for Visual Studio 242 | .ntvs_analysis.dat 243 | node_modules/ 244 | 245 | # Typescript v1 declaration files 246 | typings/ 247 | 248 | # Visual Studio 6 build log 249 | *.plg 250 | 251 | # Visual Studio 6 workspace options file 252 | *.opt 253 | 254 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 255 | *.vbw 256 | 257 | # Visual Studio LightSwitch build output 258 | **/*.HTMLClient/GeneratedArtifacts 259 | **/*.DesktopClient/GeneratedArtifacts 260 | **/*.DesktopClient/ModelManifest.xml 261 | **/*.Server/GeneratedArtifacts 262 | **/*.Server/ModelManifest.xml 263 | _Pvt_Extensions 264 | 265 | # Paket dependency manager 266 | .paket/paket.exe 267 | paket-files/ 268 | 269 | # FAKE - F# Make 270 | .fake/ 271 | 272 | # JetBrains Rider 273 | .idea/ 274 | *.sln.iml 275 | 276 | # CodeRush 277 | .cr/ 278 | 279 | # Python Tools for Visual Studio (PTVS) 280 | __pycache__/ 281 | *.pyc 282 | 283 | # Cake - Uncomment if you are using it 284 | # tools/** 285 | # !tools/packages.config 286 | 287 | # Telerik's JustMock configuration file 288 | *.jmconfig 289 | 290 | # BizTalk build output 291 | *.btp.cs 292 | *.btm.cs 293 | *.odx.cs 294 | *.xsd.cs 295 | 296 | 297 | # End of https://www.gitignore.io/api/csharp -------------------------------------------------------------------------------- /App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /NativeMethods.Pdfium.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | #pragma warning disable 1591 7 | 8 | namespace PdfiumLight 9 | { 10 | public partial class NativeMethods 11 | { 12 | // Interned strings are cached over AppDomains. This means that when we 13 | // lock on this string, we actually lock over AppDomain's. The Pdfium 14 | // library is not thread safe, and this way of locking 15 | // guarantees that we don't access the Pdfium library from different 16 | // threads, even when there are multiple AppDomain's in play. 17 | private static readonly string LockString = string.Intern("e362349b-001d-4cb2-bf55-a71606a3e36f"); 18 | 19 | public static void FPDF_AddRef() 20 | { 21 | lock (LockString) 22 | { 23 | Imports.FPDF_AddRef(); 24 | } 25 | } 26 | 27 | public static void FPDF_Release() 28 | { 29 | lock (LockString) 30 | { 31 | Imports.FPDF_Release(); 32 | } 33 | } 34 | 35 | public static IntPtr FPDF_LoadMemDocument(SafeHandle data_buf, int size, string password) 36 | { 37 | lock (LockString) 38 | { 39 | return Imports.FPDF_LoadMemDocument(data_buf, size, password); 40 | } 41 | } 42 | 43 | public static IntPtr FPDF_LoadMemDocument(byte[] data_buf, int size, string password) 44 | { 45 | lock (LockString) 46 | { 47 | return Imports.FPDF_LoadMemDocument(data_buf, size, password); 48 | } 49 | } 50 | 51 | public static void FPDF_CloseDocument(IntPtr document) 52 | { 53 | lock (LockString) 54 | { 55 | Imports.FPDF_CloseDocument(document); 56 | } 57 | } 58 | 59 | public static int FPDF_GetPageCount(IntPtr document) 60 | { 61 | lock (LockString) 62 | { 63 | return Imports.FPDF_GetPageCount(document); 64 | } 65 | } 66 | 67 | public static uint FPDF_GetDocPermissions(IntPtr document) 68 | { 69 | lock (LockString) 70 | { 71 | return Imports.FPDF_GetDocPermissions(document); 72 | } 73 | } 74 | 75 | public static IntPtr FPDFDOC_InitFormFillEnvironment(IntPtr document, FPDF_FORMFILLINFO formInfo) 76 | { 77 | lock (LockString) 78 | { 79 | return Imports.FPDFDOC_InitFormFillEnvironment(document, formInfo); 80 | } 81 | } 82 | 83 | public static void FPDF_SetFormFieldHighlightColor(IntPtr hHandle, int fieldType, uint color) 84 | { 85 | lock (LockString) 86 | { 87 | Imports.FPDF_SetFormFieldHighlightColor(hHandle, fieldType, color); 88 | } 89 | } 90 | 91 | public static void FPDF_SetFormFieldHighlightAlpha(IntPtr hHandle, byte alpha) 92 | { 93 | lock (LockString) 94 | { 95 | Imports.FPDF_SetFormFieldHighlightAlpha(hHandle, alpha); 96 | } 97 | } 98 | 99 | public static void FORM_DoDocumentJSAction(IntPtr hHandle) 100 | { 101 | lock (LockString) 102 | { 103 | Imports.FORM_DoDocumentJSAction(hHandle); 104 | } 105 | } 106 | 107 | public static void FORM_DoDocumentOpenAction(IntPtr hHandle) 108 | { 109 | lock (LockString) 110 | { 111 | Imports.FORM_DoDocumentOpenAction(hHandle); 112 | } 113 | } 114 | 115 | public static void FPDFDOC_ExitFormFillEnvironment(IntPtr hHandle) 116 | { 117 | lock (LockString) 118 | { 119 | Imports.FPDFDOC_ExitFormFillEnvironment(hHandle); 120 | } 121 | } 122 | 123 | public static void FORM_DoDocumentAAction(IntPtr hHandle, FPDFDOC_AACTION aaType) 124 | { 125 | lock (LockString) 126 | { 127 | Imports.FORM_DoDocumentAAction(hHandle, aaType); 128 | } 129 | } 130 | 131 | public static IntPtr FPDF_LoadPage(IntPtr document, int page_index) 132 | { 133 | lock (LockString) 134 | { 135 | return Imports.FPDF_LoadPage(document, page_index); 136 | } 137 | } 138 | 139 | 140 | public static void FPDFPage_SetCropBox(IntPtr page_object, float left, float bottom, float right, float top) 141 | { 142 | lock (LockString) 143 | { 144 | Imports.FPDFPage_SetCropBox(page_object, left, bottom, right, top); 145 | } 146 | } 147 | 148 | // remove start 149 | public static IntPtr FPDFPageObj_NewPathObj() 150 | { 151 | lock (LockString) 152 | { 153 | return Imports.FPDFPageObj_NewPathObj(); 154 | } 155 | } 156 | 157 | public static IntPtr FPDFPathObj_GetPath(IntPtr path_object) 158 | { 159 | lock (LockString) 160 | { 161 | return Imports.FPDFPathObj_GetPath(path_object); 162 | } 163 | } 164 | 165 | 166 | public static void FPDFPathObj_InsertPoint(IntPtr path_object, double x, double y, int flag) 167 | { 168 | lock (LockString) 169 | { 170 | Imports.FPDFPathObj_InsertPoint(path_object, x, y, flag); 171 | } 172 | } 173 | 174 | public static void FPDFPageObj_AddClip(IntPtr page_object, IntPtr path_object, int type) 175 | { 176 | lock (LockString) 177 | { 178 | Imports.FPDFPageObj_AddClip(page_object, path_object, type); 179 | } 180 | } 181 | 182 | // remove end 183 | 184 | public static IntPtr FPDFText_LoadPage(IntPtr page) 185 | { 186 | lock (LockString) 187 | { 188 | return Imports.FPDFText_LoadPage(page); 189 | } 190 | } 191 | 192 | public static void FORM_OnAfterLoadPage(IntPtr page, IntPtr _form) 193 | { 194 | lock (LockString) 195 | { 196 | Imports.FORM_OnAfterLoadPage(page, _form); 197 | } 198 | } 199 | 200 | public static void FORM_DoPageAAction(IntPtr page, IntPtr _form, FPDFPAGE_AACTION fPDFPAGE_AACTION) 201 | { 202 | lock (LockString) 203 | { 204 | Imports.FORM_DoPageAAction(page, _form, fPDFPAGE_AACTION); 205 | } 206 | } 207 | 208 | public static double FPDF_GetPageWidth(IntPtr page) 209 | { 210 | lock (LockString) 211 | { 212 | return Imports.FPDF_GetPageWidth(page); 213 | } 214 | } 215 | 216 | public static double FPDF_GetPageHeight(IntPtr page) 217 | { 218 | lock (LockString) 219 | { 220 | return Imports.FPDF_GetPageHeight(page); 221 | } 222 | } 223 | 224 | public static void FORM_OnBeforeClosePage(IntPtr page, IntPtr _form) 225 | { 226 | lock (LockString) 227 | { 228 | Imports.FORM_OnBeforeClosePage(page, _form); 229 | } 230 | } 231 | 232 | public static void FPDFText_ClosePage(IntPtr text_page) 233 | { 234 | lock (LockString) 235 | { 236 | Imports.FPDFText_ClosePage(text_page); 237 | } 238 | } 239 | 240 | public static void FPDF_ClosePage(IntPtr page) 241 | { 242 | lock (LockString) 243 | { 244 | Imports.FPDF_ClosePage(page); 245 | } 246 | } 247 | 248 | public static void FPDF_RenderPage(IntPtr dc, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, FPDF flags) 249 | { 250 | lock (LockString) 251 | { 252 | Imports.FPDF_RenderPage(dc, page, start_x, start_y, size_x, size_y, rotate, flags); 253 | } 254 | } 255 | 256 | public static void FPDF_RenderPageBitmap(IntPtr bitmapHandle, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, FPDF flags) 257 | { 258 | lock (LockString) 259 | { 260 | Imports.FPDF_RenderPageBitmap(bitmapHandle, page, start_x, start_y, size_x, size_y, rotate, flags); 261 | } 262 | } 263 | 264 | public static int FPDF_GetPageSizeByIndex(IntPtr document, int page_index, out double width, out double height) 265 | { 266 | lock (LockString) 267 | { 268 | return Imports.FPDF_GetPageSizeByIndex(document, page_index, out width, out height); 269 | } 270 | } 271 | 272 | public static IntPtr FPDFBitmap_CreateEx(int width, int height, int format, IntPtr first_scan, int stride) 273 | { 274 | lock (LockString) 275 | { 276 | return Imports.FPDFBitmap_CreateEx(width, height, format, first_scan, stride); 277 | } 278 | } 279 | 280 | public static void FPDFBitmap_FillRect(IntPtr bitmapHandle, int left, int top, int width, int height, uint color) 281 | { 282 | lock (LockString) 283 | { 284 | Imports.FPDFBitmap_FillRect(bitmapHandle, left, top, width, height, color); 285 | } 286 | } 287 | 288 | public static IntPtr FPDFBitmap_Destroy(IntPtr bitmapHandle) 289 | { 290 | lock (LockString) 291 | { 292 | return Imports.FPDFBitmap_Destroy(bitmapHandle); 293 | } 294 | } 295 | 296 | public static IntPtr FPDFText_FindStart(IntPtr page, byte[] findWhat, FPDF_SEARCH_FLAGS flags, int start_index) 297 | { 298 | lock (LockString) 299 | { 300 | return Imports.FPDFText_FindStart(page, findWhat, flags, start_index); 301 | } 302 | } 303 | 304 | public static int FPDFText_GetSchResultIndex(IntPtr handle) 305 | { 306 | lock (LockString) 307 | { 308 | return Imports.FPDFText_GetSchResultIndex(handle); 309 | } 310 | } 311 | 312 | public static int FPDFText_GetSchCount(IntPtr handle) 313 | { 314 | lock (LockString) 315 | { 316 | return Imports.FPDFText_GetSchCount(handle); 317 | } 318 | } 319 | 320 | public static int FPDFText_GetText(IntPtr page, int start_index, int count, byte[] result) 321 | { 322 | lock (LockString) 323 | { 324 | return Imports.FPDFText_GetText(page, start_index, count, result); 325 | } 326 | } 327 | 328 | public static void FPDFText_GetCharBox(IntPtr page, int index, out double left, out double right, out double bottom, out double top) 329 | { 330 | lock (LockString) 331 | { 332 | Imports.FPDFText_GetCharBox(page, index, out left, out right, out bottom, out top); 333 | } 334 | } 335 | 336 | public static int FPDFText_CountChars(IntPtr page) 337 | { 338 | lock (LockString) 339 | { 340 | return Imports.FPDFText_CountChars(page); 341 | } 342 | } 343 | 344 | public static bool FPDFText_FindNext(IntPtr handle) 345 | { 346 | lock (LockString) 347 | { 348 | return Imports.FPDFText_FindNext(handle); 349 | } 350 | } 351 | 352 | public static void FPDFText_FindClose(IntPtr handle) 353 | { 354 | lock (LockString) 355 | { 356 | Imports.FPDFText_FindClose(handle); 357 | } 358 | } 359 | 360 | public static bool FPDFLink_Enumerate(IntPtr page, ref int startPos, out IntPtr linkAnnot) 361 | { 362 | lock (LockString) 363 | { 364 | return Imports.FPDFLink_Enumerate(page, ref startPos, out linkAnnot); 365 | } 366 | } 367 | 368 | public static IntPtr FPDFLink_GetDest(IntPtr document, IntPtr link) 369 | { 370 | lock (LockString) 371 | { 372 | return Imports.FPDFLink_GetDest(document, link); 373 | } 374 | } 375 | 376 | public static uint FPDFDest_GetPageIndex(IntPtr document, IntPtr dest) 377 | { 378 | lock (LockString) 379 | { 380 | return Imports.FPDFDest_GetPageIndex(document, dest); 381 | } 382 | } 383 | 384 | public static bool FPDFLink_GetAnnotRect(IntPtr linkAnnot, FS_RECTF rect) 385 | { 386 | lock (LockString) 387 | { 388 | return Imports.FPDFLink_GetAnnotRect(linkAnnot, rect); 389 | } 390 | } 391 | 392 | public static void FPDF_DeviceToPage(IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, int device_x, int device_y, out double page_x, out double page_y) 393 | { 394 | lock (LockString) 395 | { 396 | Imports.FPDF_DeviceToPage(page, start_x, start_y, size_x, size_y, rotate, device_x, device_y, out page_x, out page_y); 397 | } 398 | } 399 | 400 | public static void FPDF_PageToDevice(IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, double page_x, double page_y, out int device_x, out int device_y) 401 | { 402 | lock (LockString) 403 | { 404 | Imports.FPDF_PageToDevice(page, start_x, start_y, size_x, size_y, rotate, page_x, page_y, out device_x, out device_y); 405 | } 406 | } 407 | 408 | public static IntPtr FPDFLink_GetAction(IntPtr link) 409 | { 410 | lock (LockString) 411 | { 412 | return Imports.FPDFLink_GetAction(link); 413 | } 414 | } 415 | 416 | public static uint FPDFAction_GetURIPath(IntPtr document, IntPtr action, StringBuilder buffer, uint buflen) 417 | { 418 | lock (LockString) 419 | { 420 | return Imports.FPDFAction_GetURIPath(document, action, buffer, buflen); 421 | } 422 | } 423 | 424 | public static IntPtr FPDF_BookmarkGetFirstChild(IntPtr document, IntPtr bookmark) 425 | { 426 | lock (LockString) 427 | { 428 | return Imports.FPDFBookmark_GetFirstChild(document, bookmark); 429 | } 430 | } 431 | 432 | public static IntPtr FPDF_BookmarkGetNextSibling(IntPtr document, IntPtr bookmark) 433 | { 434 | lock (LockString) 435 | return Imports.FPDFBookmark_GetNextSibling(document, bookmark); 436 | } 437 | 438 | public static uint FPDF_BookmarkGetTitle(IntPtr bookmark, byte[] buffer, uint buflen) 439 | { 440 | lock (LockString) 441 | return Imports.FPDFBookmark_GetTitle(bookmark, buffer, buflen); 442 | } 443 | 444 | public static IntPtr FPDF_BookmarkGetAction(IntPtr bookmark) 445 | { 446 | lock (LockString) 447 | return Imports.FPDFBookmark_GetAction(bookmark); 448 | } 449 | 450 | public static IntPtr FPDF_BookmarkGetDest(IntPtr document, IntPtr bookmark) 451 | { 452 | lock (LockString) 453 | return Imports.FPDFBookmark_GetDest(document, bookmark); 454 | } 455 | 456 | public static uint FPDF_ActionGetType(IntPtr action) 457 | { 458 | lock (LockString) 459 | return Imports.FPDFAction_GetType(action); 460 | } 461 | 462 | /// 463 | /// Opens a document using a .NET Stream. Allows opening huge 464 | /// PDFs without loading them into memory first. 465 | /// 466 | /// The input Stream. Don't dispose prior to closing the pdf. 467 | /// Password, if the PDF is protected. Can be null. 468 | /// Retrieves an IntPtr to the COM object for the Stream. The caller must release this with Marshal.Release prior to Disposing the Stream. 469 | /// An IntPtr to the FPDF_DOCUMENT object. 470 | public static IntPtr FPDF_LoadCustomDocument(Stream input, string password, int id) 471 | { 472 | var getBlock = Marshal.GetFunctionPointerForDelegate(_getBlockDelegate); 473 | 474 | var access = new FPDF_FILEACCESS 475 | { 476 | m_FileLen = (uint)input.Length, 477 | m_GetBlock = getBlock, 478 | m_Param = (IntPtr)id 479 | }; 480 | 481 | lock (LockString) 482 | { 483 | return Imports.FPDF_LoadCustomDocument(access, password); 484 | } 485 | } 486 | 487 | public static FPDF_ERR FPDF_GetLastError() 488 | { 489 | lock (LockString) 490 | { 491 | return (FPDF_ERR)Imports.FPDF_GetLastError(); 492 | } 493 | } 494 | 495 | public static uint FPDF_GetMetaText(IntPtr document, string tag, byte[] buffer, uint buflen) 496 | { 497 | lock (LockString) 498 | { 499 | return Imports.FPDF_GetMetaText(document, tag, buffer, buflen); 500 | } 501 | } 502 | 503 | #region Save / Edit Methods 504 | 505 | public static void FPDFPage_SetRotation(IntPtr page, PdfRotation rotation) 506 | { 507 | lock (LockString) 508 | { 509 | Imports.FPDFPage_SetRotation(page, (int)rotation); 510 | } 511 | } 512 | 513 | public static bool FPDFPage_GenerateContent(IntPtr page) 514 | { 515 | lock (LockString) 516 | { 517 | return Imports.FPDFPage_GenerateContent(page); 518 | } 519 | } 520 | 521 | public static void FPDFPage_Delete(IntPtr doc, int page) 522 | { 523 | lock (LockString) 524 | { 525 | Imports.FPDFPage_Delete(doc, page); 526 | } 527 | } 528 | 529 | public static bool FPDF_SaveAsCopy(IntPtr doc, Stream output, FPDF_SAVE_FLAGS flags) 530 | { 531 | int id = StreamManager.Register(output); 532 | 533 | try 534 | { 535 | var write = new FPDF_FILEWRITE 536 | { 537 | stream = (IntPtr)id, 538 | version = 1, 539 | WriteBlock = Marshal.GetFunctionPointerForDelegate(_saveBlockDelegate) 540 | }; 541 | 542 | lock (LockString) 543 | { 544 | return Imports.FPDF_SaveAsCopy(doc, write, flags); 545 | } 546 | } 547 | finally 548 | { 549 | StreamManager.Unregister(id); 550 | } 551 | } 552 | 553 | public static bool FPDF_SaveWithVersion(IntPtr doc, Stream output, FPDF_SAVE_FLAGS flags, int fileVersion) 554 | { 555 | int id = StreamManager.Register(output); 556 | 557 | try 558 | { 559 | var write = new FPDF_FILEWRITE 560 | { 561 | stream = (IntPtr)id, 562 | version = 1, 563 | WriteBlock = Marshal.GetFunctionPointerForDelegate(_saveBlockDelegate) 564 | }; 565 | 566 | lock (LockString) 567 | { 568 | return Imports.FPDF_SaveWithVersion(doc, write, flags, fileVersion); 569 | } 570 | } 571 | finally 572 | { 573 | StreamManager.Unregister(id); 574 | } 575 | } 576 | 577 | public static int FPDFText_GetCharIndexAtPos(IntPtr page, double x, double y, double x_tolerance, double y_tolerance) 578 | { 579 | lock (LockString) 580 | { 581 | return Imports.FPDFText_GetCharIndexAtPos(page, x, y, x_tolerance, y_tolerance); 582 | } 583 | } 584 | 585 | public static int FPDFText_CountRects(IntPtr text_page, int start_index, int count) 586 | { 587 | lock (LockString) 588 | { 589 | return Imports.FPDFText_CountRects(text_page, start_index, count); 590 | } 591 | } 592 | 593 | public static void FPDFText_GetRect(IntPtr text_page, int rect_index, out double left, out double top, out double right, out double bottom) 594 | { 595 | lock (LockString) 596 | { 597 | Imports.FPDFText_GetRect(text_page, rect_index, out left, out top, out right, out bottom); 598 | } 599 | } 600 | 601 | 602 | // Parameters: 603 | // text_page - Handle to a text page information structure. 604 | // left - Left boundary. 605 | // top - Top boundary. 606 | // right - Right boundary. 607 | // bottom - Bottom boundary. 608 | // buffer - A unicode buffer. 609 | // buflen - Number of characters (not bytes) for the buffer, excluding an additional terminator. 610 | // Return Value: 611 | // If buffer is NULL or buflen is zero, return number of characters (not bytes) needed, 612 | // otherwise, return number of characters copied into the buffer. 613 | public static uint FPDFText_GetBoundedText(IntPtr text_page, double left, double top, double right, double bottom, byte[] buffer, uint buflen) 614 | { 615 | lock (LockString) 616 | { 617 | return Imports.FPDFText_GetBoundedText(text_page, left, top, right, bottom, buffer, buflen); 618 | } 619 | } 620 | 621 | 622 | public static void FPDF_FFLDraw(IntPtr form, IntPtr bitmap, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, FPDF flags) 623 | { 624 | lock (LockString) 625 | { 626 | Imports.FPDF_FFLDraw(form, bitmap, page, start_x, start_y, size_x, size_y, rotate, flags); 627 | } 628 | } 629 | #endregion 630 | 631 | #region Custom Load/Save Logic 632 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 633 | private delegate int FPDF_GetBlockDelegate(IntPtr param, uint position, IntPtr buffer, uint size); 634 | 635 | private static readonly FPDF_GetBlockDelegate _getBlockDelegate = FPDF_GetBlock; 636 | 637 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 638 | private delegate int FPDF_SaveBlockDelegate(IntPtr fileWrite, IntPtr data, uint size); 639 | 640 | private static readonly FPDF_SaveBlockDelegate _saveBlockDelegate = FPDF_SaveBlock; 641 | 642 | private static int FPDF_GetBlock(IntPtr param, uint position, IntPtr buffer, uint size) 643 | { 644 | var stream = StreamManager.Get((int)param); 645 | if (stream is null) 646 | return 0; 647 | byte[] managedBuffer = new byte[size]; 648 | 649 | stream.Position = position; 650 | int read = stream.Read(managedBuffer, 0, (int)size); 651 | if (read != size) 652 | return 0; 653 | 654 | Marshal.Copy(managedBuffer, 0, buffer, (int)size); 655 | return 1; 656 | } 657 | 658 | private static int FPDF_SaveBlock(IntPtr fileWrite, IntPtr data, uint size) 659 | { 660 | var write = new FPDF_FILEWRITE(); 661 | Marshal.PtrToStructure(fileWrite, write); 662 | 663 | var stream = StreamManager.Get((int)write.stream); 664 | if (stream is null) 665 | return 0; 666 | 667 | byte[] buffer = new byte[size]; 668 | Marshal.Copy(data, buffer, 0, (int)size); 669 | 670 | try 671 | { 672 | stream.Write(buffer, 0, (int)size); 673 | return (int)size; 674 | } 675 | catch 676 | { 677 | return 0; 678 | } 679 | } 680 | #endregion 681 | 682 | private static class Imports 683 | { 684 | [DllImport("pdfium.dll")] 685 | public static extern void FPDF_AddRef(); 686 | 687 | [DllImport("pdfium.dll")] 688 | public static extern void FPDF_Release(); 689 | 690 | [DllImport("pdfium.dll", CharSet = CharSet.Ansi)] 691 | public static extern IntPtr FPDF_LoadCustomDocument([MarshalAs(UnmanagedType.LPStruct)]FPDF_FILEACCESS access, string password); 692 | 693 | [DllImport("pdfium.dll", CharSet = CharSet.Ansi)] 694 | public static extern IntPtr FPDF_LoadMemDocument(SafeHandle data_buf, int size, string password); 695 | 696 | [DllImport("pdfium.dll", CharSet = CharSet.Ansi)] 697 | public static extern IntPtr FPDF_LoadMemDocument(byte[] data_buf, int size, string password); 698 | 699 | [DllImport("pdfium.dll")] 700 | public static extern void FPDF_CloseDocument(IntPtr document); 701 | 702 | [DllImport("pdfium.dll")] 703 | public static extern int FPDF_GetPageCount(IntPtr document); 704 | 705 | [DllImport("pdfium.dll")] 706 | public static extern uint FPDF_GetDocPermissions(IntPtr document); 707 | 708 | [DllImport("pdfium.dll")] 709 | public static extern IntPtr FPDFDOC_InitFormFillEnvironment(IntPtr document, FPDF_FORMFILLINFO formInfo); 710 | 711 | [DllImport("pdfium.dll")] 712 | public static extern void FPDF_SetFormFieldHighlightColor(IntPtr hHandle, int fieldType, uint color); 713 | 714 | [DllImport("pdfium.dll")] 715 | public static extern void FPDF_SetFormFieldHighlightAlpha(IntPtr hHandle, byte alpha); 716 | 717 | [DllImport("pdfium.dll")] 718 | public static extern void FORM_DoDocumentJSAction(IntPtr hHandle); 719 | 720 | [DllImport("pdfium.dll")] 721 | public static extern void FORM_DoDocumentOpenAction(IntPtr hHandle); 722 | 723 | [DllImport("pdfium.dll")] 724 | public static extern void FPDFDOC_ExitFormFillEnvironment(IntPtr hHandle); 725 | 726 | [DllImport("pdfium.dll")] 727 | public static extern void FORM_DoDocumentAAction(IntPtr hHandle, FPDFDOC_AACTION aaType); 728 | 729 | [DllImport("pdfium.dll")] 730 | public static extern IntPtr FPDF_LoadPage(IntPtr document, int page_index); 731 | 732 | [DllImport("pdfium.dll")] 733 | public static extern IntPtr FPDFPage_SetCropBox(IntPtr page_object, float left, float bottom, float right, float top); 734 | 735 | // remove start 736 | [DllImport("pdfium.dll")] 737 | public static extern IntPtr FPDFPathObj_GetPath(IntPtr path_object); 738 | 739 | [DllImport("pdfium.dll")] 740 | public static extern IntPtr FPDFPageObj_NewPathObj(); 741 | 742 | 743 | [DllImport("pdfium.dll")] 744 | public static extern void FPDFPathObj_InsertPoint(IntPtr path_object, double x, double y, int flag); 745 | 746 | [DllImport("pdfium.dll")] 747 | public static extern IntPtr FPDFPageObj_AddClip(IntPtr page_object, IntPtr path_object, int type); 748 | // remove end 749 | 750 | [DllImport("pdfium.dll")] 751 | public static extern IntPtr FPDFText_LoadPage(IntPtr page); 752 | 753 | [DllImport("pdfium.dll")] 754 | public static extern void FORM_OnAfterLoadPage(IntPtr page, IntPtr _form); 755 | 756 | [DllImport("pdfium.dll")] 757 | public static extern void FORM_DoPageAAction(IntPtr page, IntPtr _form, FPDFPAGE_AACTION fPDFPAGE_AACTION); 758 | 759 | [DllImport("pdfium.dll")] 760 | public static extern double FPDF_GetPageWidth(IntPtr page); 761 | 762 | [DllImport("pdfium.dll")] 763 | public static extern double FPDF_GetPageHeight(IntPtr page); 764 | 765 | [DllImport("pdfium.dll")] 766 | public static extern void FORM_OnBeforeClosePage(IntPtr page, IntPtr _form); 767 | 768 | [DllImport("pdfium.dll")] 769 | public static extern void FPDFText_ClosePage(IntPtr text_page); 770 | 771 | [DllImport("pdfium.dll")] 772 | public static extern int FPDFText_GetCharIndexAtPos(IntPtr text_page, double x, double y, double x_tolerance, double y_tolerance); 773 | 774 | [DllImport("pdfium.dll")] 775 | public static extern void FPDFText_GetRect(IntPtr text_page, int rect_index, out double left, out double top, out double right, out double bottom); 776 | 777 | [DllImport("pdfium.dll")] 778 | public static extern uint FPDFText_GetBoundedText(IntPtr text_page, double left, double top, double right, double bottom, byte[] buffer, uint buflen); 779 | 780 | [DllImport("pdfium.dll")] 781 | public static extern int FPDFText_CountRects(IntPtr text_page, int start_index, int count); 782 | 783 | [DllImport("pdfium.dll")] 784 | public static extern void FPDF_ClosePage(IntPtr page); 785 | 786 | [DllImport("pdfium.dll")] 787 | public static extern void FPDF_RenderPage(IntPtr dc, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, FPDF flags); 788 | 789 | [DllImport("pdfium.dll")] 790 | public static extern void FPDF_RenderPageBitmap(IntPtr bitmapHandle, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, FPDF flags); 791 | 792 | [DllImport("pdfium.dll")] 793 | public static extern int FPDF_GetPageSizeByIndex(IntPtr document, int page_index, out double width, out double height); 794 | 795 | [DllImport("pdfium.dll")] 796 | public static extern IntPtr FPDFBitmap_CreateEx(int width, int height, int format, IntPtr first_scan, int stride); 797 | 798 | [DllImport("pdfium.dll")] 799 | public static extern void FPDFBitmap_FillRect(IntPtr bitmapHandle, int left, int top, int width, int height, uint color); 800 | 801 | [DllImport("pdfium.dll")] 802 | public static extern IntPtr FPDFBitmap_Destroy(IntPtr bitmapHandle); 803 | 804 | [DllImport("pdfium.dll")] 805 | public static extern IntPtr FPDFText_FindStart(IntPtr page, byte[] findWhat, FPDF_SEARCH_FLAGS flags, int start_index); 806 | 807 | [DllImport("pdfium.dll")] 808 | public static extern int FPDFText_GetSchResultIndex(IntPtr handle); 809 | 810 | [DllImport("pdfium.dll")] 811 | public static extern int FPDFText_GetSchCount(IntPtr handle); 812 | 813 | [DllImport("pdfium.dll")] 814 | public static extern int FPDFText_GetText(IntPtr page, int start_index, int count, byte[] result); 815 | 816 | [DllImport("pdfium.dll")] 817 | public static extern void FPDFText_GetCharBox(IntPtr page, int index, out double left, out double right, out double bottom, out double top); 818 | 819 | [DllImport("pdfium.dll")] 820 | public static extern int FPDFText_CountChars(IntPtr page); 821 | 822 | [DllImport("pdfium.dll")] 823 | public static extern bool FPDFText_FindNext(IntPtr handle); 824 | 825 | [DllImport("pdfium.dll")] 826 | public static extern void FPDFText_FindClose(IntPtr handle); 827 | 828 | [DllImport("pdfium.dll")] 829 | public static extern bool FPDFLink_Enumerate(IntPtr page, ref int startPos, out IntPtr linkAnnot); 830 | 831 | [DllImport("pdfium.dll")] 832 | public static extern IntPtr FPDFLink_GetDest(IntPtr document, IntPtr link); 833 | 834 | [DllImport("pdfium.dll")] 835 | public static extern uint FPDFDest_GetPageIndex(IntPtr document, IntPtr dest); 836 | 837 | [DllImport("pdfium.dll")] 838 | public static extern bool FPDFLink_GetAnnotRect(IntPtr linkAnnot, FS_RECTF rect); 839 | 840 | [DllImport("pdfium.dll")] 841 | public static extern void FPDF_DeviceToPage(IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, int device_x, int device_y, out double page_x, out double page_y); 842 | 843 | [DllImport("pdfium.dll")] 844 | public static extern void FPDF_PageToDevice(IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, double page_x, double page_y, out int device_x, out int device_y); 845 | 846 | [DllImport("pdfium.dll")] 847 | public static extern IntPtr FPDFLink_GetAction(IntPtr link); 848 | 849 | [DllImport("pdfium.dll")] 850 | public static extern uint FPDFAction_GetURIPath(IntPtr document, IntPtr action, StringBuilder buffer, uint buflen); 851 | 852 | [DllImport("pdfium.dll")] 853 | public static extern IntPtr FPDFBookmark_GetFirstChild(IntPtr document, IntPtr bookmark); 854 | 855 | [DllImport("pdfium.dll")] 856 | public static extern IntPtr FPDFBookmark_GetNextSibling(IntPtr document, IntPtr bookmark); 857 | 858 | [DllImport("pdfium.dll")] 859 | public static extern uint FPDFBookmark_GetTitle(IntPtr bookmark, byte[] buffer, uint buflen); 860 | 861 | [DllImport("pdfium.dll")] 862 | public static extern IntPtr FPDFBookmark_GetAction(IntPtr bookmark); 863 | 864 | [DllImport("pdfium.dll")] 865 | public static extern IntPtr FPDFBookmark_GetDest(IntPtr document, IntPtr bookmark); 866 | 867 | [DllImport("pdfium.dll")] 868 | public static extern uint FPDFAction_GetType(IntPtr action); 869 | 870 | [DllImport("pdfium.dll")] 871 | public static extern uint FPDF_GetLastError(); 872 | 873 | [DllImport("pdfium.dll")] 874 | public static extern uint FPDF_GetMetaText(IntPtr document, string tag, byte[] buffer, uint buflen); 875 | 876 | #region Save/Edit APIs 877 | 878 | [DllImport("pdfium.dll")] 879 | public static extern bool FPDF_ImportPages(IntPtr destDoc, IntPtr srcDoc, [MarshalAs(UnmanagedType.LPStr)]string pageRange, int index); 880 | 881 | [DllImport("pdfium.dll")] 882 | public static extern bool FPDF_SaveAsCopy(IntPtr doc, 883 | [MarshalAs(UnmanagedType.LPStruct)]FPDF_FILEWRITE writer, 884 | [MarshalAs(UnmanagedType.I4)]FPDF_SAVE_FLAGS flag); 885 | 886 | [DllImport("pdfium.dll")] 887 | public static extern bool FPDF_SaveWithVersion(IntPtr doc, 888 | [MarshalAs(UnmanagedType.LPStruct)]FPDF_FILEWRITE writer, 889 | [MarshalAs(UnmanagedType.I4)]FPDF_SAVE_FLAGS flags, 890 | int fileVersion); 891 | 892 | [DllImport("pdfium.dll")] 893 | public static extern IntPtr FPDFPage_New(IntPtr doc, int index, double width, double height); 894 | 895 | [DllImport("pdfium.dll")] 896 | public static extern void FPDFPage_Delete(IntPtr doc, int index); 897 | 898 | [DllImport("pdfium.dll")] 899 | public static extern int FPDFPage_GetRotation(IntPtr page); 900 | 901 | [DllImport("pdfium.dll")] 902 | public static extern void FPDFPage_SetRotation(IntPtr page, int rotate); 903 | 904 | [DllImport("pdfium.dll")] 905 | public static extern IntPtr FPDF_CreateNewDocument(); 906 | 907 | [DllImport("pdfium.dll")] 908 | public static extern IntPtr FPDFPageObj_NewImgeObj(IntPtr document); 909 | 910 | [DllImport("pdfium.dll")] 911 | public static extern bool FPDFImageObj_SetBitmap(IntPtr pages, int count, IntPtr imageObject, IntPtr bitmap); 912 | 913 | [DllImport("pdfium.dll")] 914 | public static extern void FPDFPageObj_Transform(IntPtr page, double a, double b, double c, double d, double e, double f); 915 | 916 | [DllImport("pdfium.dll")] 917 | public static extern void FPDFPage_InsertObject(IntPtr page, IntPtr pageObject); 918 | 919 | [DllImport("pdfium.dll")] 920 | public static extern bool FPDFPage_GenerateContent(IntPtr page); 921 | 922 | [DllImport("pdfium.dll")] 923 | public static extern void FPDF_FFLDraw(IntPtr form, IntPtr bitmap, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, FPDF flags); 924 | 925 | #endregion 926 | } 927 | 928 | [StructLayout(LayoutKind.Sequential)] 929 | public class FPDF_FORMFILLINFO 930 | { 931 | public int version; 932 | 933 | private IntPtr Release; 934 | 935 | private IntPtr FFI_Invalidate; 936 | 937 | private IntPtr FFI_OutputSelectedRect; 938 | 939 | private IntPtr FFI_SetCursor; 940 | 941 | private IntPtr FFI_SetTimer; 942 | 943 | private IntPtr FFI_KillTimer; 944 | 945 | private IntPtr FFI_GetLocalTime; 946 | 947 | private IntPtr FFI_OnChange; 948 | 949 | private IntPtr FFI_GetPage; 950 | 951 | private IntPtr FFI_GetCurrentPage; 952 | 953 | private IntPtr FFI_GetRotation; 954 | 955 | private IntPtr FFI_ExecuteNamedAction; 956 | 957 | private IntPtr FFI_SetTextFieldFocus; 958 | 959 | private IntPtr FFI_DoURIAction; 960 | 961 | private IntPtr FFI_DoGoToAction; 962 | 963 | private IntPtr m_pJsPlatform; 964 | 965 | // XFA support i.e. version 2 966 | 967 | private IntPtr FFI_DisplayCaret; 968 | 969 | private IntPtr FFI_GetCurrentPageIndex; 970 | 971 | private IntPtr FFI_SetCurrentPage; 972 | 973 | private IntPtr FFI_GotoURL; 974 | 975 | private IntPtr FFI_GetPageViewRect; 976 | 977 | private IntPtr FFI_PageEvent; 978 | 979 | private IntPtr FFI_PopupMenu; 980 | 981 | private IntPtr FFI_OpenFile; 982 | 983 | private IntPtr FFI_EmailTo; 984 | 985 | private IntPtr FFI_UploadTo; 986 | 987 | private IntPtr FFI_GetPlatform; 988 | 989 | private IntPtr FFI_GetLanguage; 990 | 991 | private IntPtr FFI_DownloadFromURL; 992 | 993 | private IntPtr FFI_PostRequestURL; 994 | 995 | private IntPtr FFI_PutRequestURL; 996 | } 997 | 998 | public enum FPDF_UNSP 999 | { 1000 | DOC_XFAFORM = 1, 1001 | DOC_PORTABLECOLLECTION = 2, 1002 | DOC_ATTACHMENT = 3, 1003 | DOC_SECURITY = 4, 1004 | DOC_SHAREDREVIEW = 5, 1005 | DOC_SHAREDFORM_ACROBAT = 6, 1006 | DOC_SHAREDFORM_FILESYSTEM = 7, 1007 | DOC_SHAREDFORM_EMAIL = 8, 1008 | ANNOT_3DANNOT = 11, 1009 | ANNOT_MOVIE = 12, 1010 | ANNOT_SOUND = 13, 1011 | ANNOT_SCREEN_MEDIA = 14, 1012 | ANNOT_SCREEN_RICHMEDIA = 15, 1013 | ANNOT_ATTACHMENT = 16, 1014 | ANNOT_SIG = 17 1015 | } 1016 | 1017 | public enum FPDFDOC_AACTION 1018 | { 1019 | WC = 0x10, 1020 | WS = 0x11, 1021 | DS = 0x12, 1022 | WP = 0x13, 1023 | DP = 0x14 1024 | } 1025 | 1026 | public enum FPDFPAGE_AACTION 1027 | { 1028 | OPEN = 0, 1029 | CLOSE = 1 1030 | } 1031 | 1032 | [Flags] 1033 | public enum FPDF 1034 | { 1035 | ANNOT = 0x01, 1036 | LCD_TEXT = 0x02, 1037 | NO_NATIVETEXT = 0x04, 1038 | GRAYSCALE = 0x08, 1039 | DEBUG_INFO = 0x80, 1040 | NO_CATCH = 0x100, 1041 | RENDER_LIMITEDIMAGECACHE = 0x200, 1042 | RENDER_FORCEHALFTONE = 0x400, 1043 | PRINTING = 0x800, 1044 | REVERSE_BYTE_ORDER = 0x10 1045 | } 1046 | 1047 | [Flags] 1048 | public enum FPDF_SEARCH_FLAGS 1049 | { 1050 | FPDF_MATCHCASE = 1, 1051 | FPDF_MATCHWHOLEWORD = 2 1052 | } 1053 | 1054 | [StructLayout(LayoutKind.Sequential)] 1055 | public class FS_RECTF 1056 | { 1057 | public float left; 1058 | public float top; 1059 | public float right; 1060 | public float bottom; 1061 | } 1062 | 1063 | public enum FPDF_ERR : uint 1064 | { 1065 | FPDF_ERR_SUCCESS = 0, // No error. 1066 | FPDF_ERR_UNKNOWN = 1, // Unknown error. 1067 | FPDF_ERR_FILE = 2, // File not found or could not be opened. 1068 | FPDF_ERR_FORMAT = 3, // File not in PDF format or corrupted. 1069 | FPDF_ERR_PASSWORD = 4, // Password required or incorrect password. 1070 | FPDF_ERR_SECURITY = 5, // Unsupported security scheme. 1071 | FPDF_ERR_PAGE = 6 // Page not found or content error. 1072 | } 1073 | 1074 | #region Save/Edit Structs and Flags 1075 | [Flags] 1076 | public enum FPDF_SAVE_FLAGS 1077 | { 1078 | FPDF_INCREMENTAL = 1, 1079 | FPDF_NO_INCREMENTAL = 2, 1080 | FPDF_REMOVE_SECURITY = 3 1081 | } 1082 | 1083 | [StructLayout(LayoutKind.Sequential)] 1084 | public class FPDF_FILEACCESS 1085 | { 1086 | public uint m_FileLen; 1087 | public IntPtr m_GetBlock; 1088 | public IntPtr m_Param; 1089 | } 1090 | 1091 | [StructLayout(LayoutKind.Sequential)] 1092 | public class FPDF_FILEWRITE 1093 | { 1094 | public int version; 1095 | public IntPtr WriteBlock; 1096 | public IntPtr stream; 1097 | } 1098 | #endregion 1099 | } 1100 | } 1101 | -------------------------------------------------------------------------------- /NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.Runtime.ConstrainedExecution; 5 | using System.Runtime.InteropServices; 6 | using System.Security.Permissions; 7 | using Microsoft.Win32.SafeHandles; 8 | 9 | namespace PdfiumLight 10 | { 11 | public static partial class NativeMethods 12 | { 13 | static NativeMethods() 14 | { 15 | // Load the platform dependent Pdfium.dll if it exists. 16 | 17 | if (!TryLoadNativeLibrary(AppDomain.CurrentDomain.RelativeSearchPath)) 18 | { 19 | TryLoadNativeLibrary(Path.GetDirectoryName(typeof(NativeMethods).Assembly.Location)); 20 | } 21 | } 22 | 23 | private static bool TryLoadNativeLibrary(string path) 24 | { 25 | if (path is null) 26 | return false; 27 | 28 | path = Path.Combine(path, IntPtr.Size == 4 ? "x86" : "x64"); 29 | path = Path.Combine(path, "Pdfium.dll"); 30 | 31 | return File.Exists(path) && LoadLibrary(path) != IntPtr.Zero; 32 | } 33 | 34 | [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] 35 | private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPTStr)] string lpFileName); 36 | 37 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 38 | public static extern MemoryMappedHandle CreateFileMapping(SafeHandle hFile, IntPtr lpFileMappingAttributes, FileMapProtection flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, [MarshalAs(UnmanagedType.LPTStr)] string lpName); 39 | 40 | [DllImport("kernel32.dll", SetLastError = true)] 41 | [return: MarshalAs(UnmanagedType.Bool)] 42 | public static extern bool CloseHandle(IntPtr hObject); 43 | 44 | [Flags] 45 | public enum FileMapProtection : uint 46 | { 47 | PageReadonly = 0x02, 48 | PageReadWrite = 0x04, 49 | PageWriteCopy = 0x08, 50 | PageExecuteRead = 0x20, 51 | PageExecuteReadWrite = 0x40, 52 | SectionCommit = 0x8000000, 53 | SectionImage = 0x1000000, 54 | SectionNoCache = 0x10000000, 55 | SectionReserve = 0x4000000, 56 | } 57 | 58 | [DllImport("kernel32.dll", SetLastError = true)] 59 | public static extern MappedViewHandle MapViewOfFile(SafeHandle hFileMappingObject, FileMapAccess dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); 60 | 61 | [Flags] 62 | public enum FileMapAccess : uint 63 | { 64 | FileMapCopy = 0x0001, 65 | FileMapWrite = 0x0002, 66 | FileMapRead = 0x0004, 67 | FileMapAllAccess = 0x001f, 68 | FileMapExecute = 0x0020, 69 | } 70 | 71 | [DllImport("kernel32.dll", SetLastError = true)] 72 | public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress); 73 | 74 | [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)] 75 | [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] 76 | public class MemoryMappedHandle : SafeHandleZeroOrMinusOneIsInvalid 77 | { 78 | public MemoryMappedHandle() 79 | : base(true) 80 | { 81 | } 82 | 83 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 84 | protected override bool ReleaseHandle() 85 | { 86 | return CloseHandle(handle); 87 | } 88 | } 89 | 90 | public class MappedViewHandle : SafeHandleZeroOrMinusOneIsInvalid 91 | { 92 | public MappedViewHandle() 93 | : base(true) 94 | { 95 | } 96 | 97 | protected override bool ReleaseHandle() 98 | { 99 | return UnmapViewOfFile(handle); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /PdfBookmarkCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace PdfiumLight 4 | { 5 | public class PdfBookmark 6 | { 7 | public PdfBookmark() 8 | { 9 | Children = new PdfBookmarkCollection(); 10 | } 11 | 12 | public string Title { get; set; } 13 | 14 | public int PageIndex { get; set; } 15 | 16 | public PdfBookmarkCollection Children { get; set; } 17 | 18 | public override string ToString() => Title; 19 | } 20 | 21 | public class PdfBookmarkCollection : Collection 22 | { 23 | } 24 | } -------------------------------------------------------------------------------- /PdfDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | 9 | namespace PdfiumLight 10 | { 11 | /// 12 | /// Represents a PDF document. 13 | /// 14 | public class PdfDocument : IDisposable 15 | { 16 | private static readonly Encoding FPDFEncoding = new UnicodeEncoding(false, false, false); 17 | 18 | private IntPtr _document; 19 | private IntPtr _form; 20 | private bool _disposed; 21 | private NativeMethods.FPDF_FORMFILLINFO _formCallbacks; 22 | private GCHandle _formCallbacksHandle; 23 | private int _id; 24 | private Stream _stream; 25 | 26 | /// 27 | /// Initializes a new instance of PdfDocument 28 | /// 29 | /// The path to the PDF-file 30 | /// Password to decrypt PDF 31 | public PdfDocument(string path, string password = null) 32 | { 33 | if (path is null) 34 | throw new ArgumentNullException(nameof(path)); 35 | 36 | LoadFile(File.OpenRead(path), password); 37 | } 38 | 39 | /// 40 | /// Initializes a new instance of PdfDocument a .NET Stream. Allows opening huge PDFs without loading them into memory first. 41 | /// 42 | /// The input Stream. Don't dispose prior to closing the PDF. 43 | /// Password to decrypt PDF 44 | public PdfDocument(Stream stream, string password = null) 45 | { 46 | LoadFile(stream, password); 47 | } 48 | 49 | /// 50 | /// The Bookmarks of this PDF document. 51 | /// 52 | public PdfBookmarkCollection Bookmarks { get; private set; } 53 | 54 | private void LoadFile(Stream stream, string password) 55 | { 56 | PdfLibrary.EnsureLoaded(); 57 | 58 | _stream = stream ?? throw new ArgumentNullException(nameof(stream)); 59 | _id = StreamManager.Register(stream); 60 | 61 | var document = NativeMethods.FPDF_LoadCustomDocument(stream, password, _id); 62 | 63 | if (document == IntPtr.Zero) 64 | { 65 | throw new PdfException((PdfError)NativeMethods.FPDF_GetLastError()); 66 | } 67 | 68 | LoadDocument(document); 69 | } 70 | 71 | /// 72 | /// This method returns the dimension of the pages in this document without loading them into memory first 73 | /// 74 | /// The List of page dimensions 75 | public SizeF[] GetPageSize() 76 | { 77 | if (_disposed) 78 | { 79 | throw new ObjectDisposedException(nameof(PdfDocument)); 80 | } 81 | 82 | int pageCount = NativeMethods.FPDF_GetPageCount(_document); 83 | 84 | var result = new SizeF[pageCount]; 85 | 86 | for (int i = 0; i < pageCount; i++) 87 | { 88 | result[i] = GetPageSize(i); 89 | } 90 | 91 | return result; 92 | } 93 | 94 | /// 95 | /// This method returns the dimension of a page in this document without loading it into memory first 96 | /// 97 | /// The page number of the page you want to retrice the dimensions for 98 | /// Dimesions as SizeF 99 | public SizeF GetPageSize(int pageNumber) 100 | { 101 | NativeMethods.FPDF_GetPageSizeByIndex(_document, pageNumber, out double width, out double height); 102 | 103 | return new SizeF((float)width, (float)height); 104 | } 105 | 106 | public void Save(Stream stream) 107 | { 108 | NativeMethods.FPDF_SaveAsCopy(_document, stream, NativeMethods.FPDF_SAVE_FLAGS.FPDF_NO_INCREMENTAL); 109 | } 110 | 111 | protected void LoadDocument(IntPtr document) 112 | { 113 | _document = document; 114 | 115 | NativeMethods.FPDF_GetDocPermissions(_document); 116 | 117 | _formCallbacks = new NativeMethods.FPDF_FORMFILLINFO(); 118 | _formCallbacksHandle = GCHandle.Alloc(_formCallbacks, GCHandleType.Pinned); 119 | 120 | // Depending on whether XFA support is built into the PDFium library, the version 121 | // needs to be 1 or 2. We don't really care, so we just try one or the other. 122 | 123 | for (int i = 1; i <= 2; i++) 124 | { 125 | _formCallbacks.version = i; 126 | 127 | _form = NativeMethods.FPDFDOC_InitFormFillEnvironment(_document, _formCallbacks); 128 | if (_form != IntPtr.Zero) 129 | { 130 | break; 131 | } 132 | } 133 | 134 | NativeMethods.FPDF_SetFormFieldHighlightColor(_form, 0, 0xFFE4DD); 135 | NativeMethods.FPDF_SetFormFieldHighlightAlpha(_form, 100); 136 | 137 | NativeMethods.FORM_DoDocumentJSAction(_form); 138 | NativeMethods.FORM_DoDocumentOpenAction(_form); 139 | 140 | 141 | Bookmarks = LoadBookmarks(NativeMethods.FPDF_BookmarkGetFirstChild(document, IntPtr.Zero)); 142 | } 143 | 144 | private PdfBookmarkCollection LoadBookmarks(IntPtr bookmark) 145 | { 146 | var result = new PdfBookmarkCollection(); 147 | if (bookmark == IntPtr.Zero) 148 | return result; 149 | 150 | result.Add(LoadBookmark(bookmark)); 151 | 152 | while ((bookmark = NativeMethods.FPDF_BookmarkGetNextSibling(_document, bookmark)) != IntPtr.Zero) 153 | { 154 | result.Add(LoadBookmark(bookmark)); 155 | } 156 | 157 | return result; 158 | } 159 | 160 | private PdfBookmark LoadBookmark(IntPtr bookmark) 161 | { 162 | var pdfbookmark = new PdfBookmark 163 | { 164 | Title = GetBookmarkTitle(bookmark), 165 | PageIndex = (int)GetBookmarkPageIndex(bookmark) 166 | }; 167 | 168 | // Action = NativeMethods.FPDF_BookmarkGetAction(_bookmark); 169 | // if (Action != IntPtr.Zero) 170 | // ActionType = NativeMethods.FPDF_ActionGetType(Action); 171 | 172 | var child = NativeMethods.FPDF_BookmarkGetFirstChild(_document, bookmark); 173 | 174 | if (child != IntPtr.Zero) 175 | { 176 | pdfbookmark.Children = LoadBookmarks(child); 177 | } 178 | 179 | return pdfbookmark; 180 | } 181 | 182 | private string GetBookmarkTitle(IntPtr bookmark) 183 | { 184 | uint length = NativeMethods.FPDF_BookmarkGetTitle(bookmark, null, 0); 185 | byte[] buffer = new byte[length]; 186 | NativeMethods.FPDF_BookmarkGetTitle(bookmark, buffer, length); 187 | 188 | string result = Encoding.Unicode.GetString(buffer); 189 | 190 | if (result.Length > 0 && result[result.Length - 1] == 0) 191 | { 192 | result = result.Substring(0, result.Length - 1); 193 | } 194 | 195 | return result; 196 | } 197 | 198 | private uint GetBookmarkPageIndex(IntPtr bookmark) 199 | { 200 | IntPtr dest = NativeMethods.FPDF_BookmarkGetDest(_document, bookmark); 201 | 202 | return (dest != IntPtr.Zero) 203 | ? NativeMethods.FPDFDest_GetPageIndex(_document, dest) 204 | : 0u; 205 | } 206 | 207 | /// 208 | /// This method will load and return the sepecific page 209 | /// 210 | /// The null-based index of the page 211 | /// A new PdfPage 212 | public PdfPage GetPage(int page) => new PdfPage(_document, _form, page); 213 | 214 | /// 215 | /// Returns the page count of the document. This will call a native Pdfium function. 216 | /// 217 | /// 218 | public int PageCount() => NativeMethods.FPDF_GetPageCount(_document); 219 | 220 | /// 221 | /// Dtailed meta informations from the PDF document 222 | /// 223 | /// A PdfInformation containing the metadata 224 | public PdfInformation GetInformation() 225 | { 226 | return new PdfInformation 227 | { 228 | Creator = GetMetaText("Creator"), 229 | Title = GetMetaText("Title"), 230 | Author = GetMetaText("Author"), 231 | Subject = GetMetaText("Subject"), 232 | Keywords = GetMetaText("Keywords"), 233 | Producer = GetMetaText("Producer"), 234 | CreationDate = GetMetaTextAsDate("CreationDate"), 235 | ModificationDate = GetMetaTextAsDate("ModDate") 236 | }; 237 | } 238 | 239 | private string GetMetaText(string tag) 240 | { 241 | // Length includes a trailing \0. 242 | 243 | uint length = NativeMethods.FPDF_GetMetaText(_document, tag, null, 0); 244 | if (length <= 2) 245 | return string.Empty; 246 | 247 | byte[] buffer = new byte[length]; 248 | NativeMethods.FPDF_GetMetaText(_document, tag, buffer, length); 249 | 250 | return Encoding.Unicode.GetString(buffer, 0, (int)(length - 2)); 251 | } 252 | 253 | public DateTime? GetMetaTextAsDate(string tag) 254 | { 255 | string dt = GetMetaText(tag); 256 | 257 | if (string.IsNullOrEmpty(dt)) 258 | return null; 259 | 260 | Regex dtRegex = 261 | new Regex( 262 | @"(?:D:)(?\d\d\d\d)(?\d\d)(?\d\d)(?\d\d)(?\d\d)(?\d\d)(?[+-zZ])?(?\d\d)?'?(?\d\d)?'?"); 263 | 264 | Match match = dtRegex.Match(dt); 265 | 266 | if (match.Success) 267 | { 268 | var year = match.Groups["year"].Value; 269 | var month = match.Groups["month"].Value; 270 | var day = match.Groups["day"].Value; 271 | var hour = match.Groups["hour"].Value; 272 | var minute = match.Groups["minute"].Value; 273 | var second = match.Groups["second"].Value; 274 | var tzOffset = match.Groups["tz_offset"]?.Value; 275 | var tzHour = match.Groups["tz_hour"]?.Value; 276 | var tzMinute = match.Groups["tz_minute"]?.Value; 277 | 278 | string formattedDate = $"{year}-{month}-{day}T{hour}:{minute}:{second}.0000000"; 279 | 280 | if (tzOffset != null && tzOffset.Length == 1) 281 | { 282 | switch (tzOffset[0]) 283 | { 284 | case 'Z': 285 | case 'z': 286 | formattedDate += "+0"; 287 | break; 288 | case '+': 289 | case '-': 290 | formattedDate += $"{tzOffset}{tzHour}:{tzMinute}"; 291 | break; 292 | } 293 | } 294 | 295 | try 296 | { 297 | return DateTime.Parse(formattedDate); 298 | } 299 | catch (FormatException) 300 | { 301 | return null; 302 | } 303 | } 304 | 305 | return null; 306 | } 307 | 308 | public void Dispose() 309 | { 310 | Dispose(true); 311 | 312 | GC.SuppressFinalize(this); 313 | } 314 | 315 | private NativeMethods.FPDF FlagsToFPDFFlags(PdfRenderFlags flags) 316 | { 317 | return (NativeMethods.FPDF)(flags & ~(PdfRenderFlags.Transparent | PdfRenderFlags.CorrectFromDpi)); 318 | } 319 | 320 | protected virtual void Dispose(bool disposing) 321 | { 322 | if (!_disposed) 323 | { 324 | StreamManager.Unregister(_id); 325 | 326 | if (_form != IntPtr.Zero) 327 | { 328 | NativeMethods.FORM_DoDocumentAAction(_form, NativeMethods.FPDFDOC_AACTION.WC); 329 | NativeMethods.FPDFDOC_ExitFormFillEnvironment(_form); 330 | _form = IntPtr.Zero; 331 | } 332 | 333 | if (_document != IntPtr.Zero) 334 | { 335 | NativeMethods.FPDF_CloseDocument(_document); 336 | _document = IntPtr.Zero; 337 | } 338 | 339 | if (_formCallbacksHandle.IsAllocated) 340 | _formCallbacksHandle.Free(); 341 | 342 | if (_stream != null) 343 | { 344 | _stream.Dispose(); 345 | _stream = null; 346 | } 347 | 348 | _disposed = true; 349 | } 350 | } 351 | } 352 | } -------------------------------------------------------------------------------- /PdfError.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable 1591 2 | 3 | namespace PdfiumLight 4 | { 5 | public enum PdfError 6 | { 7 | Success = (int)NativeMethods.FPDF_ERR.FPDF_ERR_SUCCESS, 8 | Unknown = (int)NativeMethods.FPDF_ERR.FPDF_ERR_UNKNOWN, 9 | CannotOpenFile = (int)NativeMethods.FPDF_ERR.FPDF_ERR_FILE, 10 | InvalidFormat = (int)NativeMethods.FPDF_ERR.FPDF_ERR_FORMAT, 11 | PasswordProtected = (int)NativeMethods.FPDF_ERR.FPDF_ERR_PASSWORD, 12 | UnsupportedSecurityScheme = (int)NativeMethods.FPDF_ERR.FPDF_ERR_SECURITY, 13 | PageNotFound = (int)NativeMethods.FPDF_ERR.FPDF_ERR_PAGE 14 | } 15 | } -------------------------------------------------------------------------------- /PdfException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | #pragma warning disable 1591 5 | 6 | namespace PdfiumLight 7 | { 8 | public class PdfException : Exception 9 | { 10 | public PdfError Error { get; private set; } 11 | 12 | public PdfException() 13 | { 14 | } 15 | 16 | public PdfException(PdfError error) 17 | : this(GetMessage(error)) 18 | { 19 | Error = error; 20 | } 21 | 22 | private static string GetMessage(PdfError error) 23 | { 24 | switch (error) 25 | { 26 | case PdfError.Success: 27 | return "No error"; 28 | case PdfError.CannotOpenFile: 29 | return "File not found or could not be opened"; 30 | case PdfError.InvalidFormat: 31 | return "File not in PDF format or corrupted"; 32 | case PdfError.PasswordProtected: 33 | return "Password required or incorrect password"; 34 | case PdfError.UnsupportedSecurityScheme: 35 | return "Unsupported security scheme"; 36 | case PdfError.PageNotFound: 37 | return "Page not found or content error"; 38 | default: 39 | return "Unknown error"; 40 | } 41 | } 42 | 43 | public PdfException(string message) 44 | : base(message) 45 | { 46 | } 47 | 48 | public PdfException(string message, Exception innerException) 49 | : base(message, innerException) 50 | { 51 | } 52 | 53 | protected PdfException(SerializationInfo info, StreamingContext context) 54 | : base(info, context) 55 | { 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /PdfInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PdfiumLight 4 | { 5 | /// 6 | /// Contains text from metadata of the document. 7 | /// 8 | public class PdfInformation 9 | { 10 | public string Author { get; set; } 11 | 12 | public string Creator { get; set; } 13 | 14 | public DateTime? CreationDate { get; set; } 15 | 16 | public string Keywords { get; set; } 17 | 18 | public DateTime? ModificationDate { get; set; } 19 | 20 | public string Producer { get; set; } 21 | 22 | public string Subject { get; set; } 23 | 24 | public string Title { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /PdfLibrary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PdfiumLight 4 | { 5 | internal class PdfLibrary : IDisposable 6 | { 7 | private static readonly object _syncRoot = new object(); 8 | private static PdfLibrary _library; 9 | 10 | public static void EnsureLoaded() 11 | { 12 | lock (_syncRoot) 13 | { 14 | if (_library is null) 15 | _library = new PdfLibrary(); 16 | } 17 | } 18 | 19 | private bool _disposed; 20 | 21 | private PdfLibrary() 22 | { 23 | NativeMethods.FPDF_AddRef(); 24 | } 25 | 26 | ~PdfLibrary() 27 | { 28 | Dispose(false); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | Dispose(true); 34 | 35 | GC.SuppressFinalize(this); 36 | } 37 | 38 | private void Dispose(bool disposing) 39 | { 40 | if (!_disposed) 41 | { 42 | NativeMethods.FPDF_Release(); 43 | 44 | _disposed = true; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /PdfMatches.cs: -------------------------------------------------------------------------------- 1 | //using System; 2 | //using System.Collections.Generic; 3 | //using System.Collections.ObjectModel; 4 | //using System.Text; 5 | 6 | //#pragma warning disable 1591 7 | 8 | //namespace PdfiumLight 9 | //{ 10 | // public class PdfMatches 11 | // { 12 | // public int StartPage { get; private set; } 13 | 14 | // public int EndPage { get; private set; } 15 | 16 | // public IList Items { get; private set; } 17 | 18 | // public PdfMatches(int startPage, int endPage, IList matches) 19 | // { 20 | // if (matches is null) 21 | // throw new ArgumentNullException("matches"); 22 | 23 | // StartPage = startPage; 24 | // EndPage = endPage; 25 | // Items = new ReadOnlyCollection(matches); 26 | // } 27 | // } 28 | //} 29 | -------------------------------------------------------------------------------- /PdfPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.Text; 6 | 7 | namespace PdfiumLight 8 | { 9 | /// 10 | /// Represents a page of a PDF document. 11 | /// 12 | public class PdfPage : IDisposable 13 | { 14 | private static readonly Encoding FPDFEncoding = new UnicodeEncoding(false, false, false); 15 | 16 | private readonly IntPtr _form; 17 | private bool _disposed; 18 | 19 | /// 20 | /// Handle to the page. 21 | /// 22 | public IntPtr Page { get; private set; } 23 | 24 | /// 25 | /// Handle to the text page 26 | /// 27 | public IntPtr TextPage { get; private set; } 28 | 29 | /// 30 | /// Width of the page in pt 31 | /// 32 | public double Width { get; private set; } 33 | 34 | /// 35 | /// Height of th page in pt 36 | /// 37 | public double Height { get; private set; } 38 | 39 | /// 40 | /// The index og this page in the document 41 | /// 42 | public int PageNumber { get; private set; } 43 | 44 | /// 45 | /// Initializes a new instance of PdfPage 46 | /// 47 | /// The PDF document 48 | /// 49 | /// Number of this page in the document 50 | public PdfPage(IntPtr document, IntPtr form, int pageNumber) 51 | { 52 | _form = form; 53 | 54 | PageNumber = pageNumber; 55 | 56 | Page = NativeMethods.FPDF_LoadPage(document, pageNumber); 57 | TextPage = NativeMethods.FPDFText_LoadPage(Page); 58 | NativeMethods.FORM_OnAfterLoadPage(Page, form); 59 | NativeMethods.FORM_DoPageAAction(Page, form, NativeMethods.FPDFPAGE_AACTION.OPEN); 60 | 61 | Width = NativeMethods.FPDF_GetPageWidth(Page); 62 | Height = NativeMethods.FPDF_GetPageHeight(Page); 63 | } 64 | 65 | /// 66 | /// Dispose 67 | /// 68 | public void Dispose() 69 | { 70 | if (!_disposed) 71 | { 72 | NativeMethods.FORM_DoPageAAction(Page, _form, NativeMethods.FPDFPAGE_AACTION.CLOSE); 73 | NativeMethods.FORM_OnBeforeClosePage(Page, _form); 74 | NativeMethods.FPDFText_ClosePage(TextPage); 75 | NativeMethods.FPDF_ClosePage(Page); 76 | 77 | _disposed = true; 78 | } 79 | } 80 | 81 | private RectangleF GetBounds(int index) 82 | { 83 | NativeMethods.FPDFText_GetCharBox( 84 | Page, 85 | index, 86 | out double left, 87 | out double right, 88 | out double bottom, 89 | out double top 90 | ); 91 | 92 | return new RectangleF( 93 | (float)left, 94 | (float)top, 95 | (float)(right - left), 96 | (float)(bottom - top) 97 | ); 98 | } 99 | 100 | private bool RenderPDFPageToDC(IntPtr dc, int dpiX, int dpiY, int boundsOriginX, int boundsOriginY, int boundsWidth, int boundsHeight, NativeMethods.FPDF flags) 101 | { 102 | if (_disposed) 103 | { 104 | throw new ObjectDisposedException(nameof(PdfPage)); 105 | } 106 | 107 | NativeMethods.FPDF_RenderPage(dc, Page, boundsOriginX, boundsOriginY, boundsWidth, boundsHeight, 0, flags); 108 | 109 | return true; 110 | } 111 | 112 | /// 113 | /// Renders the page. 114 | /// 115 | /// 116 | /// The full width of the rendered image in px or percentage (if dpiX and dpiY are specified). 117 | /// If 0, width will be calculated from height using the correct apsect ratio. 118 | /// Height and width can not be both set to zero. 119 | /// 120 | /// 121 | /// The full wiheightth of the rendered image in px or percentage (if dpiX and dpiY are specified). 122 | /// If 0, height will be calculated from height using the correct apsect ratio. 123 | /// Height and width can not be both set to zero. 124 | /// 125 | /// X value of the start point of the clipping area 126 | /// Y value of the start point of the clipping area 127 | /// Width of the clip area 128 | /// Height of the clip area 129 | /// DPI to render page. If set, width and height will accept percentage. 130 | /// DPI to render page. If set, width and height will accept percentage. 131 | /// Specify rotation. 132 | /// Specify flags. 133 | /// Image from the page. 134 | public Image Render(int width, int height, int clipX, int clipY, int clipWidth, int clipHeight, float dpiX, float dpiY, PdfRotation rotate, PdfRenderFlags flags) 135 | { 136 | if (_disposed) 137 | { 138 | throw new ObjectDisposedException(nameof(PdfPage)); 139 | } 140 | 141 | if (height == 0 && width != 0) 142 | { 143 | height = width * (int)(Height / Width); 144 | } 145 | else if (height != 0 && width == 0) 146 | { 147 | width = height * (int)(Width / Height); 148 | } 149 | else if (height == 0 && width == 0) 150 | { 151 | throw new ArgumentException(); 152 | } 153 | 154 | if (dpiX != 0 && dpiY != 0) 155 | { 156 | clipWidth = (int)(clipWidth / 100f * width / 100f * Width * 0.013888888888889 * dpiX); 157 | clipHeight = (int)(clipHeight / 100f * height / 100f * Height * 0.013888888888889 * dpiY); 158 | width = (int)(width / 100f * Width * 0.013888888888889 * dpiX); 159 | height = (int)(height / 100f * Height * 0.013888888888889 * dpiY); 160 | } 161 | 162 | var bitmap = new Bitmap(clipWidth, clipHeight, PixelFormat.Format32bppArgb); 163 | 164 | var data = bitmap.LockBits(new Rectangle(0, 0, clipWidth, clipHeight), ImageLockMode.ReadWrite, bitmap.PixelFormat); 165 | 166 | try 167 | { 168 | var handle = NativeMethods.FPDFBitmap_CreateEx(clipWidth, clipHeight, 4, data.Scan0, clipWidth * 4); 169 | 170 | try 171 | { 172 | uint background = (flags & PdfRenderFlags.Transparent) == 0 ? 0xFFFFFFFF : 0x00FFFFFF; 173 | 174 | NativeMethods.FPDFBitmap_FillRect(handle, 0, 0, clipWidth, clipHeight, background); 175 | 176 | bool success = RenderPDFPageToBitmap( 177 | handle, 178 | (int)dpiX, (int)dpiY, -clipX, -clipY, width, height, 179 | (int)rotate, 180 | FlagsToFPDFFlags(flags), 181 | (flags & PdfRenderFlags.Annotations) != 0 182 | ); 183 | 184 | if (!success) 185 | throw new Exception(); 186 | } 187 | finally 188 | { 189 | NativeMethods.FPDFBitmap_Destroy(handle); 190 | } 191 | 192 | } 193 | finally 194 | { 195 | bitmap.UnlockBits(data); 196 | } 197 | 198 | return bitmap; 199 | } 200 | 201 | /// 202 | /// Renders the page. 203 | /// 204 | /// 205 | /// The full width of the rendered image in px. 206 | /// If 0, width will be calculated from height using the correct apsect ratio. 207 | /// Height and width can not be both set to zero. 208 | /// 209 | /// 210 | /// The full wiheightth of the rendered image in px . 211 | /// If 0, height will be calculated from height using the correct apsect ratio. 212 | /// Height and width can not be both set to zero. 213 | /// 214 | /// Specify rotation. 215 | /// Specify flags. 216 | /// Image from the page. 217 | public Image Render(int width, int height, PdfRotation rotate, PdfRenderFlags flags) 218 | { 219 | if (height == 0 && width != 0) height = (int)((float)width * (Height / Width)); 220 | else if (height != 0 && width == 0) width = (int)((float)height * (int)(Width / Height)); 221 | else if (height == 0 && width == 0) throw new ArgumentException(); 222 | return Render(width, height, 0, 0, width, height, 0, 0, rotate, flags); 223 | } 224 | 225 | /// 226 | /// Renders the page. 227 | /// 228 | /// 229 | /// The full width of the rendered image in px. 230 | /// If 0, width will be calculated from height using the correct apsect ratio. 231 | /// Height and width can not be both set to zero. 232 | /// 233 | /// 234 | /// The full wiheightth of the rendered image in px . 235 | /// If 0, height will be calculated from height using the correct apsect ratio. 236 | /// Height and width can not be both set to zero. 237 | /// 238 | /// Image from the page. 239 | public Image Render(int width, int height) 240 | { 241 | if (height == 0 && width != 0) height = (int)((float)width * (Height / Width)); 242 | else if (height != 0 && width == 0) width = (int)((float)height * (int)(Width / Height)); 243 | else if (height == 0 && width == 0) throw new ArgumentException(); 244 | return Render(width, height, 0, 0, width, height, 0, 0, PdfRotation.Rotate0, PdfRenderFlags.None); 245 | } 246 | 247 | /// 248 | /// Renders the page. 249 | /// 250 | /// 251 | /// The full width of the rendered image in px or percentage (if dpiX and dpiY are specified). 252 | /// If 0, width will be calculated from height using the correct apsect ratio. 253 | /// Height and width can not be both set to zero. 254 | /// 255 | /// 256 | /// The full wiheightth of the rendered image in px or percentage (if dpiX and dpiY are specified). 257 | /// If 0, height will be calculated from height using the correct apsect ratio. 258 | /// Height and width can not be both set to zero. 259 | /// 260 | /// DPI to render page. If set, width and height will accept percentage. 261 | /// DPI to render page. If set, width and height will accept percentage. 262 | /// Specify rotation. 263 | /// Specify flags. 264 | /// Image from the page. 265 | public Image Render(int width, int height, float dpiX, float dpiY, PdfRotation rotate, PdfRenderFlags flags) 266 | { 267 | return Render(width, height, 0, 0, width, height, dpiX, dpiY, rotate, flags); 268 | } 269 | 270 | /// 271 | /// Renders the page. 272 | /// 273 | /// 274 | /// The full width of the rendered image in px. 275 | /// If 0, width will be calculated from height using the correct apsect ratio. 276 | /// Height and width can not be both set to zero. 277 | /// 278 | /// 279 | /// The full wiheightth of the rendered image in px. 280 | /// If 0, height will be calculated from height using the correct apsect ratio. 281 | /// Height and width can not be both set to zero. 282 | /// 283 | /// X value of the start point of the clipping area 284 | /// Y value of the start point of the clipping area 285 | /// Width of the clip area 286 | /// Height of the clip area 287 | /// Specify rotation. 288 | /// Specify flags. 289 | /// Image from the page. 290 | public Image Render(int width, int height, int clipX, int clipY, int clipWidth, int clipHeight, PdfRotation rotate, PdfRenderFlags flags) 291 | { 292 | return Render(width, height, clipX, clipY, clipWidth, clipHeight, 0, 0, rotate, flags); 293 | } 294 | 295 | private bool RenderPDFPageToBitmap(IntPtr bitmapHandle, int dpiX, int dpiY, int boundsOriginX, int boundsOriginY, int boundsWidth, int boundsHeight, int rotate, NativeMethods.FPDF flags, bool renderFormFill) 296 | { 297 | if (_disposed) 298 | { 299 | throw new ObjectDisposedException(nameof(PdfPage)); 300 | } 301 | 302 | if (renderFormFill) 303 | flags &= ~NativeMethods.FPDF.ANNOT; 304 | 305 | NativeMethods.FPDF_RenderPageBitmap(bitmapHandle, Page, boundsOriginX, boundsOriginY, boundsWidth, boundsHeight, rotate, flags); 306 | 307 | if (renderFormFill) 308 | { 309 | NativeMethods.FPDF_FFLDraw(_form, bitmapHandle, Page, boundsOriginX, boundsOriginY, boundsWidth, boundsHeight, rotate, flags); 310 | } 311 | 312 | return true; 313 | } 314 | 315 | /// 316 | /// Gets the index of the character at the provided position 317 | /// 318 | /// x 319 | /// y 320 | /// The tolerance 321 | /// The zero-based index of the character at, or nearby the point (x,y). If there is no character at or nearby the point, return value will be -1. If an error occurs, -3 will be returned. 322 | public int GetCharIndexAtPos(double x, double y, double tol = 12) 323 | { 324 | return NativeMethods.FPDFText_GetCharIndexAtPos(TextPage, x, y, tol, tol); 325 | } 326 | 327 | /// 328 | /// Transforms a Point in device coordinates to PDF coordinates. 329 | /// 330 | /// The point in device coordinates 331 | /// Render with of the page 332 | /// Render height of the page 333 | /// The transformed Point 334 | public PointF PointToPdf(Point point, int renderWidth, int renderHeight) 335 | { 336 | 337 | NativeMethods.FPDF_DeviceToPage( 338 | Page, 339 | 0, 340 | 0, 341 | renderWidth, 342 | renderHeight, 343 | 0, 344 | point.X, 345 | point.Y, 346 | out double deviceX, 347 | out double deviceY 348 | ); 349 | 350 | return new PointF((float)deviceX, (float)deviceY); 351 | } 352 | 353 | /// 354 | /// Transforms a Rectangle in device coordinates to PDF coordinates. 355 | /// Will also make sure to return a Rectangle with positive height and width. 356 | /// 357 | /// The Rectangle in device coordinates 358 | /// Render with of the page 359 | /// Render height of the page 360 | /// The transformed Rectangle 361 | public RectangleF RectangleToPdf(Rectangle rect, int renderWidth, int renderHeight) 362 | { 363 | 364 | NativeMethods.FPDF_DeviceToPage( 365 | Page, 366 | 0, 367 | 0, 368 | renderWidth, 369 | renderHeight, 370 | 0, 371 | rect.Left, 372 | rect.Top, 373 | out double deviceX1, 374 | out double deviceY1 375 | ); 376 | 377 | NativeMethods.FPDF_DeviceToPage( 378 | Page, 379 | 0, 380 | 0, 381 | renderWidth, 382 | renderHeight, 383 | 0, 384 | rect.Right, 385 | rect.Bottom, 386 | out double deviceX2, 387 | out double deviceY2 388 | ); 389 | 390 | return new RectangleF( 391 | (float)deviceX1, 392 | (float)deviceY1, 393 | (float)Math.Abs((deviceX2 - deviceX1)), 394 | (float)Math.Abs((deviceY2 - deviceY1)) 395 | ); 396 | 397 | } 398 | 399 | /// 400 | /// Gets the text fromf page 401 | /// 402 | /// The text/returns> 403 | public string GetPdfText() 404 | { 405 | int length = NativeMethods.FPDFText_CountChars(TextPage); 406 | return GetPdfText(0, length); 407 | } 408 | 409 | /// 410 | /// Gets the text from page specified by offset and length 411 | /// 412 | /// The text/returns> 413 | public string GetPdfText(int offset, int length) 414 | { 415 | var result = new byte[(length + 1) * 2]; 416 | NativeMethods.FPDFText_GetText(TextPage, offset, length, result); 417 | return FPDFEncoding.GetString(result, 0, length * 2); 418 | } 419 | 420 | /// 421 | /// Rotates the page. 422 | /// 423 | /// Specify the rotation. 424 | public void RotatePage(PdfRotation rotation) => NativeMethods.FPDFPage_SetRotation(Page, rotation); 425 | 426 | /// 427 | /// Get the bounds of the text (specified by index and length) from the page. 428 | /// 429 | /// The start index of the text 430 | /// The length of the text 431 | /// List of the bounds for the text 432 | public PdfRectangle[] GetTextBounds(int startIndex, int length) 433 | { 434 | int countRects = NativeMethods.FPDFText_CountRects(TextPage, startIndex, length); 435 | 436 | var result = new PdfRectangle[countRects]; 437 | 438 | for (int i = 0; i < countRects; i++) 439 | { 440 | NativeMethods.FPDFText_GetRect(TextPage, i, out double left, out double top, out double right, out double bottom); 441 | 442 | RectangleF bounds = new RectangleF( 443 | (float)left, 444 | (float)top, 445 | (float)(right - left), 446 | (float)(bottom - top)); 447 | 448 | if (bounds.Width == 0 || bounds.Height == 0) 449 | continue; 450 | 451 | result[i] = new PdfRectangle(PageNumber, bounds); 452 | } 453 | 454 | return result; 455 | } 456 | 457 | /// 458 | /// Transforms a Rectangle in PDF coordinates to device coordinates. 459 | /// Will also make sure to return a Rectangle with positive height and width. 460 | /// 461 | /// The rect in PDF coordinates 462 | /// Render width of the page 463 | /// Render height of the page 464 | /// The transformed Rectangle 465 | public Rectangle RectangleFromPdf(RectangleF rect, int renderWidth, int renderHeight) 466 | { 467 | NativeMethods.FPDF_PageToDevice( 468 | Page, 469 | 0, 470 | 0, 471 | renderWidth, 472 | renderHeight, 473 | 0, 474 | rect.Left, 475 | rect.Top, 476 | out int deviceX1, 477 | out int deviceY1 478 | ); 479 | 480 | NativeMethods.FPDF_PageToDevice( 481 | Page, 482 | 0, 483 | 0, 484 | renderWidth, 485 | renderHeight, 486 | 0, 487 | rect.Right, 488 | rect.Bottom, 489 | out int deviceX2, 490 | out int deviceY2 491 | ); 492 | 493 | return new Rectangle( 494 | Math.Min(deviceX1, deviceX2), 495 | Math.Min(deviceY1, deviceY2), 496 | Math.Abs(deviceX2 - deviceX1), 497 | Math.Abs(deviceY2 - deviceY1) 498 | ); 499 | 500 | } 501 | 502 | private NativeMethods.FPDF FlagsToFPDFFlags(PdfRenderFlags flags) 503 | { 504 | return (NativeMethods.FPDF)(flags & ~(PdfRenderFlags.Transparent | PdfRenderFlags.CorrectFromDpi)); 505 | } 506 | 507 | 508 | /// 509 | /// Extract unicode text within a rectangular boundary on the page, 510 | /// rectangle defined by device coordinates: left, top, right, bottom. 511 | /// 512 | /// X device coordinate of the rectangle 513 | /// Y device coordinate of the rectangle 514 | /// X2 device coordinate of the rectangle 515 | /// Y2 device coordinate of the rectangle 516 | /// Render width of the page 517 | /// Render height of the page 518 | /// unicode text in the described rectangle 519 | public string GetBoundedText(int left,int top,int right,int bottom, int renderWidth, int renderHeight) 520 | { 521 | 522 | //double deviceX1, deviceY1, deviceX2, deviceY2 = 0; 523 | NativeMethods.FPDF_DeviceToPage( 524 | Page, 525 | 0, 526 | 0, 527 | renderWidth, 528 | renderHeight, 529 | 0, 530 | left, 531 | top, 532 | out double deviceX1, 533 | out double deviceY1 534 | ); 535 | 536 | NativeMethods.FPDF_DeviceToPage( 537 | Page, 538 | 0, 539 | 0, 540 | renderWidth, 541 | renderHeight, 542 | 0, 543 | right, 544 | bottom, 545 | out double deviceX2, 546 | out double deviceY2 547 | ); 548 | 549 | uint number_of_characters_not_bytes_needed = NativeMethods.FPDFText_GetBoundedText(TextPage,deviceX1,deviceY1,deviceX2,deviceY2, null, 0); 550 | 551 | uint length = ( number_of_characters_not_bytes_needed + 1) * 2; 552 | if (length <= 2) 553 | { 554 | return string.Empty; 555 | } 556 | 557 | byte[] buffer = new byte[length]; 558 | NativeMethods.FPDFText_GetBoundedText(TextPage, deviceX1, deviceY1, deviceX2, deviceY2, buffer, length); 559 | 560 | return Encoding.Unicode.GetString(buffer, 0, (int)(length - 2)); 561 | } 562 | } 563 | } -------------------------------------------------------------------------------- /PdfPageLink.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace PdfiumLight 4 | { 5 | /// 6 | /// Describes a link on a page. 7 | /// 8 | public class PdfPageLink 9 | { 10 | /// 11 | /// Creates a new instance of the PdfPageLink class. 12 | /// 13 | /// The location of the link 14 | /// The target page of the link 15 | /// The target URI of the link 16 | public PdfPageLink(RectangleF bounds, int? targetPage, string uri) 17 | { 18 | Bounds = bounds; 19 | TargetPage = targetPage; 20 | Uri = uri; 21 | } 22 | 23 | /// 24 | /// The location of the link. 25 | /// 26 | public RectangleF Bounds { get; private set; } 27 | 28 | /// 29 | /// The target of the link. 30 | /// 31 | public int? TargetPage { get; private set; } 32 | 33 | /// 34 | /// The target URI of the link. 35 | /// 36 | public string Uri { get; private set; } 37 | } 38 | } -------------------------------------------------------------------------------- /PdfPageLinks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | 5 | namespace PdfiumLight 6 | { 7 | /// 8 | /// Describes all links on a page. 9 | /// 10 | public class PdfPageLinks 11 | { 12 | /// 13 | /// All links of the page. 14 | /// 15 | public IList Links { get; private set; } 16 | 17 | /// 18 | /// Creates a new instance of the PdfPageLinks class. 19 | /// 20 | /// The links on the PDF page. 21 | public PdfPageLinks(IList links) 22 | { 23 | if (links is null) 24 | throw new ArgumentNullException(nameof(links)); 25 | 26 | Links = new ReadOnlyCollection(links); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PdfPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | #pragma warning disable 1591 5 | 6 | namespace PdfiumLight 7 | { 8 | public readonly struct PdfPoint : IEquatable 9 | { 10 | public static readonly PdfPoint Empty = new PdfPoint(); 11 | 12 | // _page is offset by 1 so that Empty returns an invalid point. 13 | private readonly int _page; 14 | 15 | public int Page => _page - 1; 16 | 17 | public PointF Location { get; } 18 | 19 | public bool IsValid => _page != 0; 20 | 21 | public PdfPoint(int page, PointF location) 22 | { 23 | _page = page + 1; 24 | Location = location; 25 | } 26 | 27 | public bool Equals(PdfPoint other) 28 | { 29 | return 30 | Page == other.Page && 31 | Location == other.Location; 32 | } 33 | 34 | public override bool Equals(object obj) 35 | { 36 | return obj is PdfPoint other && Equals(other); 37 | } 38 | 39 | public override int GetHashCode() 40 | { 41 | unchecked 42 | { 43 | return (Page * 397) ^ Location.GetHashCode(); 44 | } 45 | } 46 | 47 | public static bool operator ==(PdfPoint left, PdfPoint right) 48 | { 49 | return left.Equals(right); 50 | } 51 | 52 | public static bool operator !=(PdfPoint left, PdfPoint right) 53 | { 54 | return !left.Equals(right); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /PdfRectangle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace PdfiumLight 5 | { 6 | public readonly struct PdfRectangle : IEquatable 7 | { 8 | // _page is offset by 1 so that Empty returns an invalid rectangle. 9 | private readonly int _page; 10 | 11 | public static readonly PdfRectangle Empty = new PdfRectangle(); 12 | 13 | public PdfRectangle(int page, RectangleF bounds) 14 | { 15 | _page = page + 1; 16 | Bounds = bounds; 17 | } 18 | 19 | public PdfRectangle(int page, RectangleF bounds, int astartIndex, int asize) 20 | { 21 | _page = page + 1; 22 | Bounds = bounds; 23 | } 24 | 25 | public int Page => _page - 1; 26 | 27 | public RectangleF Bounds { get; } 28 | 29 | public bool IsValid => _page != 0; 30 | 31 | public bool Equals(PdfRectangle other) 32 | { 33 | return 34 | Page == other.Page && 35 | Bounds == other.Bounds; 36 | } 37 | 38 | public override bool Equals(object obj) 39 | { 40 | return obj is PdfRectangle other && Equals(other); 41 | } 42 | 43 | public override int GetHashCode() 44 | { 45 | unchecked 46 | { 47 | return (Page * 397) ^ Bounds.GetHashCode(); 48 | } 49 | } 50 | 51 | public static bool operator ==(PdfRectangle left, PdfRectangle right) 52 | { 53 | return left.Equals(right); 54 | } 55 | 56 | public static bool operator !=(PdfRectangle left, PdfRectangle right) 57 | { 58 | return !left.Equals(right); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /PdfRenderFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PdfiumLight 4 | { 5 | /// 6 | /// Flags that influence the page rendering process. 7 | /// 8 | [Flags] 9 | public enum PdfRenderFlags 10 | { 11 | /// 12 | /// No flags. 13 | /// 14 | None = 0, 15 | /// 16 | /// Render for printing. 17 | /// 18 | ForPrinting = NativeMethods.FPDF.PRINTING, 19 | /// 20 | /// Set if annotations are to be rendered. 21 | /// 22 | Annotations = NativeMethods.FPDF.ANNOT, 23 | /// 24 | /// Set if using text rendering optimized for LCD display. 25 | /// 26 | LcdText = NativeMethods.FPDF.LCD_TEXT, 27 | /// 28 | /// Don't use the native text output available on some platforms. 29 | /// 30 | NoNativeText = NativeMethods.FPDF.NO_NATIVETEXT, 31 | /// 32 | /// Grayscale output. 33 | /// 34 | Grayscale = NativeMethods.FPDF.GRAYSCALE, 35 | /// 36 | /// Limit image cache size. 37 | /// 38 | LimitImageCacheSize = NativeMethods.FPDF.RENDER_LIMITEDIMAGECACHE, 39 | /// 40 | /// Always use halftone for image stretching. 41 | /// 42 | ForceHalftone = NativeMethods.FPDF.RENDER_FORCEHALFTONE, 43 | /// 44 | /// Render with a transparent background. 45 | /// 46 | Transparent = 0x1000, 47 | /// 48 | /// Correct height/width for DPI. 49 | /// 50 | CorrectFromDpi = 0x2000 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /PdfRotation.cs: -------------------------------------------------------------------------------- 1 | namespace PdfiumLight 2 | { 3 | /// 4 | /// Specifies the rotation of pages shown in the PDF renderer. 5 | /// 6 | public enum PdfRotation 7 | { 8 | /// 9 | /// Rotates the output 0 degrees. 10 | /// 11 | Rotate0, 12 | /// 13 | /// Rotates the output 90 degrees. 14 | /// 15 | Rotate90, 16 | /// 17 | /// Rotates the output 180 degrees. 18 | /// 19 | Rotate180, 20 | /// 21 | /// Rotates the output 270 degrees. 22 | /// 23 | Rotate270 24 | } 25 | } -------------------------------------------------------------------------------- /PdfTextSpan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PdfiumLight 4 | { 5 | public readonly struct PdfTextSpan : IEquatable 6 | { 7 | public PdfTextSpan(int page, int offset, int length) 8 | { 9 | Page = page; 10 | Offset = offset; 11 | Length = length; 12 | } 13 | 14 | public int Page { get; } 15 | 16 | public int Offset { get; } 17 | 18 | public int Length { get; } 19 | 20 | public bool Equals(PdfTextSpan other) 21 | { 22 | return 23 | Page == other.Page && 24 | Offset == other.Offset && 25 | Length == other.Length; 26 | } 27 | 28 | public override bool Equals(object obj) 29 | { 30 | return obj is PdfTextSpan other && Equals((PdfTextSpan)obj); 31 | } 32 | 33 | public override int GetHashCode() 34 | { 35 | unchecked 36 | { 37 | int hashCode = Page; 38 | hashCode = (hashCode * 397) ^ Offset; 39 | hashCode = (hashCode * 397) ^ Length; 40 | return hashCode; 41 | } 42 | } 43 | 44 | public static bool operator ==(PdfTextSpan left, PdfTextSpan right) 45 | { 46 | return left.Equals(right); 47 | } 48 | 49 | public static bool operator !=(PdfTextSpan left, PdfTextSpan right) 50 | { 51 | return !left.Equals(right); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /PdfiumLight.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | PdfiumLight 6 | PdfiumLight 7 | 7.3 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /PdfiumLight.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PdfiumLight 6 | 7 | 8 | 0.0.4 9 | 10 | 11 | Marc Pabst 12 | 13 | 17 | marcpabst 18 | 19 | 20 | https://opensource.org/licenses/MIT 21 | https://github.com/marcpabst/PdfiumLight 22 | 23 | 24 | 28 | false 29 | 30 | 31 | Initial release 32 | 33 | 37 | A lightweight C# Library to render PDFs with Google's Pdfium in .NET Apps. 38 | 39 | 40 | 41 | 42 | 43 | pdf pdfium pdfrenderer 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /PdfiumLight.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2000 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfiumLight", "PdfiumLight.csproj", "{438914B6-5D1C-482C-B942-5C0E057EEF6F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {438914B6-5D1C-482C-B942-5C0E057EEF6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {438914B6-5D1C-482C-B942-5C0E057EEF6F}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {438914B6-5D1C-482C-B942-5C0E057EEF6F}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {438914B6-5D1C-482C-B942-5C0E057EEF6F}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {DD9D26EE-BF58-4219-B855-DD43DBCE1B5D} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PdfiumLight 2 | A lightweight C# Library to render PDFs with Google's Pdfium in .NET Apps. This is basically a much slimmer version of [Pieter van Ginkel's PdfiumViewer](https://github.com/pvginkel/PdfiumViewer) with some added functionality. 3 | 4 | ## Getting started 5 | Getting started is easy. Just add `PdfiumLight`, `PdfiumViewer.Native.x86.v8-xfa` and / or `PdfiumViewer.Native.x86_64.v8-xfa` (see below) as NuGet dependencies to your project and you're good to go: 6 | ```c# 7 | // Load the pdf file and create a new document object 8 | PdfDocument document = new PdfDocument("C:/Users/Tom/Documents/sample.pdf"); 9 | // Load the first page 10 | PdfPage page = document.GetPage(0); 11 | // Render the page with a width of 1000px and automatically computed height 12 | var renderedPage = page.Render(1000, 0); 13 | ``` 14 | ### Render part of a page 15 | You can specify a clipping rectangle within the original image that would have been rendered only specifying `widht` and `height`. This will render only part of the page, thus beeing much faster than rendering the whole page and clipping afterwards. 16 | ```c# 17 | // Load the pdf file and create a new document object 18 | PdfDocument document = new PdfDocument("C:/Users/Marc/Documents/sample.pdf"); 19 | // Load the first page 20 | PdfPage page = document.GetPage(0); 21 | // Render the page 22 | var renderedPage = page.Render( 23 | 10000, // width in px 24 | 0, // '0' to compute height according to aspect ratio 25 | 0, // x of the top/left of clipping rectangle 26 | 0, // y of the top/left point of clipping rectangle 27 | 1000, // width of clipping reactangle 28 | 1000, // height of clipping reactangle 29 | PdfRotation.Rotate0, // no rotation 30 | PdfRenderFlags.None // no render flags 31 | ); 32 | ``` 33 | ### You have to provide pdfium.dll 34 | There are many ways to include pdfium.dll, the most easy one is by adding one or both of the following NuGet-dependencies created by @pvginkel: 35 | 36 | - `PdfiumViewer.Native.x86.v8-xfa` 37 | - `PdfiumViewer.Native.x86_64.v8-xfa` 38 | 39 | A very basic packages.config could look like this: 40 | ```xm 41 | 42 | 43 | 44 | 45 | 46 | 47 | ``` 48 | ## Features 49 | ### (already implemented) 50 | - load PDF-documents 51 | - render pages as a whole or partly 52 | - convert device coordinates to page coordinates and vice versa 53 | - extract text from page 54 | - check if there is text at a specific point on the page (very usefull if implementing a text layer) 55 | ### (still to come) 56 | - support for internal and external links 57 | - search 58 | - annoatation support 59 | - form support 60 | ### (wishlist) 61 | - caching 62 | - simple editing features such as reorder and deleting pages and merging documents 63 | -------------------------------------------------------------------------------- /StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace PdfiumLight 5 | { 6 | internal static class StreamExtensions 7 | { 8 | public static byte[] ToByteArray(Stream stream) 9 | { 10 | if (stream is null) 11 | throw new ArgumentNullException(nameof(stream)); 12 | 13 | if (stream is MemoryStream memoryStream) 14 | { 15 | return memoryStream.ToArray(); 16 | } 17 | 18 | if (stream.CanSeek) 19 | return ReadBytesFast(stream); 20 | else 21 | return ReadBytesSlow(stream); 22 | } 23 | 24 | private static byte[] ReadBytesFast(Stream stream) 25 | { 26 | byte[] data = new byte[stream.Length]; 27 | 28 | int offset = 0; 29 | 30 | while (offset < data.Length) 31 | { 32 | int read = stream.Read(data, offset, data.Length - offset); 33 | 34 | if (read <= 0) 35 | break; 36 | 37 | offset += read; 38 | } 39 | 40 | if (offset < data.Length) 41 | throw new InvalidOperationException("Incorrect length reported"); 42 | 43 | return data; 44 | } 45 | 46 | private static byte[] ReadBytesSlow(Stream stream) 47 | { 48 | using (var memoryStream = new MemoryStream()) 49 | { 50 | CopyStream(stream, memoryStream); 51 | 52 | return memoryStream.ToArray(); 53 | } 54 | } 55 | 56 | public static void CopyStream(Stream from, Stream to) 57 | { 58 | if (@from is null) 59 | throw new ArgumentNullException(nameof(from)); 60 | 61 | if (to is null) 62 | throw new ArgumentNullException(nameof(to)); 63 | 64 | var buffer = new byte[4096]; 65 | 66 | while (true) 67 | { 68 | int read = from.Read(buffer, 0, buffer.Length); 69 | 70 | if (read == 0) 71 | return; 72 | 73 | to.Write(buffer, 0, read); 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /StreamManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace PdfiumLight 6 | { 7 | internal static class StreamManager 8 | { 9 | private static readonly object _syncRoot = new object(); 10 | private static int _nextId = 1; 11 | private static readonly Dictionary _files = new Dictionary(); 12 | 13 | public static int Register(Stream stream) 14 | { 15 | if (stream is null) 16 | throw new ArgumentNullException(nameof(stream)); 17 | 18 | lock (_syncRoot) 19 | { 20 | int id = _nextId++; 21 | _files.Add(id, stream); 22 | return id; 23 | } 24 | } 25 | 26 | public static void Unregister(int id) 27 | { 28 | lock (_syncRoot) 29 | { 30 | _files.Remove(id); 31 | } 32 | } 33 | 34 | public static Stream Get(int id) 35 | { 36 | lock (_syncRoot) 37 | { 38 | _files.TryGetValue(id, out Stream stream); 39 | return stream; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | --------------------------------------------------------------------------------