Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dump to any type that implements Write + Seek traits #9

Open
mariopal opened this issue Mar 17, 2024 · 2 comments
Open

Dump to any type that implements Write + Seek traits #9

mariopal opened this issue Mar 17, 2024 · 2 comments

Comments

@mariopal
Copy link

Instead of always writing the generated PDF to a File, I have modified the code a little so that it dumps the result to any type W that implements Write + Seek:

diff --git a/src/fontsource.rs b/src/fontsource.rs
index 5c7f38c..77f2f67 100644
--- a/src/fontsource.rs
+++ b/src/fontsource.rs
@@ -5,7 +5,7 @@ use crate::fontmetrics::{get_builtin_metrics, FontMetrics};
 use crate::Pdf;
 use std::cmp::Eq;
 use std::hash::Hash;
-use std::io::{self, Write};
+use std::io::{self, Write, Seek};

 /// The "Base14" built-in fonts in PDF.
 /// Underscores in these names are hyphens in the real names.
@@ -38,7 +38,7 @@ pub trait FontSource: PartialEq + Eq + Hash {
     ///
     /// This is called automatically for each font used in a document.
     /// There should be no need to call this method from user code.
-    fn write_object(&self, pdf: &mut Pdf) -> io::Result<usize>;
+    fn write_object<W: Write + Seek>(&self, pdf: &mut Pdf<W>) -> io::Result<usize>;

     /// Get the PDF name of this font.
     ///
@@ -80,7 +80,8 @@ pub trait FontSource: PartialEq + Eq + Hash {
 }

 impl FontSource for BuiltinFont {
-    fn write_object(&self, pdf: &mut Pdf) -> io::Result<usize> {
+    fn write_object<W: Write + Seek>(&self, pdf: &mut Pdf<W>) -> io::Result<usize>
+    {
         // Note: This is enough for a Base14 font, other fonts will
         // require a stream for the actual font, and probably another
         // object for metrics etc
diff --git a/src/lib.rs b/src/lib.rs
index 6dd4812..950cc9e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -85,8 +85,8 @@ pub use crate::textobject::TextObject;
 /// are appended with the `render_page` method.
 /// Don't forget to call `finish` when done, to write the document
 /// trailer, without it the written file won't be a proper PDF.
-pub struct Pdf {
-    output: File,
+pub struct Pdf<W> {
+    output: W,
     object_offsets: Vec<i64>,
     page_objects_ids: Vec<usize>,
     all_font_object_ids: HashMap<BuiltinFont, usize>,
@@ -97,15 +97,17 @@ pub struct Pdf {
 const ROOT_OBJECT_ID: usize = 1;
 const PAGES_OBJECT_ID: usize = 2;

-impl Pdf {
+impl Pdf<File> {
     /// Create a new PDF document as a new file with given filename.
-    pub fn create(filename: &str) -> io::Result<Pdf> {
+    pub fn create(filename: &str) -> io::Result<Pdf<File>> {
         let file = File::create(filename)?;
         Pdf::new(file)
     }
+}

+impl<W: Write + Seek> Pdf<W> {
     /// Create a new PDF document, writing to `output`.
-    pub fn new(mut output: File) -> io::Result<Pdf> {
+    pub fn new(mut output: W) -> io::Result<Pdf<W>> {
         // TODO Maybe use a lower version?  Possibly decide by features used?
         output.write_all(b"%PDF-1.7\n%\xB5\xED\xAE\xFB\n")?;
         Ok(Pdf {
@@ -255,7 +257,7 @@ impl Pdf {

     fn write_new_object<F, T>(&mut self, write_content: F) -> io::Result<T>
     where
-        F: FnOnce(usize, &mut Pdf) -> io::Result<T>,
+        F: FnOnce(usize, &mut Pdf<W>) -> io::Result<T>,
     {
         let id = self.object_offsets.len();
         let (result, offset) =
@@ -270,7 +272,7 @@ impl Pdf {
         write_content: F,
     ) -> io::Result<T>
     where
-        F: FnOnce(&mut Pdf) -> io::Result<T>,
+        F: FnOnce(&mut Pdf<W>) -> io::Result<T>,
     {
         assert!(self.object_offsets[id] == -1);
         let (result, offset) = self.write_object(id, write_content)?;
@@ -284,7 +286,7 @@ impl Pdf {
         write_content: F,
     ) -> io::Result<(T, i64)>
     where
-        F: FnOnce(&mut Pdf) -> io::Result<T>,
+        F: FnOnce(&mut Pdf<W>) -> io::Result<T>,
     {
         // `as i64` here would overflow for PDF files bigger than 2**63 bytes
         let offset = self.tell()? as i64;

I only put it here in case you might be interested in adding it to the current code. I think it is compatible with the code that already uses this library. I use it to save the PDF in memory and return it directly as a response to a web request, without going through the file system.

@kaj
Copy link
Owner

kaj commented Mar 23, 2024

Thank! This looks good. I'll test it properly when I have a little more time, hopefully within a week or few. If you want to be "in the git log", you can submit the changes as a PR (otherwise I'll probably just commit something similar myself and attribute it to you in text).

@mariopal
Copy link
Author

Ok, you can make the commit yourself and if you want, mention me in the text. I also provide a code example to use the new Pdf::new() function, which in addition to the File type, accepts other types that implement Write + Seek traits, such as std::io::Cursor:

//! Example program dumping a PDF to a memory buffer and then to a file
use std::io::Cursor;
use pdf_canvas::{Pdf, BuiltinFont};
use pdf_canvas::graphicsstate::Color;


fn main() {
//  let mut document = Pdf::create("example.pdf").expect("Create pdf file");
  let mut buf: Vec<u8> = Vec::new();
  let fakefile = Cursor::new(&mut buf); //Create fake "file" from buf
  let mut document = Pdf::new(fakefile).expect("Create pdf buffer");

  let font = BuiltinFont::Helvetica;
  let head = "Header";
  let foot = "Footer";

  // Add a page to the document.  This page will be 594 by 841 pt large.
  document.render_page(594.0, 841.0, |canvas| {
    canvas.set_fill_color(Color::rgb(0, 0, 0))?;
    canvas.left_text(80.0  + 320.0*0.0, 761.0 - 530.0*0.0 , font, 9.0, head)?;
    canvas.rectangle(80.0  + 320.0*0.0, 755.0 - 530.0*0.0, 108.0, 4.0)?;
    canvas.rectangle(184.0 + 320.0*0.0, 647.0 - 530.0*0.0, 4.0, 108.0)?;
    canvas.rectangle(80.0  + 320.0*0.0, 647.0 - 530.0*0.0, 108.0, 4.0)?;
    canvas.rectangle(80.0  + 320.0*0.0, 647.0 - 530.0*0.0, 4.0, 108.0)?;
    canvas.left_text(80.0  + 320.0*0.0, 637.0 - 530.0*0.0 , font, 9.0, foot)?;
    canvas.fill()?;

    canvas.left_text(80.0  + 320.0*1.0, 761.0 - 530.0*1.0 , font, 9.0, head)?;
    canvas.rectangle(80.0  + 320.0*1.0, 755.0 - 530.0*1.0, 108.0, 4.0)?;
    canvas.rectangle(184.0 + 320.0*1.0, 647.0 - 530.0*1.0, 4.0, 108.0)?;
    canvas.rectangle(80.0  + 320.0*1.0, 647.0 - 530.0*1.0, 108.0, 4.0)?;
    canvas.rectangle(80.0  + 320.0*1.0, 647.0 - 530.0*1.0, 4.0, 108.0)?;
    canvas.left_text(80.0  + 320.0*1.0, 637.0 - 530.0*1.0 , font, 9.0, foot)?;
    canvas.fill()?;

    Ok(())
  }).expect("Write page");
  // Write all pending content, including the trailer and index
  document.finish().expect("Finish pdf document");

  //Dump the buf in memory to a PDF file
  std::fs::write("example.pdf", buf.as_slice()).expect("Create pdf file");
}//main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants