Giter VIP home page Giter VIP logo

printpdf's Introduction

printpdf

[CI] Dependencies

printpdf is a library designed for creating printable PDF documents.

Crates.io | Documentation

[dependencies]
printpdf = "0.7.0"

Features

Currently, printpdf can only write documents, not read them.

  • Page generation
  • Layers (Illustrator like layers)
  • Graphics (lines, shapes, bezier curves)
  • Images (currently BMP/PNG/JPG 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)

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_crate::codecs::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(), ImageTransform::default());

    // 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.0, 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.0);
    current_layer.set_text_cursor(Mm(10.0), Mm(10.0));
    current_layer.set_line_height(33.0);
    current_layer.set_word_spacing(3000.0);
    current_layer.set_character_spacing(10.0);
    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.0);
    current_layer.write_text(text2.clone(), &font2);

current_layer.end_text_section();

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

printpdf's People

Contributors

alberdingk-thijm avatar alpaylan avatar cyphar avatar dnlmlr avatar dodomorandi avatar drahnr avatar fd avatar florian-escola avatar fschutt avatar g-w1 avatar jannschu avatar jeroenvervaeke avatar julienschminke avatar liampclancy avatar messense avatar nabijaczleweli avatar ngirard avatar ninjacato avatar nubis avatar ragibhasin avatar robinkrahl avatar rodrigorc avatar sapir avatar sizeru avatar twemyss avatar urlordjames avatar uuhan avatar vklachkov avatar vlmutolo avatar wyatt-herkamp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

printpdf's Issues

Links

Is it possible to use hyperlinks within the pdf? When I use the url directly in the string for use_text(), it is clickable, but I would like to display other text that opens the link when clicked.

Support for Paragraphs/Word Wrapping

So I'm wondering if there is a way to either support word wrapping when writing text so newlines are automatically inserted, or provide some metrics around glyph sizes so it can be handled externally.

The idea is to be able to support a paragraph of text which wraps when the width is full.

Supporting centered and right aligned text is an obvious extension of this, but is harder to do.

Generated PDF are large - size 660KB

No matter what I do, generated PDF files are large. 660KB in debug mode, 490KB in release mode on macOS.

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();

This produces test_working.pdf with size of 660KB ( debug mode )

SVG example

Hey,

Would be great with a example for rendering SVG.

Windows

Hello 👍 tried to get this running on Windows but it's mostly a no-go because of freetype. Any chance of using something friendlier 🤒

Character placement sometimes off

Sometimes, when generating PDFs, character placement seems to become broken, or there are extra spaces inserted. Attached are two images; one showing the expected result and the other the strange result.

The PDFs these images were cut from were generated with the exact same code. The only thing differing between the generated PDFs should be a serial number high up on the page (which I cropped when converting PDF to JPG). The string "Godkänd för F-skatt" is a literal string in the code; it was added to the PDF like this (The font used is "LiberationSans-Bold.ttf"):

current_pdf_layer.use_text("Godkänd för F-skatt".to_string(), 10, Mm(155.0), Mm(56.0), &font_lib_sans_bold);

For generating JPGs from PDFs I did

gm convert -crop 595x692+0+150 "$pdf" "$pdf".jpg

For a test run I generated 800 PDFs and noticed that 716 were looking as expected but the rest were not (and those, in turn, were often differing in different places).

My system is xubuntu 19.10. All software should be up to date.

19

20

Potential Way get length of text at certain fontsize

I have been wondering if it would be possible to get the length of a line of text for a certain font and at a certain font size.
Something along the lines of

get_length( text: String, font_size: i64 , font: &IndirectFontRef) -> Mm;

Any idea how I might go about implementing something like this ?
Or where I should start ?
I'm newish to rust, but I'm keen to give it a shot.

Handling untrusted user input

When writing untrusted user input to a PDF file with write_text, are there any security implications? Should the user escape or remove some control characters, or will all characters be interpreted as a string?

Is it possible to reuse an inserted image?

I am generating a large pdf where every page consists of the same layout with only the text varying. Is it possible to insert the bitmap only once in order to speed up generation of the file?

specifically, I'm currently using add_to_layer once per page. And noticed that when I use a bitmap of smaller size, the speedup is enormous. However, I understand the documentation so that perhaps this is a limitation of the PDF format?

Adds the image to a specific layer and consumes it This is due to a PDF weirdness - images are basically just "names" and you have to make sure that they are added to the same page as they are used on.

And printpdf::types::plugins::graphics::two_dimensional::image::Image::add_to_layer consumes the image.

So if you would be so kind to tell me whether it is technically possible, I might try to read up on that and create a PR if it is :)
Thank you so much for the time you invested into this library!

Private Indices module

I have a question regarding the design of the library. I'll start my question by describing what I'm trying to do.

I'm writing a small rust API which generates a PDF report. It's pretty straight forward, you throw data at it and it should spit out a PDF.
To do this created a simple struct Report, my goal is to be able to call add_data on my Report struct and that it automatically appends data and new pages (if needed) to the report.

This is the place where I get stuck with this library. To save the current page in my struct I need to be able to access PdfPageIndex from my application, which is not possible.

As far as I understood indices.rs are meant for internal use only.
However they're marked with pub. Also the public api to create a Pdf document returns a PdfPageIndex. In my opinion it's not logical to not export a type that's used in the public API.

I think a good solution would be to make indices public instead of private. Another option would be to write some public wrapper structs that can be used in external applications. Otherwise we're restricted to write most of the code in one method, which isn't really good practice.

XREF table

Regarding "1.1.1 Hello World PDF", I would like to know how the xref is calculated manually. You said that each object is 20-byte long. I can not verify that in the table

xref
0 7
0000000000 65535 f 
0000000009 00000 n 
0000000050 00000 n 
0000000102 00000 n 
0000000268 00000 n 
0000000374 00000 n 
0000000443 00000 n 

Are these number in Hex? 9-0 is not 20 and also 50-9 isn't 20 bytes.

UTF-8 vs PDF built-in fonts

It seems the PDF built-in fonts use the ISO-8859-1 encoding which causes output of a lot of "national" characters look wrong. Could printpdf be modified to check if the selected font is built-in and then translate the String contents into something that looks right on output? If the String contains characters not available in ISO-8859-1 they could maybe just be discarded?

The recoding of the String is quite straightforward - if the character code value is less than 256 then use it, otherwise discard it (or replace with NBSP or something else?).

If the chosen built-in font is 'Symbol' or 'Zapf Dingbats' then something more convoluted may have to be done, if at all.

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.

Running the shape example

Hi!

Thanks for your work on creating this library. First off, the example that draws a shape needs this using
statement:

use std::iter::FromIterator;

Secondly, after copying the code verbatim running 1.28.0-nightly on OSX. This is the output. Is this correct as I assumed the seconds shape should be a triangle.

Thanks!
test_working.pdf

Remove unused glyphs from embedded fonts?

First of all, thanks for this very useful crate!

I noticed that every embedded fonts adds about ~ 150–200 KiB to the size of the output file. This quickly leads to a quite large file when using multiple fonts. Running the generated file through ps2pdf reduces the file size to some KiB. As far as I see, this is because printpdf embeds every available glyph into the PDF file.

Would it be possible to keep track of the used characters and only embed the glyphs for the required characters?

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.

Update version

Hi, I just saw that you released the 0.3.4 version, could you please update it crates.io, so I can use it in a crate?

TTC and OTC font files cause Rusttype(IllFormed)

Reading opentype fonts(.otf) or truetype collection files (.ttc) causes panic!

let mut font_reader = std::io::Cursor::new(include_bytes!("../assets/fonts/Cantarell-Bold.otf").as_ref());
let font = doc.add_external_font(&mut font_reader).unwrap();

The second line caused panic, and retrun Rusttype(IllFormed).
I tried .ttc file and this also failed.

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

"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.

PNG and JPG images support

It's annoying to convert the image to BMP beforehand. Would be nice to have the ability to add also PNG and JPG images. What about using image-rs crate for this?

2. Supported Image Formats

image provides implementations of common image format encoders and decoders.

2.1 Supported Image Formats

Format Decoding Encoding
PNG All supported color types Same as decoding
JPEG Baseline and progressive Baseline JPEG
GIF Yes Yes
BMP Yes RGB(8), RGBA(8), Gray(8), GrayA(8)
ICO Yes Yes
TIFF Baseline(no fax support) + LZW + PackBits RGB(8), RGBA(8), Gray(8)
Webp Lossy(Luma channel only) No
PNM PBM, PGM, PPM, standard PAM Yes

2.2 The ImageDecoder Trait

All image format decoders implement the ImageDecoder trait which provides the following methods:

  • dimensions: Return a tuple containing the width and height of the image
  • colortype: Return the color type of the image.
  • row_len: Returns the length in bytes of one decoded row of the image
  • read_scanline: Read one row from the image into buf Returns the row index
  • read_image: Decode the entire image and return it as a Vector
  • load_rect: Decode a specific region of the image

3 Pixels

image provides the following pixel types:

  • Rgb: RGB pixel
  • Rgba: RGBA pixel
  • Luma: Grayscale pixel
  • LumaA: Grayscale with alpha

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
}

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

Some fonts may not work correctly (spacing between words)?

Now that printpdf has been migrated to rusttype, I have seen a problem where words are spaced weirdly, i.e. the space character is very wide. The strange thing about this is that the characters themselves are spaced correctly. So it's not the old character-spacing bug again. The font is Liberation Sans Bold. Need to investigate this later.

Does set_word_spacing() work?

The documentation clearly states that the set_word_spacing method (implemented for pdf_layer) is not operational. However, examples on the front page of the documentation show the method being used.

Is it safe to use set_word_spacing?

Multiline strings

I love this library it's easy to use and understand, perfect. The only issue I'm running into currently is multiline strings e.g.

let text_source_address = "610 Monroe St.\nStoudsburg, PA 18630-2115";

The output from:

current_layer.use_text(text_source_address, 10, Mm(30.0), Mm(230.0), &font);

is:

610 Monroe St. Stoudsburg, PA 18630-2115

Ideas?

Thank you for your efforts

Fonts are broken in iOS pdf viewer

Text using embedded fonts is not displayed correctly in iOS, like that one:
target_date.pdf

which looks like this in iOS:

grafik

when it should look like this:

grafik

I made it display correctly by a roundtrip through pdf2ps and ps2pdf, which also made the file 20 times smaller.

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.

Support stable Rust

Using #![feature(try_from)] makes this impossible to use from projects based on stable Rust.

Build error with time crate dependency

error: expected an item keyword
   --> /home/qtfkwk/.cargo/registry/src/github.com-1ecc6299db9ec823/time-0.2.8/src/utc_offset.rs:366:13
    |
366 |             let tm = timestamp_to_tm(datetime.timestamp())?;
    |             ^^^
    |
   ::: /home/qtfkwk/.cargo/registry/src/github.com-1ecc6299db9ec823/cfg-if-0.1.9/src/lib.rs:41:40
    |
41  |         if #[cfg($($meta:meta),*)] { $($it:item)* }
    |                                        -------- while parsing argument for this `item` macro fragment

error: aborting due to previous error

error: could not compile `time`.

This is relatively consistent on both a Windows 10 Debian WSL and a Linux laptop, both with Rust 1.44 stable. I say relatively because for some reason the build succeeds somehow sometimes, but no idea why.

Support for very thin lines

I'd like to draw shapes with line thickness of ~0.08mm, but the line thickness setting accepts an i64, of which any value <= 0 seems to have the same result, which is still too thick. The unit for this setting isn't documented, so I'm not sure if it's supposed to be pt or mm (or neither, since 0 wouldn't be visible if it was a normal unit).

Use rust life times instead of Rc<>

@fschutt Are you okay If I attempt to refactor the library such that PageRef and LayerRef types actually use rust life times instead of Rc<>? This would obviously be a breaking change but I believe it would more accurately communicate mutability and ownership of the referenced data.

png with transparency/alpha channel wouldn't shown

printpdf version: 0.3.3

If I try to add a png with transparency, the image wouldn't shown. Other png files works fine.

let mut image_file = File::open("test.png").unwrap();
let image = Image::try_from(image::png::PngDecoder::new(&mut image_file).unwrap()).unwrap();
image.add_to_layer(current_layer.clone(), Some(Mm(x)), Some(Mm(y)), None, Some(1.0), Some(1.0), Some(300.0));

Aligning text in a PDF

From looking at some of the issued here, I see that it looks like to get the font metrics, I would need to use azul-text-layout but, in attempting this, I get the following error:

error: failed to run custom build command for `servo-freetype-sys v4.0.5`

Caused by:
  process didn't exit successfully: `E:\proj\target\debug\build\servo-freetype-sys-b4e8d742cf1d3c83\build-script-build` (exit code: 101)
  --- stdout
  cargo:rerun-if-env-changed=FREETYPE2_NO_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG
  cargo:rerun-if-env-changed=FREETYPE2_STATIC
  cargo:rerun-if-env-changed=FREETYPE2_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64-pc-windows-msvc
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64_pc_windows_msvc
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64-pc-windows-msvc
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64_pc_windows_msvc
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64-pc-windows-msvc
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64_pc_windows_msvc
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
  running: "cmake" "C:\\Users\\user\\.cargo\\registry\\src\\github.com-1ecc6299db9ec823\\servo-freetype-sys-4.0.5\\freetype2" "-G" "Visual Studio 16 2019" "-Thost=x64" "-Ax64" "-DWITH_BZip2=OFF" "-DWITH_HarfBuzz=OFF" "-DWITH_PNG=OFF" "-DWITH_ZLIB=OFF" "-DCMAKE_INSTALL_PREFIX=E:\\delmenow\\target\\debug\\build\\servo-freetype-sys-bd77bc5f5b356b86\\out" "-DCMAKE_C_FLAGS= -nologo -MD -Brepro" "-DCMAKE_C_FLAGS_RELEASE= -nologo -MD -Brepro" "-DCMAKE_CXX_FLAGS= -nologo -MD -Brepro" "-DCMAKE_CXX_FLAGS_RELEASE= -nologo -MD -Brepro" "-DCMAKE_ASM_FLAGS= -nologo -MD -Brepro" "-DCMAKE_ASM_FLAGS_RELEASE= -nologo -MD -Brepro" "-DCMAKE_BUILD_TYPE=Release"

  --- stderr
  thread 'main' panicked at '
  failed to execute command: The system cannot find the file specified. (os error 2)
  is `cmake` not installed?

  build script failed, must exit now', C:\Users\user\.cargo\registry\src\github.com-1ecc6299db9ec823\cmake-0.1.45\src\lib.rs:894:5
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: build failed

Is there an alternate method?

Line closing bug with drawing circle

The line drawing API currently has a bug where you can't draw a circle - the last point doesn't get connected to the first point. Need to investigate this and do a circle drawing example.

For drawing maps, this is currently not important, however, it may become important later on.

Add PdfDocumentReference::set_creation_date

To be able to reproducibly generate a PDF document, e. g. for unit tests, it would be good to be able to set the creation date. If you don’t have any objections, I can try to implement this.

[Minor] Readme says "y from top edge" but should be "y from bottom edge"

Current Readme states:

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

But actually, reducing the fourth argument makes the text move downwards, not upwards.

I created this as an issue instead of a PR because I don't want to fork the project just for this minor change. Thanks for your library! :)

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?

PdfDocument::new without creating a page?

What's the reason PdfDocument::new also creates the first page. Is there a way to create an empty document?

I'm writing something that reads in a CSV and creates a document with a page per CSV row. Things would be a little easier if the first page wasn't automatically created for me.

Evenodd fill rule for paths

Hi,

I would like to use the "evenodd" filling rule for a path. Skimming through the code the corresponding operations already seem to be defined but the Line API only uses only "nonzero" filling at the moment.

I also have some time to create a PR for this if you want, but I would need your opinion on how the API should change and if an API break would be fine. Alternatively, I could also work with access to the low level Operation.

Edit: On second look, I also need the "close" and "move" semantics.

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.