{"id":13439590,"url":"https://github.com/fschutt/printpdf","last_synced_at":"2025-05-13T21:10:42.666Z","repository":{"id":41310600,"uuid":"90769108","full_name":"fschutt/printpdf","owner":"fschutt","description":"Rust / WASM library for reading, writing, templating and rendering PDF","archived":false,"fork":false,"pushed_at":"2025-04-28T12:55:19.000Z","size":40481,"stargazers_count":925,"open_issues_count":15,"forks_count":110,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-05-07T05:57:00.811Z","etag":null,"topics":["html2pdf","pdf","pdf-generation","pdf-reader","pdf-renderer","rust","rust-library","wasm"],"latest_commit_sha":null,"homepage":"https://fschutt.github.io/printpdf/","language":"Rust","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/fschutt.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null},"funding":{"github":"fschutt"}},"created_at":"2017-05-09T16:42:09.000Z","updated_at":"2025-04-30T16:54:03.000Z","dependencies_parsed_at":"2024-01-20T00:25:28.414Z","dependency_job_id":"d64249fd-ccbb-4469-9ef3-112e917f226c","html_url":"https://github.com/fschutt/printpdf","commit_stats":{"total_commits":353,"total_committers":36,"mean_commits":9.805555555555555,"dds":"0.30594900849858353","last_synced_commit":"e78b284741a6a0372a35272958e7dfa93ce9569c"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fprintpdf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fprintpdf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fprintpdf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschutt%2Fprintpdf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fschutt","download_url":"https://codeload.github.com/fschutt/printpdf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252823705,"owners_count":21809709,"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":["html2pdf","pdf","pdf-generation","pdf-reader","pdf-renderer","rust","rust-library","wasm"],"created_at":"2024-07-31T03:01:15.384Z","updated_at":"2025-05-13T21:10:37.650Z","avatar_url":"https://github.com/fschutt.png","language":"Rust","readme":"﻿# printpdf\n\n[![CI](https://github.com/fschutt/printpdf/actions/workflows/ci.yaml/badge.svg)](https://github.com/fschutt/printpdf/actions/workflows/ci.yaml)\n[![Dependencies](https://deps.rs/repo/github/fschutt/printpdf/status.svg)](https://deps.rs/repo/github/fschutt/printpdf)\n\n`printpdf` is a Rust library for generating, reading (!), rendering (!!) and optimizing PDF documents.\n\n[Website](https://fschutt.github.io/printpdf) | [Crates.io](https://crates.io/crates/printpdf) | [Documentation](https://docs.rs/printpdf) | [Donate](https://github.com/sponsors/fschutt)\n\n\u003e [!IMPORTANT]  \n\u003e HTML-to-PDF rendering is still experimental and WIP. \n\u003e In doubt, position PDF elements manually instead.\n\n## Features\n\n- Pages, Bookmarks, link annotations (read / write)\n- Layers (read / write)\n- Graphics: lines, shapes, bezier curves, SVG content (read / write)\n- Images encoding / decoding (read support: experimental / write supported with `image`)\n- Embedded fonts, Unicode support (read support: experimental / write)\n- Minifying file size (auto-subsetting fonts)\n- Rendering PDF pages to SVG files: experimental\n- Extracting text from PDF pages (with auto-decoding Unicode, line breaks, text positions: experimental)\n- Minimal HTML layouting for simple page layout (using `azul-layout` + `kuchiki` HTML parser)\n- Advanced graphics - overprint, blending, etc.\n- Advanced typography - character / word scaling and spacing, superscript, subscript, etc.\n- Embedding SVGs (uses `svg2pdf` crate internally)\n\nNOT supported are:\n\n- Gradients\n- Patterns\n- File attachements\n- Open Prepress Interface\n- Halftoning images\n- Conformance / error checking for various PDF standards\n- Embedded Javascript\n\nSee the WASM32 demo live at: https://fschutt.github.io/printpdf\n\n## Writing PDF\n\n### Basic example\n\n```rust\nuse printpdf::*;\n\nfn main() {\n    let mut doc = PdfDocument::new(\"My first PDF\");\n    let page1_contents = vec![Op::Marker { id: \"debugging-marker\".to_string() }];\n    let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);\n    let pdf_bytes: Vec\u003cu8\u003e = doc\n        .with_pages(vec![page1])\n        .save(\u0026PdfSaveOptions::default());\n}\n```\n\n### Graphics\n\n```rust\nuse printpdf::*;\n\nfn main() {\n    let mut doc = PdfDocument::new(\"My first PDF\");\n\n    let line = Line {\n        // Quadratic shape. The \"false\" determines if the next (following)\n        // point is a bezier handle (for curves)\n        // If you want holes, simply reorder the winding of the points to be\n        // counterclockwise instead of clockwise.\n        points: vec![\n            (Point::new(Mm(100.0), Mm(100.0)), false),\n            (Point::new(Mm(100.0), Mm(200.0)), false),\n            (Point::new(Mm(300.0), Mm(200.0)), false),\n            (Point::new(Mm(300.0), Mm(100.0)), false),\n        ],\n        is_closed: true,\n    };\n    \n    // Triangle shape\n    let polygon = Polygon {\n        rings: vec![vec![\n            (Point::new(Mm(150.0), Mm(150.0)), false),\n            (Point::new(Mm(150.0), Mm(250.0)), false),\n            (Point::new(Mm(350.0), Mm(250.0)), false),\n        ]],\n        mode: PaintMode::FillStroke,\n        winding_order: WindingOrder::NonZero,\n    };\n    \n    // Graphics config\n    let fill_color = Color::Cmyk(Cmyk::new(0.0, 0.23, 0.0, 0.0, None));\n    let outline_color = Color::Rgb(Rgb::new(0.75, 1.0, 0.64, None));\n    let mut dash_pattern = LineDashPattern::default();\n    dash_pattern.dash_1 = Some(20);\n\n    let extgstate = ExtendedGraphicsStateBuilder::new()\n        .with_overprint_stroke(true)\n        .with_blend_mode(BlendMode::multiply())\n        .build();\n    \n    let page1_contents = vec![\n        // add line1 (square)\n        Op::SetOutlineColor { col: Color::Rgb(Rgb::new(0.75, 1.0, 0.64, None)) },\n        Op::SetOutlineThickness { pt: Pt(10.0) },\n        Op::DrawLine { line: line },\n    \n        // add line2 (triangle)\n        Op::SaveGraphicsState,\n        Op::LoadGraphicsState { gs: doc.add_graphics_state(extgstate) },\n        Op::SetLineDashPattern { dash: dash_pattern },\n        Op::SetLineJoinStyle { join: LineJoinStyle::Round },\n        Op::SetLineCapStyle { cap: LineCapStyle::Round },\n        Op::SetFillColor { col: fill_color },\n        Op::SetOutlineThickness { pt: Pt(15.0) },\n        Op::SetOutlineColor { col: outline_color },\n        Op::DrawPolygon { polygon: polygon },\n        Op::RestoreGraphicsState,\n    ];\n    \n    let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);\n    let pdf_bytes: Vec\u003cu8\u003e = doc\n        .with_pages(vec![page1])\n        .save(\u0026PdfSaveOptions::default());\n}\n```\n\n### Images\n\n- Images only get compressed in release mode. You might get huge PDFs (6 or more MB) in debug mode.\n-  To make this process faster, use `BufReader` instead of directly reading from the file.\n- Scaling of images is implicitly done to fit one pixel = one dot at 300 dpi.\n\n```rust\nuse printpdf::*;\n\nfn main() {\n    let mut doc = PdfDocument::new(\"My first PDF\");\n    let image_bytes = include_bytes!(\"assets/img/BMP_test.bmp\");\n    let image = RawImage::decode_from_bytes(image_bytes).unwrap(); // requires --feature bmp\n    \n    // In the PDF, an image is an `XObject`, identified by a unique `ImageId`\n    let image_xobject_id = doc.add_image(image);\n\n    let page1_contents = vec![\n        Op::UseXobject { \n            id: image_xobject_id.clone(), \n            transform: XObjectTransform::default() \n        }\n    ];\n\n    let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);\n    let pdf_bytes: Vec\u003cu8\u003e = doc\n        .with_pages(vec![page1])\n        .save(\u0026PdfSaveOptions::default());\n}\n```\n\n### Fonts\n\n```rust\nuse printpdf::*;\n\nfn main() {\n    let mut doc = PdfDocument::new(\"My first PDF\");\n\n    let roboto_bytes = include_bytes!(\"assets/fonts/RobotoMedium.ttf\").unwrap()\n    let font_index = 0;\n    let mut warnings = Vec::new();\n    let font = ParsedFont::from_bytes(\u0026roboto_bytes, font_index, \u0026mut warnings).unwrap();\n\n    // If you need custom text shaping (uses the `allsorts` font shaper internally)\n    // let glyphs = font.shape(text);\n\n    // printpdf automatically keeps track of which fonts are used in the PDF\n    let font_id = doc.add_font(\u0026font);\n\n    let text_pos = Point {\n        x: Mm(10.0).into(),\n        y: Mm(100.0).into(),\n    }; // from bottom left\n\n    let page1_contents = vec![\n        Op::SetLineHeight { lh: Pt(33.0) },\n        Op::SetWordSpacing { pt: Pt(33.0) },\n        Op::SetCharacterSpacing { multiplier: 10.0 },\n        Op::SetTextCursor { pos: text_pos },\n        // Op::WriteCodepoints { ... }\n        // Op::WriteCodepointsWithKerning { ... }\n        Op::WriteText {\n            items: vec![TextItem::Text(\"Lorem ipsum\".to_string())],\n            font: font_id.clone(),\n        },\n        Op::AddLineBreak,\n        Op::WriteText {\n            items: vec![TextItem::Text(\"dolor sit amet\".to_string())],\n            font: font_id.clone(),\n        },\n        Op::AddLineBreak,\n    ];\n\n    let save_options = PdfSaveOptions {\n        subset_fonts: true, // auto-subset fonts on save\n        ..Default::default()\n    };\n\n    let page1 = PdfPage::new(Mm(10.0), Mm(250.0), page1_contents);\n    let mut warnings = Vec::new();\n    let pdf_bytes: Vec\u003cu8\u003e = doc\n        .with_pages(vec![page1])\n        .save(\u0026save_options, \u0026mut warnings);\n}\n```\n\n### Tables, HTML\n\nFor creating tables, etc. printpdf uses a basic layout system, similar to wkhtmltopdf \n(although more limited in terms of features). It's good enough for basic page layouting, \nbook rendering and reports / forms / etc. Includes automatic page-breaking.\n\nSince printpdf supports WASM, there is an interactive demo at \nhttps://fschutt.github.io/printpdf - try playing with the XML.\n\nSee [SYNTAX.md](./SYNTAX.md) for the XML syntax description.\n\n```rust\n// needs --features=\"html\"\nuse printpdf::*;\n\nfn main() {\n\n    // See https://fschutt.github.io/printpdf for an interactive WASM demo!\n\n    let html = r#\"\n    \u003chtml\u003e\n\n        \u003c!-- printpdf automatically breaks content into pages --\u003e\n        \u003cbody style=\"padding:10mm\"\u003e\n            \u003cp style=\"color: red; font-family: sans-serif;\" data-chapter=\"1\" data-subsection=\"First subsection\"\u003eHello!\u003c/p\u003e\n            \u003cdiv style=\"width:200px;height:200px;background:red;\" data-chapter=\"1\" data-subsection=\"Second subsection\"\u003e\n                \u003cp\u003eWorld!\u003c/p\u003e\n            \u003c/div\u003e\n        \u003c/body\u003e\n\n        \u003c!-- configure header and footer for each page --\u003e\n        \u003chead\u003e\n            \u003cheader\u003e\n                \u003ch4 style=\"color: #2e2e2e;min-height: 8mm;\"\u003eChapter {attr:chapter} * {attr:subsection}\u003c/h4\u003e\n                \u003cp style=\"position: absolute;top:5mm;left:5mm;\"\u003e{builtin:pagenum}\u003c/p\u003e\n            \u003c/header\u003e\n\n            \u003cfooter\u003e\n                \u003chr/\u003e\n            \u003cfooter/\u003e\n        \u003c/head\u003e\n    \u003c/html\u003e\n    \"#;\n\n    let options = XmlRenderOptions {\n        // named images to be used in the HTML, i.e. [\"image1.png\" =\u003e DecodedImage(image1_bytes)]\n        images: BTreeMap::new(),\n        // named fonts to be used in the HTML, i.e. [\"Roboto\" =\u003e DecodedImage(roboto_bytes)]\n        fonts: BTreeMap::new(),\n        // default page width, printpdf will auto-page-break\n        page_width: Mm(210.0),\n        // default page height\n        page_height: Mm(297.0),\n    };\n\n    let pdf_bytes = PdfDocument::new(\"My PDF\")\n        .with_html(html, \u0026options).unwrap()\n        .save(\u0026PdfSaveOptions::default());\n}\n```\n\n## Roadmap\n\nThe goal of printpdf is to be a general-use PDF library, such as\nlibharu or similar. PDFs generated by printpdf should always adhere\nto a PDF standard, except if you turn it off. Currently, only the\nstandard `PDF/X-3:2002` is covered (i.e. valid PDF according to Adobe\nAcrobat). Over time, there will be more standards supported.\n\nThe printpdf wiki is live at: https://github.com/fschutt/printpdf/wiki\n\nHere are some resources I found while working on this library:\n\n- [`PDFXPlorer`, shows the DOM tree of a PDF, needs .NET 2.0](http://www.o2sol.com/pdfxplorer/download.htm)\n- [Official PDF 1.7 reference](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.7old.pdf)\n- [\\[GERMAN\\] How to embed unicode fonts in PDF](http://www.p2501.ch/pdf-howto/typographie/vollzugriff/direkt)\n- [PDF X/1-a Validator](https://www.pdf-online.com/osa/validate.aspx)\n- [PDF X/3 technical notes](http://www.pdfxreport.com/lib/exe/fetch.php?media=en:technote_pdfx_checks.pdf)\n\n## License / Support\n\nLibrary is licensed MIT.\n\nYou can donate (one-time or recurrent) at https://github.com/sponsors/fschutt. Thanks!\n","funding_links":["https://github.com/sponsors/fschutt"],"categories":["Libraries","库 Libraries","Rust","库"],"sub_categories":["Graphics","图形 Graphics","图像","图像 Graphics","Rust"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschutt%2Fprintpdf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffschutt%2Fprintpdf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschutt%2Fprintpdf/lists"}