{"id":37027402,"url":"https://github.com/leftshiftone/pdfscript","last_synced_at":"2026-01-14T03:15:14.638Z","repository":{"id":49868062,"uuid":"183316367","full_name":"leftshiftone/pdfscript","owner":"leftshiftone","description":"PDFScript is an open source software library for script based PDF generation.","archived":false,"fork":false,"pushed_at":"2021-06-09T14:14:52.000Z","size":16439,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2023-07-05T16:42:49.920Z","etag":null,"topics":["pdf-generation","pdfbox"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leftshiftone.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}},"created_at":"2019-04-24T22:37:34.000Z","updated_at":"2022-07-21T16:24:10.000Z","dependencies_parsed_at":"2022-08-19T03:21:04.522Z","dependency_job_id":null,"html_url":"https://github.com/leftshiftone/pdfscript","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"purl":"pkg:github/leftshiftone/pdfscript","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leftshiftone%2Fpdfscript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leftshiftone%2Fpdfscript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leftshiftone%2Fpdfscript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leftshiftone%2Fpdfscript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leftshiftone","download_url":"https://codeload.github.com/leftshiftone/pdfscript/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leftshiftone%2Fpdfscript/sbom","scorecard":{"id":583048,"data":{"date":"2025-08-11","repo":{"name":"github.com/leftshiftone/pdfscript","commit":"b29c80438466e9f057efc686153d3e23f2aface1"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: 'allow deletion' enabled on branch 'master'","Warn: 'force pushes' enabled on branch 'master'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'master'","Warn: could not determine whether codeowners review is allowed","Warn: no status checks found to merge onto branch 'master'","Warn: PRs are not required to make changes on branch 'master'; or we don't have data to detect it.If you think it might be the latter, make sure to run Scorecard with a PAT or use Repo Rules (that are always public) instead of Branch Protection settings"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T19:41:33.050Z","repository_id":49868062,"created_at":"2025-08-20T19:41:33.050Z","updated_at":"2025-08-20T19:41:33.050Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408823,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["pdf-generation","pdfbox"],"created_at":"2026-01-14T03:15:14.043Z","updated_at":"2026-01-14T03:15:14.629Z","avatar_url":"https://github.com/leftshiftone.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CircleCI branch](https://img.shields.io/circleci/project/github/leftshiftone/pdfscript/master.svg?style=flat-square)](https://circleci.com/gh/leftshiftone/pdfscript)\n[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/leftshiftone/pdfscript.svg?style=flat-square)](https://github.com/leftshiftone/pdfscript/tags)\n[![Maven Central](https://img.shields.io/maven-central/v/one.leftshift.pdfscript/pdfscript?style=flat-square)](https://mvnrepository.com/artifact/one.leftshift.pdfscript/pdfscript)\n\n\n# PDFScript\n\nPDFScript is an open source software library for script based PDF generation\nusing a rendering evaluation graph. The graph nodes are used to evaluate the\nbounding boxes of each renderable before it gets rendered. The evaluation \ngraphs enables **PDFScript** to auto-adjust the renderables within the boundaries of a page.\n\nAvailable in [jcenter](https://bintray.com/leftshiftone/pdfscript/one.leftshift.pdfscript.pdfscript/_latestVersion). Can be included like `compile 'one.leftshift.pdfscript:pdfscript:0.25.0'`.\n\n## Quickstart\n\nA PDF script is initiated by calling one of the static methods e.g. dinA4.\nThe static method call opens a new PDFScriptStream, which is used to create\nan evaluation graph. By calling **execute** on the script stream, the PDF\npages gets rendered and the PDF is returned as a byte array.\n\n```\ndinA4 { text(\"Hello World\") }.execute()\n``` \n\nA PDFScriptStream automatically takes care of the boundaries of a PDF page format (e.g. dinA4).\nIf a row or column overflows the available space, the text automatically wraps to the available space.\n\nFor example the following script creates an evaluation graph which leads to multiple lines.\n```\ndinA4 { \n    text(\"Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\") \n}\n``` \n\n## Header and Footer\n\nBy using the **withHeader** and **withFooter** function, it is possible to create a header and/or footer content for each page.\nPDFScript automatically takes care of the page format boundary adjustments. \n\n```\ndinA4 { \n    withHeader {\n        text(\"Header Text\")\n    }\n    withFooter {\n        text(\"Footer Text\")\n    }\n}\n``` \n\n## Table\n\nPDFScript provides a flexible way of table rendering. By default, PDFScript auto adjusts each\ncolumn by giving each column the same amount of space.\n\n```\ndinA4 { \n    table {\n        row {\n            col { text(\"Column 1\") }\n            col { text(\"Column 2\") }\n        }\n        row {\n            col { text(\"Column 3\") }\n            col { text(\"Column 4\") }\n        }\n    } \n}\n``` \n\nIn order to overwrite the default behaviour, it is possible to set the ratio of each column at the row level.\nA ratio setting e.g. `ratio(40, 60)` means, that the first column has a width of 40% while the second column\nhas a width of 60%. \n\n```\ndinA4 { \n    table {\n        row({ratio(40, 60)}) {\n            col { text(\"Column 1\") }\n            col { text(\"Column 2\") }\n        }\n        row({ratio(60, 40)}) {\n            col { text(\"Column 3\") }\n            col { text(\"Column 4\") }\n        }\n    } \n}\n``` \n\n## Image\nPDFScript renders an jpg or png image by calling **image** with an image source, width and height argument.\nThe image source can be a url, a byte array or a supplier of an input stream. The supplier handling is necessary\nbecause of the possibility of multiple image render executions. For example an image within a header or footer\nis rendered for each page. \n\n```\nimage(\"https://example.com/image.jpg\", 150, 100)\nimage(getByteArray(), 150, 100)\nimage({getInputStream()}, 150, 100)\n```\n\n## Svg\nAnalogous to the **image** call the **svg** method lets define a svg renderable. The given svg image\ngets encoded to a png image on the fly.\n\n```\nsvg(\"https://example.com/image.svg\", 150, 100)\nsvg(getByteArray(), 150, 100)\nsvg({getInputStream()}, 150, 100)\n```\n\n## Tabulator\nBy calling the **tab** renderable a tabulator gets rendered. By default, the tabulator space\nis set to 100 points. The tabulator space value can be set by specifying the amount at the **tab** call.  \n\n```\ntext(\"A\")\ntab()\ntext(\"B\")\ntab(200)\n```\n\n## Subscript and Superscript\nSubscript and superscript renderables are elements that are set slightly below or above the normal line of text. \nThey are smaller than the standard text and appear either below the baseline (subscript) or above the baseline (superscript).\n\n```\nsuperscript(\"A\")\ntext(\"B\")\nsubscript(\"A\")\n```\n\n## Paragraph\nA paragraph groups multiple renderables and separates them from other elements by a newline before and after the paragraph.\n```\nparagraph {\n   text(\"A\")\n   text(\"B\")\n   text(\"C\")\n}\nparagraph {\n   text(\"D\")\n   text(\"E\")\n   text(\"F\")\n}\n```\n\n## Bold\nBy using the bold renderable a text can be styled in a bold manner. The specific font is selected automatically.\nWhen using a non standard font the bold style font have to be registered.\n```\nval font1 = loadFont(document, \"/SpecialFont-Regular.ttf\")\nval font2 = loadFont(document, \"/SpecialFont-Bold.ttf\")\n\nval fontProvider = FontProvider()\nfontProvider.addFont(font1)\nfontProvider.addFont(font2)\n\ndinA4({ font(font1) }, fontProvider) {\n   text(\"A\")\n   bold(\"B\")\n   text(\"C\")\n}\n```\n\n## Canvas\nA canvas supports to draw elements on an absolute position without adjusting the current position of the element flow.\nSo by the use of a canvas it is possible to draw elements in a free way onto the pdf.\n\nEach function within the *withCanvas* block accepts the x and y coordinates on the pdf. While the x coordinate starts\non the left of the page, the y coordinate starts on the bottom. A negative x value can be used to start on the right\nand a negative y value can be used to start from the top.\n\n```\ndinA4 {\n   withCanvas {\n      drawCircle(0, -100, 5)\n      drawLine(0, -200, 10, -200)\n      drawRect(0, -300, 10, 10)\n      drawSvg(\"/image.svg\", -400, 0, 0.25)\n      drawImage(\"/image.jpg\", 100, 200, -400, -200)\n      drawText(\"text\", -500, 0, 0.25)\n   }\n}\n```\n\n## Page and Pages\nWithin a **text** renderable it is possible to render the current page number as well as the total amount of pages.\nTo do this, simple add either the **{{page}}** and/or **{pages}}** expression within the text string.\nAlternatively the variables can be added by the expressions **#PAGE** and **#PAGES**.\n\n```\ntext(\"Page {{page}} of {{pages}}\")\ntext(\"Page #PAGE of #PAGES\")\n```\n\n## Styling\nPDFScript allows the styling of each renderable by applying a **styler** function as the first parameter.\n\n```\nparagraph({paddingTop(5); paddingBottom(5)}) {\n    text(\"B\")\n}\n```\n\n| name          | description                                                    | values         \n|---------------|----------------------------------------------------------------|----------------\n| font          | the font to use                                                | [font qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/font.properties \"Font Qualifiers\")\n| fontName      | the name of the font to use                                    | [font qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/font.properties \"Font Qualifiers\")\n| fontSize      | the size of the font to use                                    | number\n| foreground    | the color which is used to render text and line elements       | hex code or [color qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/color.properties \"Color Qualifiers\")\n| background    | the color which is used to render element backgrounds          | hex code or [color qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/color.properties \"Color Qualifiers\")\n| border        | determines if and how the borders should be rendered           | \"none\", hex code or [color qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/color.properties \"Color Qualifiers\")\n| borderBottom  | determines if and how the bottom border should be rendered     | \"none\", hex code or [color qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/color.properties \"Color Qualifiers\")\n| borderTop     | determines if and how the top border should be rendered        | \"none\", hex code or [color qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/color.properties \"Color Qualifiers\")\n| borderLeft    | determines if and how the left border should be rendered       | \"none\", hex code or [color qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/color.properties \"Color Qualifiers\")\n| borderRight   | determines if and how the right border should be rendered      | \"none\", hex code or [color qualifier](https://github.com/leftshiftone/pdfscript/blob/master/src/main/resources/color.properties \"Color Qualifiers\")\n| paddingTop    | determines the top padding                                     | number\n| paddingBottom | determines the bottom padding                                  | number\n| align         | indicates the text alignment                                   | \"left\", \"center\", \"right\"\n| ratio         | the ratio of the row columns                                   | number varargs\n\n## Font\nPDFScript offers a convenient way to embed a font in a pdf document.\n\n```\nprivate fun loadFont(document: PDDocument, path:String): PDFont {\n   val fontStream = TextTest::class.java.getResourceAsStream(path)\n   return PDType0Font.load(document, fontStream)\n}\n\nval document = PDDocument()\nval font = loadFont(document, \"/customFont.ttf\")\n\nval interceptor = RawCommandsInterceptor()\ndinA4({ font(font) }) { text(\"č\") }.execute(interceptor, document)\n```\n\nSometimes it is necessary to have a fallback font, if glyphs were used which are not supported by the current font.\nFor this case, a PDFontResolver can be used in order to build a font chain.\n\n```\nprivate fun loadFont(document: PDDocument, path:String): PDFont {\n   val fontStream = TextTest::class.java.getResourceAsStream(path)\n   return PDType0Font.load(document, fontStream)\n}\n\nval document = PDDocument()\nval font1 = loadFont(document, \"/customFont1.ttf\")\nval font2 = loadFont(document, \"/customFont2.ttf\")\n\nval interceptor = RawCommandsInterceptor()\ndinA4({ font(PDFontResolver(font1, font2)) }) { text(\"\\u0627\") }.execute(interceptor, document)\n```\n\n## Interceptor\nThe PDFScript execute method accepts an interceptor instance which can be used to hook into the events of the\nPDF generation.\n```\nval interceptor = RawCommandsInterceptor()\ndinA4 { text(\"Hello World\") }.execute(interceptor)\nprint(interceptor.commands)\n```\n\n## Unit Testing\nPDFScript supports pixel perfect PDF rendering unit tests by using the **RawCommandsInterceptor**.\nThe **RawCommandsInterceptor** collects the raw PDF instructions so that a unit test simple asserts the\nactual raw commands with the expected commands. (collected from a previous pdf rendering run)\n\n```\n@Test\nfun `create a superscript text`() {\n    val interceptor = RawCommandsInterceptor()\n    dinA4 {\n        text(\"text\")\n        superscript(\"superscript\")\n    }.execute(interceptor)\n\n    val expected = listOf(\n            \"setFont[Helvetica, 12.0]\",\n            \"beginText:[]\",\n            \"newLineAtOffset:[70.86614, 758.7796]\",\n            \"showText:[text ]\",\n            \"endText:[]\",\n            \"setFont[Helvetica, 7.2000003]\",\n            \"beginText:[]\",\n            \"newLineAtOffset:[93.54614, 764.2796]\",\n            \"showText:[superscript]\",\n            \"endText:[]\",\n            \"setFont[Helvetica, 12.0]\"\n    )\n    assertEquals(expected, interceptor.commands)\n}\n```\n\n## Native PDF reader/writer\nPDFScript is supporting the native handling of  PDF elements by using one of the native reader/writer classes.\nThe PdfTextReader for example extracts all text elements together with the corresponding bounding boxes from a pdf file\nwhile the PdfTextWriter creates a new pdf file by a list of pdfText bounding boxes.\n\n```\nval elements = PdfTextReader().read(\"/pdf/result.pdf\")\nval pdfBytes = PdfTextWriter().write(elements)\n```\n\n## Development\n\n### Release\nReleases are triggered locally. Just a tag will be pushed and CI pipelines take care of the rest.\n\n#### Major\nRun `./gradlew final -x sendReleaseEmail -Prelease.scope=major` locally.\n\n#### Minor\nRun `./gradlew final -x sendReleaseEmail -Prelease.scope=minor` locally.\n\n#### Patch\nRun `./gradlew final -x sendReleaseEmail -Prelease.scope=patch` locally.\n\n#### Upcoming Features\n\n* Footnotes support\n* Endnotes support\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleftshiftone%2Fpdfscript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleftshiftone%2Fpdfscript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleftshiftone%2Fpdfscript/lists"}