fschutt / printpdf

An easy-to-use library for writing PDF in Rust
https://docs.rs/printpdf/
MIT License
802 stars 96 forks source link

using with wasm-pack shared reference problem #128

Closed ulvido closed 1 year ago

ulvido commented 1 year ago

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"
fschutt commented 1 year ago

@ulvido you can't, it would need modifications to the user code in this example

ConProgramming commented 1 year ago

Fixed with https://github.com/fschutt/printpdf/pull/127

ulvido commented 1 year ago

Thank you. I can confirm that problem resolved. I have notations:

  1. But one should use printpdf from git repo not with cargo add printpdf because at the moment crates.io version is outdated.
  2. Vec\<u8> send with serde is somehow broken. You can create your own data type and use serde_bytes for the payload and send it to the javascript side.
EXAMPLE

Cargo.toml

#...
[dependencies]
wasm-bindgen = "0.2.63"
serde = { version = "1.0.160", features = ["derive"] }
serde-wasm-bindgen = "0.5.0"
serde_bytes = "0.11.9"
printpdf = { git = "https://github.com/fschutt/printpdf.git" }

lib.rs

use printpdf::*;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

mod utils;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet() {
    alert("Hello, printpdf!");
}

#[derive(Deserialize, Serialize)]
struct Message {
    #[serde(with = "serde_bytes")]
    payload: Vec<u8>,
}

#[wasm_bindgen]
pub fn create(title: &str, text: &str) -> JsValue {
    let (doc, page1, layer1) = PdfDocument::new(title, Mm(247.0), Mm(210.0), "Layer 1");

    let current_layer = doc.get_page(page1).get_layer(layer1);

    // use built-in font
    let font = doc.add_builtin_font(BuiltinFont::TimesRoman).unwrap();

    current_layer.begin_text_section();
    current_layer.use_text(text, 22.0, Mm(30.0), Mm(30.0), &font);
    current_layer.end_text_section();

    let bytes: Vec<u8> = doc.save_to_bytes().unwrap();
    let m = Message { payload: bytes };

    serde_wasm_bindgen::to_value(&m).unwrap()
}
USAGE

App.svelte

<script lang="ts">

  let pdf_title;
  let pdf_text;

  import init, { greet, create } from "pdf-maker"; // my wasm create name
  init().then((module) => {
    console.log(module);
    // greet();
  });

  const createpdf = () => {
    const blob = new Blob([create(pdf_title, pdf_text).payload], {
      type: "application/pdf",
    });
    const blobURL = URL.createObjectURL(blob);
    window.open(blobURL);
  };
</script>

<main>
  <div class="card">
    <input
      bind:value={pdf_title}
      placeholder="PDF Title"
      type="text"
    />
  </div>
  <div class="card">
    <textarea
      bind:value={pdf_text}
      placeholder="PDF Text"
      cols="30"
      rows="10"
    />
  </div>
  <div class="card">
    <button on:click={createpdf }>Create PDF</button>
  </div>
</main>