An easy-to-use library for writing PDF in Rust

Overview

printpdf

Travis CI Appveyor

printpdf is a library designed for creating printable PDF documents.

Crates.io | Documentation

[dependencies]
printpdf = "0.3.2"

Features

Currently, printpdf can only create new documents and write them, it cannot load existing documents yet.

  • Page generation
  • Layers (Illustrator like layers)
  • Graphics (lines, shapes, bezier curves)
  • Images (currently BMP/JPG/PNG only or generate your own images)
  • Embedded fonts (TTF and OTF) with Unicode support
  • Advanced graphics - overprint control, blending modes, etc.
  • Advanced typography - character scaling, character spacing, superscript, subscript, outlining, etc.
  • PDF layers (you should be able to open the PDF in Illustrator and have the layers appear)

Note: printpdf only implements the PDF spec, nothing more. If you more high-level PDF generation, take a look at genpdf, which is built on top of printpdf

Getting started

Writing PDF

Simple page

use printpdf::*;
use std::fs::File;
use std::io::BufWriter;

let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
let (page2, layer1) = doc.add_page(Mm(10.0), Mm(250.0),"Page 2, Layer 1");

doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap();

Adding graphical shapes

use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
use std::iter::FromIterator;

let (doc, page1, layer1) = PdfDocument::new("printpdf graphics test", Mm(297.0), Mm(210.0), "Layer 1");
let current_layer = doc.get_page(page1).get_layer(layer1);

// Quadratic shape. The "false" determines if the next (following)
// point is a bezier handle (for curves)
// If you want holes, simply reorder the winding of the points to be
// counterclockwise instead of clockwise.
let points1 = vec![(Point::new(Mm(100.0), Mm(100.0)), false),
                   (Point::new(Mm(100.0), Mm(200.0)), false),
                   (Point::new(Mm(300.0), Mm(200.0)), false),
                   (Point::new(Mm(300.0), Mm(100.0)), false)];

// Is the shape stroked? Is the shape closed? Is the shape filled?
let line1 = Line {
    points: points1,
    is_closed: true,
    has_fill: true,
    has_stroke: true,
    is_clipping_path: false,
};

// Triangle shape
// Note: Line is invisible by default, the previous method of
// constructing a line is recommended!
let mut line2 = Line::from_iter(vec![
    (Point::new(Mm(150.0), Mm(150.0)), false),
    (Point::new(Mm(150.0), Mm(250.0)), false),
    (Point::new(Mm(350.0), Mm(250.0)), false)]);

line2.set_stroke(true);
line2.set_closed(false);
line2.set_fill(false);
line2.set_as_clipping_path(false);

let fill_color = Color::Cmyk(Cmyk::new(0.0, 0.23, 0.0, 0.0, None));
let outline_color = Color::Rgb(Rgb::new(0.75, 1.0, 0.64, None));
let mut dash_pattern = LineDashPattern::default();
dash_pattern.dash_1 = Some(20);

current_layer.set_fill_color(fill_color);
current_layer.set_outline_color(outline_color);
current_layer.set_outline_thickness(10.0);

// Draw first line
current_layer.add_shape(line1);

let fill_color_2 = Color::Cmyk(Cmyk::new(0.0, 0.0, 0.0, 0.0, None));
let outline_color_2 = Color::Greyscale(Greyscale::new(0.45, None));

// More advanced graphical options
current_layer.set_overprint_stroke(true);
current_layer.set_blend_mode(BlendMode::Seperable(SeperableBlendMode::Multiply));
current_layer.set_line_dash_pattern(dash_pattern);
current_layer.set_line_cap_style(LineCapStyle::Round);

current_layer.set_fill_color(fill_color_2);
current_layer.set_outline_color(outline_color_2);
current_layer.set_outline_thickness(15.0);

// draw second line
current_layer.add_shape(line2);

Adding images

Note: Images only get compressed in release mode. You might get huge PDFs (6 or more MB) in debug mode. In release mode, the compression makes these files much smaller (~ 100 - 200 KB).

To make this process faster, use BufReader instead of directly reading from the file. Images are currently not a top priority.

Scaling of images is implicitly done to fit one pixel = one dot at 300 dpi.

extern crate printpdf;

// imports the `image` library with the exact version that we are using
use printpdf::*;

use std::convert::From;
use std::fs::File;

fn main() {
    let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
    let current_layer = doc.get_page(page1).get_layer(layer1);

    // currently, the only reliable file formats are bmp/jpeg/png
    // this is an issue of the image library, not a fault of printpdf
    let mut image_file = File::open("assets/img/BMP_test.bmp").unwrap();
    let image = Image::try_from(image::bmp::BmpDecoder::new(&mut image_file).unwrap()).unwrap();

    // translate x, translate y, rotate, scale x, scale y
    // by default, an image is optimized to 300 DPI (if scale is None)
    // rotations and translations are always in relation to the lower left corner
    image.add_to_layer(current_layer.clone(), None, None, None, None, None, None);

    // you can also construct images manually from your data:
    let mut image_file_2 = ImageXObject {
        width: Px(200),
        height: Px(200),
        color_space: ColorSpace::Greyscale,
        bits_per_component: ColorBits::Bit8,
        interpolate: true,
        /* put your bytes here. Make sure the total number of bytes =
           width * height * (bytes per component * number of components)
           (e.g. 2 (bytes) x 3 (colors) for RGB 16bit) */
        image_data: Vec::new(),
        image_filter: None, /* does not work yet */
        clipping_bbox: None, /* doesn't work either, untested */
    };

    let image2 = Image::from(image_file_2);
}

Adding fonts

Note: Fonts are shared between pages. This means that they are added to the document first and then a reference to this one object can be passed to multiple pages. This is different to images, for example, which can only be used once on the page they are created on (since that's the most common use-case).

use printpdf::*;
use std::fs::File;

let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
let current_layer = doc.get_page(page1).get_layer(layer1);

let text = "Lorem ipsum";
let text2 = "unicode: стуфхfцчшщъыьэюя";

let font = doc.add_external_font(File::open("assets/fonts/RobotoMedium.ttf").unwrap()).unwrap();
let font2 = doc.add_external_font(File::open("assets/fonts/RobotoMedium.ttf").unwrap()).unwrap();

// text, font size, x from left edge, y from bottom edge, font
current_layer.use_text(text, 48, Mm(200.0), Mm(200.0), &font);

// For more complex layout of text, you can use functions
// defined on the PdfLayerReference
// Make sure to wrap your commands
// in a `begin_text_section()` and `end_text_section()` wrapper
current_layer.begin_text_section();

    // setup the general fonts.
    // see the docs for these functions for details
    current_layer.set_font(&font2, 33);
    current_layer.set_text_cursor(Mm(10.0), Mm(10.0));
    current_layer.set_line_height(33);
    current_layer.set_word_spacing(3000);
    current_layer.set_character_spacing(10);
    current_layer.set_text_rendering_mode(TextRenderingMode::Stroke);

    // write two lines (one line break)
    current_layer.write_text(text.clone(), &font2);
    current_layer.add_line_break();
    current_layer.write_text(text2.clone(), &font2);
    current_layer.add_line_break();

    // write one line, but write text2 in superscript
    current_layer.write_text(text.clone(), &font2);
    current_layer.set_line_offset(10);
    current_layer.write_text(text2.clone(), &font2);

current_layer.end_text_section();

Optimiziation

Minimizing the size of generated PDFs

  • By default, the PDF adherese to a "PDF conformance level", usually the PDF-X 1.4 Standard. This means that the PDF includes a full ICC profile file (which is around 500KB large). To turn it off, see the no_icc example:
let (mut doc, _page1, _layer1) = PdfDocument::new("printpdf no_icc test", Mm(297.0), Mm(210.0), "Layer 1");
doc = doc.with_conformance(PdfConformance::Custom(CustomPdfConformance {
  requires_icc_profile: false,
  requires_xmp_metadata: false,
    .. Default::default()
}));
  • In debug mode, the images, streams and fonts are not compressed for easier debugging. Try building in release mode to optimize the size further.

Changelog

See the CHANGELOG.md file.

Further reading

The PdfDocument is hidden behind a PdfDocumentReference, which locks the things you can do behind a facade. Pretty much all functions operate on a PdfLayerReference, so that would be where to look for existing functions or where to implement new functions. The PdfDocumentReference is a reference-counted document. It uses the pages and layers for inner mutablility, because I ran into borrowing issues with the document. IMPORTANT: All functions that mutate the state of the document, "borrow" the document mutably for the duration of the function. It is important that you don't borrow the document twice (your program will crash if you do so). I have prevented this wherever possible, by making the document only public to the crate so you cannot lock it from outside of this library.

Images have to be added to the pages resources before using them. Meaning, you can only use an image on the page that you added it to. Otherwise, you may end up with a corrupt PDF.

Fonts are embedded using freetype. There is a rusttype branch in this repository, but rusttype does fails to get the height of an unscaled font correctly, so that's why you currently have to use freetype

Please report issues if you have any, especially if you see BorrowMut errors (they should not happen). Kerning is currently not done, because neither freetype nor rusttype can reliably read kerning data. However, "correct" kerning / placement requires a full font shaping engine, etc. This would be a completely different project.

For learning how a PDF is actually made, please read the wiki (currently not completely finished). When I began making this library, these resources were not available anywhere, so I hope to help other people with these topics. Reading the wiki is essential if you want to contribute to this library.

Goals and Roadmap

The goal of printpdf is to be a general-use PDF library, such as libharu or similar. PDFs generated by printpdf should always adhere to a PDF standard, except if you turn it off. Currently, only the standard PDF/X-3:2002 is covered (i.e. valid PDF according to Adobe Acrobat). Over time, there will be more standards supported. Checking a PDF for errors is currently only a stub.

Planned features / Not done yet

The following features aren't implemented yet, most

  • Clipping
  • Aligning / layouting text
  • Open Prepress Interface
  • Halftoning images, Gradients, Patterns
  • SVG / instantiated content
  • Forms, annotations
  • Bookmarks / Table of contents
  • Conformance / error checking for various PDF standards
  • Embedded Javascript
  • Reading PDF
  • Completion of printpdf wiki

Testing

Currently the testing is pretty much non-existent, because PDF is very hard to test. This should change over time: Testing should be done in two stages. First, test the individual PDF objects, if the conversion into a PDF object is done correctly. The second stage is manual inspection of PDF objects via Adobe Preflight.

Put the tests of the first stage in /tests/mod.rs. The second stage tests are better to be handled inside the plugins' mod.rs file. printpdf depends highly on lopdf, so you can either construct your test object against a real type or a debug string of your serialized type. Either way is fine - you just have to check that the test object is conform to what PDF expects.

Useful links

Here are some resources I found while working on this library:

PDFXPlorer, shows the DOM tree of a PDF, needs .NET 2.0

Official PDF 1.7 reference

[GERMAN] How to embed unicode fonts in PDF

PDF X/1-a Validator

PDF X/3 technical notes

Donate

  • Bitcoin: 3DkYz32P77Bfv93wPgV66vs1vrUwgStcZU
  • Bitcoin Cash: 1QAi8xVB4nRtkaxTAXbzGHmg6nxuuezuYk
  • Ethereum: 0xb9960F9970b659056B03CB0241490aDA83A73CEa
Comments
  • image.add_to_layer() example breaks with newer versions of `image`

    image.add_to_layer() example breaks with newer versions of `image`

    The docs claim that this crate depends on image ^0.23 and ships with it but running the code example produces this error:

    extern crate printpdf;
    
    use printpdf::*;
    use std::io::Cursor;
    use image_crate::bmp::BmpDecoder;
    use std::fs::File;
    use std::io::BufWriter;
    
    fn main() {
        let (doc, page1, layer1) = PdfDocument::new("printpdf graphics test", Mm(210.0), Mm(297.0), "Layer 1");
        let current_layer = doc.get_page(page1).get_layer(layer1);
    
        // currently, the only reliable file formats are bmp/jpeg/png
        // this is an issue of the image library, not a fault of printpdf
    
        let image_bytes = include_bytes!("../assets/img/BMP_test.bmp");
        let mut reader = Cursor::new(image_bytes.as_ref());
    
        let decoder = BmpDecoder::new(&mut reader).unwrap();
        let image = Image::try_from(decoder).unwrap();
    
        let rotation_center_x = Px((image.image.width.0 as f64 / 2.0) as usize);
        let rotation_center_y = Px((image.image.height.0 as f64 / 2.0) as usize);
    
        // layer,
        image.add_to_layer(current_layer.clone(), ImageTransform {
            rotate: Some(ImageRotation {
                angle_ccw_degrees: 45.0,
                rotation_center_x,
                rotation_center_y,
            }),
            translate_x: Some(Mm(10.0)),
            translate_y: Some(Mm(10.0)),
            .. Default::default()
        });
    
        doc.save(&mut BufWriter::new(File::create("test_image.pdf").unwrap())).unwrap();
    }
    

    Error:

    error[E0433]: failed to resolve: use of undeclared crate or module `image_crate`
     --> src/main.rs:5:5
      |
    5 | use image_crate::bmp::BmpDecoder;
      |     ^^^^^^^^^^^ use of undeclared crate or module `image_crate`
    

    The image crate no longer has image::bmp::BmpDecoder. It has been renamed to image::codecs::bmp::BmpDecoder. Okay, let's add the image crate manually and try again:

    Cargo.toml:

    [package]
    name = "printpdf"
    version = "0.1.0"
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    printpdf = "0.5.0"
    image = "0.23.0"
    

    main.rs:

    extern crate printpdf;
    
    use printpdf::{ImageRotation, ImageTransform, Mm, Px, Image, PdfDocument};
    use image::codecs::bmp::BmpDecoder;
    use std::io::Cursor;
    use std::fs::File;
    use std::io::BufWriter;
    
    fn main() {
        let (doc, page1, layer1) = PdfDocument::new("printpdf graphics test", Mm(210.0), Mm(297.0), "Layer 1");
        let current_layer = doc.get_page(page1).get_layer(layer1);
    
        // currently, the only reliable file formats are bmp/jpeg/png
        // this is an issue of the image library, not a fault of printpdf
    
        let image_bytes = include_bytes!("../assets/img/BMP_test.bmp");
        let mut reader = Cursor::new(image_bytes.as_ref());
    
        let decoder = BmpDecoder::new(&mut reader).unwrap();
        let image = Image::try_from(decoder).unwrap();
    
        let rotation_center_x = Px((image.image.width.0 as f64 / 2.0) as usize);
        let rotation_center_y = Px((image.image.height.0 as f64 / 2.0) as usize);
    
        // layer,
        image.add_to_layer(current_layer.clone(), ImageTransform {
            rotate: Some(ImageRotation {
                angle_ccw_degrees: 45.0,
                rotation_center_x,
                rotation_center_y,
            }),
            translate_x: Some(Mm(10.0)),
            translate_y: Some(Mm(10.0)),
            .. Default::default()
        });
    
        doc.save(&mut BufWriter::new(File::create("test_image.pdf").unwrap())).unwrap();
    }
    

    Error:

    error[E0277]: the trait bound `printpdf::Image: From<BmpDecoder<&mut std::io::Cursor<_>>>` is not satisfied
      --> src/main.rs:20:17
       |
    20 |     let image = Image::try_from(decoder).unwrap();
       |                 ^^^^^^^^^^^^^^^ the trait `From<BmpDecoder<&mut std::io::Cursor<_>>>` is not implemented for `printpdf::Image`
       |
       = help: the trait `From<ImageXObject>` is implemented for `printpdf::Image`
       = note: required because of the requirements on the impl of `Into<printpdf::Image>` for `BmpDecoder<&mut std::io::Cursor<_>>`
       = note: required because of the requirements on the impl of `TryFrom<BmpDecoder<&mut std::io::Cursor<_>>>` for `printpdf::Image`
    
    For more information about this error, try `rustc --explain E0277`.
    
    opened by twiclo 12
  • Fonts is broken in firefox's pdf reader

    Fonts is broken in firefox's pdf reader

    I just did

    cargo run --example fonts
    

    and then

    firefox test_fonts.pdf
    

    and the file is broken.

    The same happens to us in production with external partner's pdf reader software.

    opened by TatriX 12
  • Status of the rusttype dependency

    Status of the rusttype dependency

    printpdf currently uses rusttype 0.8 that is no longer maintained. I experimented with updating rusttype to 0.9. It seems to work fine, but I noticed that ab_glyph is rusttype’s more efficient successor (by the same author). Then I noticed that printpdf doesn’t really need rusttype or ab_glyph because they mainly deal with font rendering and rasterization. Instead it could directly use ttf-parser.

    So my questions are:

    • Is there a reason to stick with rusttype 0.8?
    • If no, should printpdf
      • update to rusttype 0.9,
      • move to ab_glyph or
      • move to ttf-parser?
    opened by robinkrahl 8
  • Inserting image doesn't seem to work (on Windows)

    Inserting image doesn't seem to work (on Windows)

    Hi. I'm trying to generate a pdf and insert an image, but it simply doesn't seem to work. The code is almost just a copy/paste from the documentaion. I do not have a pure linux machine, but I have tested it on windows and the windows subsystem for linux. In both cases the code compiles, runs, reads an image and produces a pdf document - albeit a completely empty one.

    I have attached both the image I run the code with all well as the pdf I produce.

    pdf-img.zip qrcodes.pdf

    The code is located in a test and looks like this:

        #[tokio::test]
        async fn test_pdf() {
            let mut pdf_source = PathBuf::from(&env!("CARGO_MANIFEST_DIR").to_owned());
            pdf_source.push("out");
            pdf_source.push("codes");
            pdf_source.set_extension("pdf");
    
            let file = File::create(pdf_source).unwrap();
    
            log::debug!("Writing pdf to location: {:?}", file);
    
            let doc = make_pdf();
            let res = save_pdf(doc, file);
    
            assert!(res.is_ok());
        }
    
    pub fn make_pdf() -> PdfDocumentReference { // qrcodes: Vec<QrCode>
    	let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(210.0), Mm(297.0), "Layer 1");
        let current_layer = doc.get_page(page1).get_layer(layer1);
    
    	// currently, the only reliable file format is bmp (jpeg works, but not in release mode)
        // this is an issue of the image library, not a fault of printpdf
        
        //let mut image_file = File::open("C:\\Users\\arbo\\Google Drive\\qrgithub\\assets\\pdf-img.bmp").unwrap();
        //let image = Image::try_from(image::bmp::BmpDecoder::new(&mut image_file).unwrap()).unwrap();
        let image_bytes = include_bytes!("/mnt/c/Users/arbo/Google Drive/qrgithub/assets/pdf-img.bmp");
        let mut reader = Cursor::new(image_bytes.as_ref());
    
        let decoder = BmpDecoder::new(&mut reader).unwrap();
        let image2 = Image::try_from(decoder).unwrap();
    
        log::debug!("decoded image: {:?}", image2);
    
        // translate x, translate y, rotate, scale x, scale y
        // by default, an image is optimized to 300 DPI (if scale is None)
        // rotations and translations are always in relation to the lower left corner
        image2.add_to_layer(current_layer, Some(Mm(100.0)), Some(Mm(100.0)), None, None, None, None);
    
    	doc
    }
    
    pub fn save_pdf(doc: PdfDocumentReference, file: File) -> Result<(), Error> {
    	let res = doc.save(&mut BufWriter::new(file));
    
    	res
    }
    
    
    opened by bottiger 6
  • String break detection

    String break detection

    Hi,

    For a project I need to know the width of a string for a given font and font size, so I can split the string into multiple lines, typically when the string is much wider than the page.

    I checked the crate's documentation but I haven't found anything about it.

    If this feature doesn't exist in this crate or one of its dependencies is there anything about getting at least the width of a char. If not what would be the best approach to solve this issue.

    Thanks for the help.

    opened by jmendes92 5
  • feature request: bookmarks

    feature request: bookmarks

    Hi,

    Thanks for the awesome library. One feature that I would really like is bookmarks in the pdf that you can put on a page. This bookmark shows up on the left of the page in firefox and is accessible on the kindle very easily with the goto menu. (The kindle is my use case.)

    I do not know how simple this would be to implement, but it doesn't seem that hard.

    Thanks so much.

    for reference, here is how the feature looks in firefox (on the left there is a list of pages) image

    And if it helps, my use case is making a code renderer that can render source code for a project in a pdf file so it can be read on a kindle, so I would like it to be able to jump around quickly with bookmarks.

    Edit:

    Just saw this is in the TODOs. I could try to implement this if you could give me a little background, since I know very little about pdfs. I think there could be a function for a page called set_bookmark that just sets a bookmark on a page and accepts a &str for the name of the bookmark.

    opened by g-w1 4
  • BMP images don't show correctly in production

    BMP images don't show correctly in production

    If I run

    extern crate printpdf;
    
    fn main() {
        println!("Hello, world!");
        use printpdf::*;
        use std::fs::File;
        use std::io::{self, BufRead, BufWriter};
    
        let (doc, page1, layer1) = PdfDocument::new("printpdf graphics test", Mm(210.0), Mm(297.0), "Layer 1");
        let current_layer = doc.get_page(page1).get_layer(layer1);
        let font = doc.add_external_font(File::open("OCRAStd.ttf").unwrap()).unwrap();
        let mut image_file = File::open("card-retro.bmp").unwrap();
        let image = Image::try_from(image::bmp::BMPDecoder::new(&mut image_file)).unwrap();
        image.add_to_layer(current_layer.clone(), Some(Mm(15.0)), Some(Mm(232.0)), None, None, None, Some(600.0));
        let fill_color = Color::Cmyk(Cmyk::new(0.0, 0.0, 0.0, 0.0, None));
        current_layer.set_fill_color(fill_color);
        let stdin = io::stdin();
        let nome = stdin.lock().lines().next().unwrap().unwrap();
        let lunghezza_nome = nome.len();
        current_layer.use_text(nome, 12, Mm(15.0+(85.6-85.6*(lunghezza_nome as f64)/28.0)/2.0), Mm(240.0), &font);
        current_layer.use_text("00001", 12, Mm(31.5), Mm(248.5), &font);
        current_layer.use_text("124912", 12, Mm(56.5), Mm(248.5), &font);
        // translate x, translate y, rotate, scale x, scale y
        // by default, an image is optimized to 300 DPI (if scale is None)
        // rotations and translations are always in relation to the lower left corner
    
        let mut image_file_retro = File::open("card-retro.bmp").unwrap();
        let image_retro = Image::try_from(image::bmp::BMPDecoder::new(&mut image_file_retro)).unwrap();
    
        // translate x, translate y, rotate, scale x, scale y
        // by default, an image is optimized to 300 DPI (if scale is None)
        // rotations and translations are always in relation to the lower left corner
        image_retro.add_to_layer(current_layer.clone(), Some(Mm(100.6)), Some(Mm(232.0)), None, None, None, Some(600.0));
        doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap();
    }
    

    the image looks wrong (macOS Preview app loads it but it looks like it encoded about 10% of the image while Adobe Acrobat X complains that there isn't enough image data) image The image above is what is shown in Preview.app test_working.pdf card-retro.bmp.zip

    opened by eutampieri 4
  • "Mismatched Types" Error

    I'm trying out some of the examples, however the library does not seem willing to compile.

       Compiling printpdf v0.1.6
    error[E0308]: mismatched types
       --> C:\Users\***\.cargo\registry\src\github.com-1ecc6299db9ec823\printpdf-0.1.6\src\types\pl
    ugins\graphics\two_dimensional\font.rs:119:39
        |
    119 |             ("Ascent".into(), Integer(face_metrics.ascender)),
        |                                       ^^^^^^^^^^^^^^^^^^^^^ expected i64, found i32
    
    error[E0308]: mismatched types
       --> C:\Users\***\.cargo\registry\src\github.com-1ecc6299db9ec823\printpdf-0.1.6\src\types\pl
    ugins\graphics\two_dimensional\font.rs:120:40
        |
    120 |             ("Descent".into(), Integer(face_metrics.descender)),
        |                                        ^^^^^^^^^^^^^^^^^^^^^^ expected i64, found i32
    
    error[E0308]: mismatched types
       --> C:\Users\***\.cargo\registry\src\github.com-1ecc6299db9ec823\printpdf-0.1.6\src\types\pl
    ugins\graphics\two_dimensional\font.rs:121:42
        |
    121 |             ("CapHeight".into(), Integer(face_metrics.ascender)),
        |                                          ^^^^^^^^^^^^^^^^^^^^^ expected i64, found i32
    
    error[E0308]: mismatched types
       --> C:\Users\***\.cargo\registry\src\github.com-1ecc6299db9ec823\printpdf-0.1.6\src\types\pl
    ugins\graphics\two_dimensional\font.rs:258:51
        |
    258 |         let font_bbox = vec![ Integer(0), Integer(max_height), Integer(total_width as i64),
     Integer(max_height) ];
        |                                                   ^^^^^^^^^^ expected i64, found i32
    
    error[E0308]: mismatched types
       --> C:\Users\***\.cargo\registry\src\github.com-1ecc6299db9ec823\printpdf-0.1.6\src\types\pl
    ugins\graphics\two_dimensional\font.rs:258:101
        |
    258 |         let font_bbox = vec![ Integer(0), Integer(max_height), Integer(total_width as i64),
     Integer(max_height) ];
        |
             ^^^^^^^^^^ expected i64, found i32
    
    error: aborting due to 5 previous errors
    
    error: Could not compile `printpdf`.
    
    To learn more, run the command again with --verbose.
    

    This can most likely be fixed pretty fast with type coercions.

    opened by Henning-K 4
  • More text formatting features in printpdf?

    More text formatting features in printpdf?

    I’m currently working on genpdf, a document generator and layout engine built on top of printpdf.

    In #2, you wrote:

    I'd also really like to do layout in a seperate library, not in printpdf - this library should only contain what the PDF spec says and contain the "primitives" for building a PDF. Anything fancy, like tables, page breaks, etc. should be done in a seperate library.

    I think this applies to most features in genpdf, but there are some genpdf features that might be useful for printpdf:

    • Measuring the width and height of strings *
    • Kerning
    • Transforming between the PDF coordinate system and the “logical” coordinate system (relative to the upper left corner)
    • Views on a sub-area of a layer
    • Text alignment (left/center/right/justified) *
    • A TextSection type to ensure matching begin_text_section() and end_text_section() calls

    * Edit: I just saw that you already said that you don’t want to include this in printpdf

    Also, I plan to implement these features for genpdf that could be added to printpdf instead:

    • Keeping track of the current settings (colors, spacing, line height, etc.) to avoid redundant operations

    Are you interested in adding any of these features to printpdf?

    opened by robinkrahl 3
  • Use WinAnsiEncoding for built-in fonts

    Use WinAnsiEncoding for built-in fonts

    Previously, PdfLayerReference::write_text would use the UTF-8 encoding for built-in fonts. But the built-in fonts only support a very limited set of encodings, see Appendix D of the PDF Reference [0]. When adding the font to the PDF document, printpdf already selects the WinAnsiEncoding (Windows-1252, see the Into implementation for BuiltinFont). This would lead to encoding issues for all non-ASCII characters, see also issue #30.

    With this patch, we use lopdf::Document::encode_text to encode the text for built-in fonts with the WinAnsiEncoding. This fixes the encoding issues for built-in fonts. Note that that function just drops characters that are not supported by the encoding.

    This patch also adds notes about this behaviour to the documentation of the add_builtin_font, use_text and write_text methods.

    Fixes #30.

    [0] https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf

    opened by robinkrahl 3
  • Divide Mm by Mm not possible?

    Divide Mm by Mm not possible?

    I'm trying to calculate how often x fits into y and would expect a float: let a: f64 = Mm(x) / Mm(y); But somehow it seems not possible. Is this not implemented? Is there a workaround? Or am I doing sth wrong? I have also problems when multiplying: let b: Mm = Mm(x) * y: f64

    opened by tronta 3
  • using with wasm-pack shared reference problem

    using with wasm-pack shared reference problem

    Hi! I am new to Rust. I want to show wasm generated pdf with live changes in browser.

    How can I solve this shared reference problem? Is there a trait implementaion missing? How can I clone or take ownership of the doc? thank you.

    Purpose:

    to convert pdf to bytes array and sent to js side.

    Code

    use printpdf::*;
    use serde::{Deserialize, Serialize};
    use wasm_bindgen::prelude::*;
    
    #[wasm_bindgen]
    pub fn create() -> JsValue {
        let (doc, page1, layer1) =
            PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
    
        let bytes: Vec<u8> = doc.save_to_bytes().unwrap();
    
        serde_wasm_bindgen::to_value(&bytes).unwrap()
    }
    

    Build Command

    wasm-pack build --target bundler
    

    Error

    [INFO]: Checking for the Wasm target...
    [INFO]: Compiling to Wasm...
       Compiling printpdf v0.5.3
    error[E0507]: cannot move out of `m.modification_date` which is behind a shared reference
      --> /home/ulvi/.cargo/registry/src/github.com-1ecc6299db9ec823/printpdf-0.5.3/src/document_info.rs:77:56
       |
    77 |         let info_mod_date = to_pdf_time_stamp_metadata(m.modification_date);
       |                                                        ^^^^^^^^^^^^^^^^^^^ move occurs because `m.modification_date` has type `js_sys_date::OffsetDateTime`, which does not implement the `Copy` trait
    
    error[E0507]: cannot move out of `m.creation_date` which is behind a shared reference
      --> /home/ulvi/.cargo/registry/src/github.com-1ecc6299db9ec823/printpdf-0.5.3/src/document_info.rs:78:59
       |
    78 |         let info_create_date = to_pdf_time_stamp_metadata(m.creation_date);
       |                                                           ^^^^^^^^^^^^^^^ move occurs because `m.creation_date` has type `js_sys_date::OffsetDateTime`, which does not implement the `Copy` trait
    
    error[E0507]: cannot move out of `m.creation_date` which is behind a shared reference
      --> /home/ulvi/.cargo/registry/src/github.com-1ecc6299db9ec823/printpdf-0.5.3/src/xmp_metadata.rs:49:43
       |
    49 |         let create_date = to_pdf_xmp_date(m.creation_date);
       |                                           ^^^^^^^^^^^^^^^ move occurs because `m.creation_date` has type `js_sys_date::OffsetDateTime`, which does not implement the `Copy` trait
    
    error[E0507]: cannot move out of `m.modification_date` which is behind a shared reference
      --> /home/ulvi/.cargo/registry/src/github.com-1ecc6299db9ec823/printpdf-0.5.3/src/xmp_metadata.rs:50:49
       |
    50 |         let modification_date = to_pdf_xmp_date(m.modification_date);
       |                                                 ^^^^^^^^^^^^^^^^^^^ move occurs because `m.modification_date` has type `js_sys_date::OffsetDateTime`, which does not implement the `Copy` trait
    
    error[E0507]: cannot move out of `m.metadata_date` which is behind a shared reference
      --> /home/ulvi/.cargo/registry/src/github.com-1ecc6299db9ec823/printpdf-0.5.3/src/xmp_metadata.rs:51:45
       |
    51 |         let metadata_date = to_pdf_xmp_date(m.metadata_date);
       |                                             ^^^^^^^^^^^^^^^ move occurs because `m.metadata_date` has type `js_sys_date::OffsetDateTime`, which does not implement the `Copy` trait
    
    For more information about this error, try `rustc --explain E0507`.
    error: could not compile `printpdf` due to 5 previous errors
    Error: Compiling your crate to WebAssembly failed
    Caused by: failed to execute `cargo build`: exited with exit status: 101
      full command: "cargo" "build" "--lib" "--release" "--target" "wasm32-unknown-unknown"
    
    opened by ulvido 1
  • RTL support

    RTL support

    Hi! I wanted to know if this library supports RTL languages or not? In terms of writing and also in terms of layout and alignment.

    If not, can you please tell me if there is a solution or if you know a library that has this feature?

    opened by pikhosh 9
  • replace color_space field in `ImageXObject` with `color_type` field

    replace color_space field in `ImageXObject` with `color_type` field

    ColorSpace is just a simplified version of ColorType and is not used much in this library codebase.

    image

    Replacing it with the color_type field in ImageXObject will give library users more information, in case they want to do some transformation with the field image_data.

    For example, some people reported that adding an image with the Alpha channel will result in a blank page (#84, #119). This can be resolved by removing the alpha channel from ImageXObject::image_data (check this comment: https://github.com/fschutt/printpdf/issues/119#issuecomment-1120434233). However, for different color_type, image_data will have different formats. For ColorType::Rgba8, image_data will have format: [Red1, Green1, Blue1, Alpha1, ..., RedN, Green2, Blue2; AlphaN]. But for ColorType::Rgba16, the format will be [Red1a, Red1b, Green1a, Green1b, Blue1a, Blue1b, Alpha1a, Alpha1b, ...]. Source: https://github.com/image-rs/image/issues/1711#issuecomment-1120450753

    opened by anhtumai 1
  • Cannot add Png image to pdf page

    Cannot add Png image to pdf page

    Every time I try to decode a PNG file with PngDecoded and add it to the PDF page, the PDF page is blank.

    My Rust code:

    use printpdf::*;
    
    use std::fs::File;
    use std::io::BufWriter;
    
    fn main() {
        let (doc, page1, layer1) =
            PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
        let current_layer = doc.get_page(page1).get_layer(layer1);
    
        // currently, the only reliable file formats are bmp/jpeg/png
        // this is an issue of the image library, not a fault of printpdf
        let mut image_file = File::open("inputs/screenshot.png").unwrap();
        let image =
            Image::try_from(image_crate::codecs::png::PngDecoder::new(&mut image_file).unwrap())
                .unwrap();
    
        println!("{:?}", image.image);
    
        image.add_to_layer(current_layer.clone(), ImageTransform::default());
    
        doc.save(&mut BufWriter::new(File::create("output.pdf").unwrap()))
            .unwrap();
    }
    

    Cargo.toml:

    [package]
    name = "test_printpdf"
    version = "0.1.0"
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    printpdf = { version = "0.5", features = ["embedded_images"] }
    

    The PNG file, inputs/screenshot.png: screenshot

    The output PDF, output.pdf: image

    P/s: This problem does not happen when I decode JPEG files with JpegDecoder. Maybe it is only applied for PNG files

    opened by anhtumai 11
  • Enable some features for docs.rs?

    Enable some features for docs.rs?

    Currently, docs.rs doesn't show Image functions which are behind a "embedded_images" feature flag. Here is docs.rs's docs on how to enable it: https://docs.rs/about/metadata. I think adding this to Cargo.toml should work:

    [package.metadata.docs.rs]
    features = ["embedded_images"]
    
    opened by Shadlock0133 0
Releases(v0.5.0)
  • v0.5.0(Dec 18, 2021)

    • changed default PDF conformance to not embed ICC color profiles (save on file size by default)
    • remove embedded_images feature from default features, has to be manually enabled now
    • added ability to instantiate SVG files as XObjects in the page (using svg2pdf) - one SVG file can be drawn many times without bloating file size

    image

    Source code(tar.gz)
    Source code(zip)
Owner
Felix M. Schütt
Felix M. Schütt
A Rust library for PDF document manipulation.

lopdf A Rust library for PDF document manipulation. Example Code Create PDF document #[macro_use] extern crate lopdf; use lopdf::{Document, Object, St

Junfeng Liu 1.1k Dec 30, 2022
Generating PDF files in pure Rust

pdf-canvas A pure rust library for generating PDF files. Currently, simple vector graphics and text set in the 14 built-in fonts are supported. To use

Rasmus Kaj 128 Jan 1, 2023
PDF command-line utils written in Rust

rpdf PDF command-line utils written in Rust. rpdf makes working with PDF annotions super easy! It can merge annotations from multiple files, some show

Jérome Eertmans 13 May 9, 2023
Generate PDF files with JavaScript and WASM (WebAssembly)

WASM-PDF Generates PDF files directly in the browser with JavaScript and WASM (WebAssembly). Idea here is to push all the work involved in creating a

Jussi Niinikoski 369 Jan 2, 2023
docx-you-want is a tool to convert a PDF document into a .docx file

ddocx-you-want is a tool to convert a PDF document into a .docx file ... in an unusual way. Since these two formats are inherently differ

null 45 Dec 23, 2022
Converts books written in Markdown to HTML, LaTeX/PDF and EPUB

Crowbook Crowbook's aim is to allow you to write a book in Markdown without worrying about formatting or typography, and let the program generate HTML

Élisabeth Henry 567 Dec 29, 2022
PDF Structure Viewer, This tool is useful for when working with PDFs and/or lopdf.

PDF Structure Viewer Inspect how the PDF's structure looks. This tool is useful for when working with PDFs and/or lopdf. This application is used lopd

Ralph Bisschops 13 Nov 21, 2022
Image cropper (and colorizer) for pdf scans

Image cropper for personal use (might not work with your pdfs) Requires pdfimages on the path to work properly It's thought just for my workflow so is

cdecompilador 2 Nov 7, 2022
An easy-to-use library for writing PDF in Rust

printpdf printpdf is a library designed for creating printable PDF documents. Crates.io | Documentation [dependencies] printpdf = "0.3.2" Features Cur

Felix M. Schütt 592 Jan 8, 2023
Make a PDF file by writing kind of like HTML and CSS.

markup-pdf-rs The Rust library for making a PDF files by writing kind of like HTML and CSS. Inspired by Satori and React-pdf. This library makes a PDF

null 9 Jan 10, 2023
ezio offers an easy to use IO API for reading and writing to files and stdio

ezio - a crate for easy IO ezio offers an easy to use IO API for reading and writing to files and stdio. ezio includes utilities for generating random

Nick Cameron 98 Dec 21, 2022
Trulang is an interpreted language that is designed to be a simple, easy to learn, and easy to use programming language.

Trulang is an interpreted language that is designed to be a simple, easy to learn, and easy to use programming language.

Bunch-of-cells 2 Nov 23, 2022
A Rust library for PDF document manipulation.

lopdf A Rust library for PDF document manipulation. Example Code Create PDF document #[macro_use] extern crate lopdf; use lopdf::{Document, Object, St

Junfeng Liu 1.1k Dec 30, 2022
Easy reading and writing of `serde` structs to/from Google Sheets

serde_sheets Read and write structs directly from google sheets using serde and csv Implement serde::Serialize to write and serde::Deserialize to read

null 5 Jul 20, 2022
Macros to make writing proc-macro crates easy

proc-easy Macros to make writing proc-macro crates easy. This crate provides mainly macros and supporting types and traits to reduce amount of boilerp

Zakarum 7 Jan 1, 2023
Generating PDF files in pure Rust

pdf-canvas A pure rust library for generating PDF files. Currently, simple vector graphics and text set in the 14 built-in fonts are supported. To use

Rasmus Kaj 128 Jan 1, 2023
A rust program to bruteforce ZIP, PDF and some popular hashes.

Veldora A program to bruteforce zips, pdfs and some popular hashes. This is basically a rust version of bruttle, but a lot faster. Installation: git c

Aquib 30 Dec 28, 2022
A backend for mdBook written in Rust for generating PDF based on headless chrome and Chrome DevTools Protocol.

A backend for mdBook written in Rust for generating PDF based on headless chrome and Chrome DevTools Protocol.

Hollow Man 52 Jan 7, 2023
An mdBook single PDF generator using pure Rust and some Node.js

mdbook-compress An mdBook backend renderer to generate a single PDF file for a full book. There are other similar projects, but most rely on chrome in

nxe 44 Jan 20, 2023
PDF command-line utils written in Rust

rpdf PDF command-line utils written in Rust. rpdf makes working with PDF annotions super easy! It can merge annotations from multiple files, some show

Jérome Eertmans 13 May 9, 2023