{"id":18995812,"url":"https://github.com/klippa-app/go-pdfium","last_synced_at":"2025-04-08T09:06:10.517Z","repository":{"id":39746339,"uuid":"347021224","full_name":"klippa-app/go-pdfium","owner":"klippa-app","description":"Easy to use PDF library using Go and PDFium","archived":false,"fork":false,"pushed_at":"2025-03-27T20:59:24.000Z","size":40817,"stargazers_count":236,"open_issues_count":10,"forks_count":22,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-01T07:51:19.791Z","etag":null,"topics":["binding","bindings","go","golang","golang-library","pdf","pdfium","renderer","rendering","webassembly"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/klippa-app.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-12T10:05:55.000Z","updated_at":"2025-03-31T15:47:04.000Z","dependencies_parsed_at":"2024-01-16T16:41:47.595Z","dependency_job_id":"25adf8d6-b7eb-41e5-82ce-beaf08fb8067","html_url":"https://github.com/klippa-app/go-pdfium","commit_stats":null,"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klippa-app%2Fgo-pdfium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klippa-app%2Fgo-pdfium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klippa-app%2Fgo-pdfium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klippa-app%2Fgo-pdfium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klippa-app","download_url":"https://codeload.github.com/klippa-app/go-pdfium/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247809963,"owners_count":20999816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binding","bindings","go","golang","golang-library","pdf","pdfium","renderer","rendering","webassembly"],"created_at":"2024-11-08T17:32:55.097Z","updated_at":"2025-04-08T09:06:10.485Z","avatar_url":"https://github.com/klippa-app.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-pdfium\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/klippa-app/go-pdfium/pdfium.svg)](https://pkg.go.dev/github.com/klippa-app/go-pdfium)\n[![Build Status][build-status]][build-url]\n[![codecov](https://codecov.io/gh/klippa-app/go-pdfium/branch/main/graph/badge.svg?token=WoIlW9RbfH)](https://codecov.io/gh/klippa-app/go-pdfium)\n\n[build-status]:https://github.com/klippa-app/go-pdfium/workflows/Go/badge.svg\n\n[build-url]:https://github.com/klippa-app/go-pdfium/actions\n\n:rocket: *Easy to use PDF library using Go and PDFium* :rocket:\n\n**A fast, multi-threaded and easy to use PDF library for Go applications.**\n\n## Features\n\n* Option between single-threaded, multi-threaded (through subprocesses) and WebAssembly (which can do multithreading\n  within go), while keeping the same interface\n* This library will handle all complicated cgo/WebAssembly gymnastics for you, no direct cgo/WebAssembly usage/knowledge\n  required\n* Implementation of all PDFium public API methods (including methods that are marked experimental), with some exceptions\n* PDFium has methods to do the following:\n    * PDFium instance configuration (sandbox policy, fonts)\n    * Document loading (from bytes, path or io.ReadSeeker)\n    * Document info (metadata, page count, render mode, PDF version, permissions, security handler revision)\n    * Page info (size, transparency)\n    * Rendering (through bitmap)\n    * Bitmap handling\n    * Named destinations\n    * Text handling (extract, search, text size/color/font information)\n    * Creation (create new documents and pages)\n    * Editing (rotating, import pages from another document, copy view preferences from another document, flattening)\n    * Bookmarks / Links / Weblinks\n    * Document saving (to bytes, path or io.Writer)\n    * JavaScript actions\n    * Thumbnails\n    * Attachments\n    * XFA packet handling\n    * ViewerRef (print settings)\n    * Windows features (`FPDF_SetPrintMode`, `FPDF_RenderPage`)\n    * Transformations (page boxes, clip paths)\n    * Progressive rendering\n    * Document loading through data availability (loading data as needed)\n    * Struct trees\n    * Page/Page object editing\n    * Annotations\n    * Form filling\n* Methods that won't be implemented for now:\n    * fpdf_sysfontinfo.h (probably too complicated)\n    * Skia methods ([not in pre-built binaries](https://github.com/bblanchon/pdfium-binaries/issues/29))\n    * XFA/v8 JS\n      methods ([not in pre-built binaries due to build issues](https://github.com/bblanchon/pdfium-binaries/issues/62))\n* Useful helpers to make your life easier:\n    * Get all document metadata\n    * Get all document bookmarks\n    * Get all document attachments\n    * Get all document JavaScript actions\n    * Get plain text of a page\n    * Get structured text of a page (text, angle, position, size, font information)\n    * Render 1 or multiple pages from 1 or multiple documents into a Go `image.Image` using either DPI or pixel size\n    * Use the same render instructions to render the image directly as a jpeg or png into a file path or byte array\n    * Get page size in either points or pixel size (when rendered in a specific DPI)\n    * Get the point to pixel ratio when rendering or extracting text (to determine the positions when rendering into an\n      image)\n\n## PDFium\n\nThis project uses the PDFium C++ library by Google (https://pdfium.googlesource.com/pdfium/) to process the PDF\ndocuments. Therefor this project could also be called a binding.\n\nPlease be aware that PDFium comes with the `Apache License 2.0` license.\n\n### Single/Multi-threading\n\nSince PDFium is [not a multithreaded C++ library](https://groups.google.com/g/pdfium/c/HeZSsM_KEUk), we can not directly\nmake it multithreaded by calling it from Go's subroutines.\n\nTo solve this, this library has 3 different implementations that you can use to call PDFium:\n\n- single_threaded: call PDFium from the same process with CGO\n- multi_threaded: call PDFium using multiple workers with CGO, implemented using go-plugin\n- webassembly: call PDFium using WebAssembly with [Wazero runtime](https://wazero.io/), you can start multiple runtimes\n  to get multi-threaded behaviour\n\nBoth `single_threaded` and `multi_threaded` requires PDFium to be installed on your machine for your platform during\ncompilation and runtime, it also requires CGO to work on the platform you're compiling to.\n\n`webassembly` does not need any external dependencies and also does not require CGO to work. However, Wazero currently\nonly has compiler support for amd64 and arm64, meaning it will be using the interpreter on other architectures which\nwill be much, much slower.\n\nAll implementations use exactly the same interface, so there won't be any code changes for you to switch between them.\n\nAll implementations are thread/subroutine safe, this has been guaranteed by locking the instance that's doing your work\nwhile it's doing PDFium operations. New operations will wait until the lock becomes available again.\n\n**Be aware that PDFium could use quite some memory depending on the size of the PDF and the requests that you do, so be\naware of the amount of workers that you configure.**\n\n## Implementations\n\n### Single/Multi-threading through CGO\n\nSingle-threading in CGO works by directly calling the PDFium library from the same process. Single-threaded might be\npreferred if the caller is managing the workers themselves and does not want the overhead of another process. Be aware\nthat since PDFium is C++, we can't handle segfaults caused by PDFium, which may cause your process to be killed. So\nusing this library in the multi-threaded way, with only 1 worker, can still have some benefits, since it can\nautomatically recover from things like segfaults.\n\nFor CGO we have implemented multi-threading\nusing [HashiCorp's Go Plugin System](https://github.com/hashicorp/go-plugin),\nwhich allows us to launch separate PDFium worker processes, and then route the requests through the different workers.\nThis also makes it a bit more safe to use PDFium, as it's less likely to segfaults or corrupt your main Go application.\nThe Plugin system provides the communication between the processes using gRPC, however, when implementing this library,\nyou won't really see anything of that. From the outside it will look like normal Go code. The inter-process\ncommunication does come with a cost as it has to serialize/deserialize input/output as it moves between the main process\nand the PDFium workers.\n\n#### Prerequisites (CGO)\n\nTo use this Go library, you will need the actual PDFium library to run it and have it available through pkgconfig.\n\n##### Get the library\n\nYou can try to compile PDFium yourself, but you can also use pre-compiled binaries, for example\nfrom: https://github.com/bblanchon/pdfium-binaries/releases\n\nIf you use a pre-compiled library, make sure to extract it somewhere logical, for example /opt/pdfium.\n\n##### Configure pkg-config\n\nCreate/edit file `/usr/lib/pkgconfig/pdfium.pc`\n\n```\nprefix={path}\nlibdir={path}/lib\nincludedir={path}/include\n\nName: PDFium\nDescription: PDFium\nVersion: 7019\nRequires:\n\nLibs: -L${libdir} -lpdfium\nCflags: -I${includedir}\n```\n\nReplace `{path}` with the path you extracted/compiled pdfium in.\n\nMake sure you extend your library path when running:\n\n`export LD_LIBRARY_PATH={path}/lib`\n\nYou can do this globally or just in your editor.\n\nthis can globally be done on ubuntu by editing `~/.profile`\nand adding the line in this file. reloading for bash can be done by relogging or running `source ~/.profile` can be used\nto test the change for a terminal\n\n#### Getting started (CGO)\n\nTo get started, make sure that you create a separate package in your application that will start the worker.\n\nThe examples below can also be found in the examples folder.\n\n##### Single-threaded through CGO\n\nFor single threaded implementations we just have to initialize the library.\n\n`pdfium/renderer/renderer.go`\n\n```go\npackage renderer\n\nimport (\n\t\"log\"\n\n\t\"github.com/klippa-app/go-pdfium\"\n\t\"github.com/klippa-app/go-pdfium/single_threaded\"\n)\n\n// Be sure to close pools/instances when you're done with them.\nvar pool pdfium.Pool\nvar instance pdfium.Pdfium\n\nfunc init() {\n\t// Init the PDFium library and return the instance to open documents.\n\tpool = single_threaded.Init(single_threaded.Config{})\n\n\tvar err error\n\tinstance, err = pool.GetInstance(time.Second * 30)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n##### Multi-threaded through CGO\n\n###### Worker package\n\nThis package has to be named main to make it available as a binary. The plugin system will use this to start new PDFium\nworkers. Example:\n\n`pdfium/worker/main.go`\n\n```go\npackage main\n\nimport (\n\t\"github.com/klippa-app/go-pdfium/multi_threaded/worker\"\n)\n\nfunc main() {\n\tworker.StartWorker(nil)\n}\n```\n\n###### Worker configuration\n\nTo actually start workers, you will have to init the PDFium library somewhere, this also allows you to dynamically start\nworkers when needed. The best location to add this is in the `init()` of a package that is going to call the PDFium\nlibrary. Example:\n\n`pdfium/renderer/renderer.go`\n\n```go\npackage renderer\n\nimport (\n\t\"log\"\n\n\t\"github.com/klippa-app/go-pdfium\"\n\t\"github.com/klippa-app/go-pdfium/multi_threaded\"\n)\n\n// Be sure to close pools/instances when you're done with them.\nvar pool pdfium.Pool\nvar instance pdfium.Pdfium\n\nfunc init() {\n\t// Init the PDFium library and return the instance to open documents.\n\t// You can tweak these configs to your need. Be aware that workers can use quite some memory.\n\tpool = multi_threaded.Init(multi_threaded.Config{\n\t\tMinIdle:  1, // Makes sure that at least x workers are always available\n\t\tMaxIdle:  1, // Makes sure that at most x workers are ever available\n\t\tMaxTotal: 1, // Maxium amount of workers in total, allows the amount of workers to grow when needed, items between total max and idle max are automatically cleaned up, while idle workers are kept alive so they can be used directly.\n\t\tCommand: multi_threaded.Command{\n\t\t\tBinPath: \"go\",                                     // Only do this while developing, on production put the actual binary path in here. You should not want the Go runtime on production.\n\t\t\tArgs:    []string{\"run\", \"pdfium/worker/main.go\"}, // This is a reference to the worker package, this can be left empty when using a direct binary path.\n\t\t},\n\t})\n\n\tvar err error\n\tinstance, err = pool.GetInstance(time.Second * 30)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n###### Get page count\n\n```go\npackage renderer\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\n\t\"github.com/klippa-app/go-pdfium\"\n\t\"github.com/klippa-app/go-pdfium/requests\"\n)\n\n// Insert the single/multi-threaded init() here.\n\nfunc main() {\n\tfilePath := \"example.pdf\"\n\tpageCount, err := getPageCount(filePath)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlog.Printf(\"The PDF %s has %d pages\", filePath, pageCount)\n}\n\nfunc getPageCount(filePath string) (int, error) {\n\t// Load the PDF file into a byte array.\n\tpdfBytes, err := ioutil.ReadFile(filePath)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\t// Open the PDF using PDFium (and claim a worker)\n\tdoc, err := instance.OpenDocument(\u0026requests.OpenDocument{\n\t\tFile: \u0026pdfBytes,\n\t})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\t// Always close the document, this will release its resources.\n\tdefer instance.FPDF_CloseDocument(\u0026requests.FPDF_CloseDocument{\n\t\tDocument: doc.Document,\n\t})\n\n\tpageCount, err := instance.FPDF_GetPageCount(\u0026requests.FPDF_GetPageCount{\n\t\tDocument: doc,\n\t})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn pageCount.PageCount, nil\n}\n```\n\n###### Render a page\n\n```go\npackage renderer\n\nimport (\n\t\"image/png\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/klippa-app/go-pdfium\"\n\t\"github.com/klippa-app/go-pdfium/requests\"\n)\n\n// Insert the single/multi-threaded init() here.\n\nfunc main() {\n\tfilePath := \"example.pdf\"\n\toutput := \"example.pdf.png\"\n\terr := renderPage(filePath, 1, output)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\nfunc renderPage(filePath string, page int, output string) error {\n\t// Load the PDF file into a byte array.\n\tpdfBytes, err := ioutil.ReadFile(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Open the PDF using PDFium (and claim a worker)\n\tdoc, err := instance.OpenDocument(\u0026requests.OpenDocument{\n\t\tFile: \u0026pdfBytes,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Always close the document, this will release its resources.\n\tdefer instance.FPDF_CloseDocument(\u0026requests.FPDF_CloseDocument{\n\t\tDocument: doc.Document,\n\t})\n\n\t// Render the page in DPI 200.\n\tpageRender, err := instance.RenderPageInDPI(\u0026requests.RenderPageInDPI{\n\t\tDPI: 200, // The DPI to render the page in.\n\t\tPage: requests.Page{\n\t\t\tByIndex: \u0026requests.PageByIndex{\n\t\t\t\tDocument: doc,\n\t\t\t\tIndex:    0,\n\t\t\t},\n\t\t}, // The page to render, 0-indexed.\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Write the output to a file.\n\tf, err := os.Create(output)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\terr = png.Encode(f, pageRender.Image)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\n#### Experimental (CGO)\n\nSome newer API's by PDFium are marked as experimental. We do have support for these functions, but because they are\nprone to change\nwe do not compile with support for it by default. This is to keep support for most PDFium versions by default.\n\n**If you call any API methods marked as experimental, the call will result in an error**\n\nAdding support for the experimental API is quite easy. All you have to do is give the build tag `pdfium_experimental`\nwhen running `go build` or `go run`, like this:\n`go run -tags pdfium_experimental {package}/{file.go}` or `go build -tags pdfium_experimental {package}/{file.go}`.\n\nWe actively monitor PDFium API additions/changes/deletions and apply them in the code-base. When API methods become\nnon-experimental, we will make them available in the default configuration.\n\n### WebAssembly\n\nRecently we have added support for a non-cgo implementation using WebAssembly, we use\nthe [Wazero runtime](https://wazero.io/)\nfor running WebAssembly within Go. The comes with quite some advantages:\n\n- WebAssembly is one binary for every platform, which means we can embed the WebAssembly version of PDFium in this\n  repository\n- Because we have `go:embed`, we can embed the WebAssembly binary inside the Go binary\n- Because of this, you won't have to download and distribute PDFium yourself, making deployments simpler\n- Wazero is pure Go, and thus runs on any platform where you can run Go on (a lot), which also allows you to run\n  go-pdfium and PDFium on all of those platforms\n- Because it's running in Go, we can just start up multiple Wazero runtimes to support processing multiple PDF files at\n  once\n- Because it's running in Go, you can directly access the memory in Wazero, for example to render a PDFium bitmap\n  directly to a Go Image\n- Because it's running in Go, method calls and responses don't have to travel over gRPC like with go-plugin, saving\n  quite some time in encoding/decoding, which makes it about 2x as fast as the go-pugin multithreading approach\n- Since PDFium is compiled to WebAssembly and runs inside the Wazero runtime, it basically runs in a sandbox:\n    - No chance of crashing the Go process like with cgo\n    - No access to other local resources in case of attacks on PDFium (disk, network, memory)\n    - Full control over file access (you decide which folders Wazero exposes to PDFium, by default it exposes the whole\n      disk)\n\nOf course there are also some disadvantages:\n\n- It's about 2x as slow as the full native cgo implementation (but about 2x as fast as the multi-threaded cgo go-plugin\n  implementation)\n- WebAssembly doesn't have an option to give memory back to the system, once it has been claimed it will there until you\n  close the instance, this could be solved by not re-using instances\n- Some platform specific quirks that have been implemented in PDFium (for example for Windows and MacOS) won't work\n  because the WebAssembly build is compiled as Linux\n\nPlease be aware that Wazero comes with the `Apache License 2.0` license.\n\n#### Path handling (WebAssembly)\n\nBecause you can tell Wazero which folders have to be mounted in WebAssembly, you have full control over the filesystem.\n\nBy default, go-pdfium will mount the full root disk in Wazero on non-Windows environments.\nOn Windows environments, go-pdfium will get the volume of the current working directory and mount that as the root.\n\nYou can change this behaviour by overwriting FSConfig in the pool setup.\n\nAll paths given to go-pdfium in WebAssembly mode have to be in POSIX style and have to be absolute, so for\nexample: `/home/user/Downloads/file.pdf`. If you have mounted `/home/user/`on the root, then the path you would have to\ngive is `/Downloads/file.pdf`, this is the same on Windows, so no backward slashes or volume names in paths.\n\nYou can set your own mounts by overwriting FSConfig in the pool setup.\n\n#### Getting started (WebAssembly)\n\nThe examples below can also be found in the examples folder.\n\nTo start go-pdfium workers, you will have to init the go-pdfium worker pool somewhere, this also allows you to\ndynamically start\nworkers when needed. The best location to add this is in the `init()` of a package that is going to call the PDFium\nlibrary. Example:\n\n`pdfium/renderer/renderer.go`\n\n```go\npackage renderer\n\nimport (\n\t\"log\"\n\n\t\"github.com/klippa-app/go-pdfium\"\n\t\"github.com/klippa-app/go-pdfium/webassembly\"\n)\n\n// Be sure to close pools/instances when you're done with them.\nvar pool pdfium.Pool\nvar instance pdfium.Pdfium\n\nfunc init() {\n\t// Init the PDFium library and return the instance to open documents.\n\t// You can tweak these configs to your need. Be aware that workers can use quite some memory.\n\tpool, err = webassembly.Init(webassembly.Config{\n\t\tMinIdle:  1, // Makes sure that at least x workers are always available\n\t\tMaxIdle:  1, // Makes sure that at most x workers are ever available\n\t\tMaxTotal: 1, // Maxium amount of workers in total, allows the amount of workers to grow when needed, items between total max and idle max are automatically cleaned up, while idle workers are kept alive so they can be used directly.\n\t})\n\n\tvar err error\n\tinstance, err = pool.GetInstance(time.Second * 30)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n##### Get page count\n\n```go\npackage renderer\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\n\t\"github.com/klippa-app/go-pdfium\"\n\t\"github.com/klippa-app/go-pdfium/requests\"\n)\n\n// Insert the webassembly init() here.\n\nfunc main() {\n\tfilePath := \"example.pdf\"\n\tpageCount, err := getPageCount(filePath)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tlog.Printf(\"The PDF %s has %d pages\", filePath, pageCount)\n}\n\nfunc getPageCount(filePath string) (int, error) {\n\t// Load the PDF file into a byte array.\n\tpdfBytes, err := ioutil.ReadFile(filePath)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\t// Open the PDF using PDFium (and claim a worker)\n\tdoc, err := instance.OpenDocument(\u0026requests.OpenDocument{\n\t\tFile: \u0026pdfBytes,\n\t})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\t// Always close the document, this will release its resources.\n\tdefer instance.FPDF_CloseDocument(\u0026requests.FPDF_CloseDocument{\n\t\tDocument: doc.Document,\n\t})\n\n\tpageCount, err := instance.FPDF_GetPageCount(\u0026requests.FPDF_GetPageCount{\n\t\tDocument: doc.Document,\n\t})\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn pageCount.PageCount, nil\n}\n```\n\n#### Render a page\n\n```go\npackage renderer\n\nimport (\n\t\"image/png\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/klippa-app/go-pdfium\"\n\t\"github.com/klippa-app/go-pdfium/requests\"\n)\n\n// Insert the webassembly init() here.\n\nfunc main() {\n\tfilePath := \"example.pdf\"\n\toutput := \"example.pdf.png\"\n\terr := renderPage(filePath, 1, output)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\nfunc renderPage(filePath string, page int, output string) error {\n\t// Load the PDF file into a byte array.\n\tpdfBytes, err := ioutil.ReadFile(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Open the PDF using PDFium (and claim a worker)\n\tdoc, err := instance.OpenDocument(\u0026requests.OpenDocument{\n\t\tFile: \u0026pdfBytes,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Always close the document, this will release its resources.\n\tdefer instance.FPDF_CloseDocument(\u0026requests.FPDF_CloseDocument{\n\t\tDocument: doc.Document,\n\t})\n\n\t// Render the page in DPI 200.\n\tpageRender, err := instance.RenderPageInDPI(\u0026requests.RenderPageInDPI{\n\t\tDPI: 200, // The DPI to render the page in.\n\t\tPage: requests.Page{\n\t\t\tByIndex: \u0026requests.PageByIndex{\n\t\t\t\tDocument: doc.Document,\n\t\t\t\tIndex:    0,\n\t\t\t},\n\t\t}, // The page to render, 0-indexed.\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// The Render* methods return a cleanup function that has to be called when\n\t// using webassembly to make sure resources are cleaned up. Do this after\n\t// you are done with the returned image object.\n\tdefer pageRender.Cleanup()\n\n\t// Write the output to a file.\n\tf, err := os.Create(output)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\terr = png.Encode(f, pageRender.Result.Image)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n```\n\n#### Experimental (WebAssembly)\n\nSome newer API's by PDFium are marked as experimental. The WebAssembly build has support for all of them.\n\nWe actively monitor PDFium API additions/changes/deletions and apply them in the code-base.\n\nThe WebAssembly build will always be the latest PDFium version that we added support for.\n\n## `io.ReadSeeker` and `io.Writer`\n\nDocument loading allows you to load a document with a `io.ReadSeeker`. Please be aware that this only works efficiently\nwhen using the single-threaded or WebAssembly usage, as that lives in the same process. For multi-threaded usage this\nwill just load in\nthe complete file and pass the bytes through the gRPC interface.\n\nDocument/image saving allows you to save using a `io.Writer`. Please be aware this only works when using the\nsingle-threaded or WebAssembly usage. It's not possible to encode the `io.Writer` with gRPC. Or share it between\nprocesses for that\nmatter.\n\n## Improving JPEG rendering speed\n\nBy default, this library renders images with the `image/jpeg` package that comes with Go to make distribution as simple\nas possible. However, this package is quite slow compared to other native libraries like libjpeg and libjpeg-turbo, you\ncan enable the usage of libjpeg-turbo by using the build tag `pdfium_use_turbojpeg`, this will require you to have the\npackage `libturbojpeg-dev` installed during build time and the `libturbojpeg` package during runtime and build time.\n\nSpeed improvements that can be expected are significant, for example: on a simple PDF the full process of rendering a\npage is 3x as fast compared to a build without libjpeg-turbo.\n\nThis is supported in both the CGO and WebAssembly implementation, please note that the WebAssembly implementation also\nuses CGO for libjpeg-turbo for now. There are plans to compile libjpeg-turbo to WebAssembly for the WebAssembly\nimplementation to keep the WebAssembly implementation actually full WebAssembly.\n\n## Support Policy\n\nWe offer an API stability promise with semantic versioning. In other words, we promise to not break any exported\nfunction signature without incrementing the major version. New features and behaviors happen with a minor version \nincrement, e.g. 1.0.11 to 1.1.0. We also fix bugs or change internal details with a patch version, e.g. 1.0.0 to 1.0.1.\nUpgrades of the supported PDFium version will cause a minor version update.\n\n### Go\n\nThis project will support the last 2 version of Go, this means that if the last version of Go is 1.24, our `go.mod`\nwill be set to Go 1.23, and our CI tests will be run on Go 1.23 and 1.24. This is in line with Go's\n[Release Policy](https://go.dev/doc/devel/release).\n\nIt won't mean that the library won't work with older versions of Go, but it will tell you what to expect of the\nsupported  Go versions. If we change the supported Go versions, we will make that a minor version upgrade. This policy\nallows you to not be forced to the latest Go version for a pretty long time, but it still allows us to use new language\nfeatures in a pretty reasonable time-frame.\n\n## About Klippa\n\nFounded in 2015, [Klippa](https://www.klippa.com/en)'s goal is to digitize \u0026 automate administrative processes with\nmodern technologies. We help clients enhance the effectiveness of their organization by using machine learning and OCR.\nSince 2015, more than a thousand happy clients have used Klippa's software solutions. Klippa currently has an\ninternational team of 50 people, with offices in Groningen, Amsterdam and Brasov.\n\n## License\n\nThe MIT License (MIT)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklippa-app%2Fgo-pdfium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklippa-app%2Fgo-pdfium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklippa-app%2Fgo-pdfium/lists"}