From 42f2170a17616545afa32bd243e5a9f131d6e248 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Mon, 10 Jun 2019 21:30:27 -0700 Subject: [PATCH] refactor into pdf lib --- cli/Cargo.toml | 7 +-- cli/src/main.rs | 146 ++--------------------------------------------- lib/Cargo.toml | 6 +- lib/src/lib.rs | 1 + lib/src/paper.rs | 13 +++-- lib/src/pdf.rs | 131 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 151 insertions(+), 153 deletions(-) create mode 100644 lib/src/pdf.rs diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c4fb37d..4d1a25f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -7,8 +7,5 @@ edition = "2018" [dependencies] clap = "~2.33" zecpaperlib = { path = "../lib" } -json = "0.11.14" -qrcode = { version = "0.8", default-features = false } -lodepng = "*" -printpdf = "0.2.8" -image = "0.21.2" \ No newline at end of file +json = "0.11.14" +printpdf = "0.2.8" \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs index be54855..88c2964 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,48 +1,9 @@ extern crate clap; extern crate zecpaperlib; -extern crate lodepng; -extern crate printpdf; -extern crate image; -extern crate qrcode; -use qrcode::QrCode; -use qrcode::types::Color; -use std::io::BufWriter; -use std::convert::From; -use std::fs::File; use clap::{Arg, App}; use zecpaperlib::paper::*; -use printpdf::*; - -// imports the `image` library with the exact version that we are using -//use printpdf::*; - -fn get_qrcode_scaled(data: &str) -> (Vec, usize) { - let code = QrCode::new(data.as_bytes()).unwrap(); - let output_size = code.width(); - println!("Total QR Code Modules: {}", output_size); - - let imgdata = code.to_colors(); - - // Scale it to 10x - let scalefactor = 10; - let padding = 10; - let scaledsize = output_size * scalefactor; - let finalsize = scaledsize + (2 * padding); - - // Build a scaled image - let scaledimg: Vec = (0..(finalsize*finalsize)).flat_map( |i| { - let x = i / finalsize; - let y = i % finalsize; - if x < padding || y < padding || x >= (padding+scaledsize) || y >= (padding+scaledsize) { - vec![255u8; 3] - } else { - if imgdata[(x - padding)/scalefactor * output_size + (y - padding)/scalefactor] != Color::Light {vec![0u8; 3] } else { vec![255u8; 3] } - } - }).collect(); - - return (scaledimg, finalsize); -} +use zecpaperlib::pdf; fn main() { let matches = App::new("zecpaperwaller") @@ -79,109 +40,12 @@ fn main() { let testnet: bool = matches.is_present("testnet"); if !testnet { - eprint!("Mainnet addresses are not supported yet. Please re-run with --testnet"); + eprint!("Mainnet addresses are not supported yet. Please re-run with --testnet\n"); return; } - let num_addresses = matches.value_of("num_addresses").unwrap().parse::().unwrap(); - let addresses = gen_addresses_as_json(testnet, num_addresses); + let num_addresses = matches.value_of("num_addresses").unwrap().parse::().unwrap(); + let addresses = generate_wallet(testnet, num_addresses); println!("{}", addresses); - - -// let path = &Path::new("write_test.png"); - -// // Use number of QR modules to specify the image dimensions. -// if let Err(e) = lodepng::encode_file(path, &scaledimg, finalsize, finalsize, ColorType::RGB, 8) { -// panic!("failed to write png: {:?}", e); -// } - - let (doc, page1, layer1) = PdfDocument::new("Zec Sapling Paper Wallet", Mm(210.0), Mm(297.0), "Layer 1"); - let current_layer = doc.get_page(page1).get_layer(layer1); - let font = doc.add_builtin_font(BuiltinFont::Courier).unwrap(); - let font_bold = doc.add_builtin_font(BuiltinFont::CourierBold).unwrap(); - - let keys = json::parse(&addresses).unwrap(); - for kv in keys.members() { - add_address_to_page(¤t_layer, &font, &font_bold, kv["address"].as_str().unwrap(), 0); - - add_pk_to_page(¤t_layer, &font, &font_bold, kv["private_key"].as_str().unwrap(), 1); - - // Is the shape stroked? Is the shape closed? Is the shape filled? - let line1 = Line { - points: vec![(Point::new(Mm(5.0), Mm(160.0)), false), (Point::new(Mm(205.0), Mm(160.0)), false)], - is_closed: true, - has_fill: false, - has_stroke: true, - is_clipping_path: false, - }; - - let outline_color = printpdf::Color::Rgb(Rgb::new(0.0, 0.0, 0.0, None)); - - current_layer.set_outline_color(outline_color); - current_layer.set_outline_thickness(2.0); - - // Draw first line - current_layer.add_shape(line1); - }; - - doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap(); -} - -fn add_address_to_page(current_layer: &PdfLayerReference, font: &IndirectFontRef, font_bold: &IndirectFontRef,address: &str, pos: u32) { - let (scaledimg, finalsize) = get_qrcode_scaled(address); - - let ypos = 297.0 - 5.0 - (50.0 * ((pos+1) as f64)); - add_qrcode_image_to_page(current_layer, scaledimg, finalsize, Mm(10.0), Mm(ypos)); - - current_layer.use_text("Address", 14, Mm(55.0), Mm(ypos+27.5), &font_bold); - let strs = split_to_max(&address, 44); - for i in 0..strs.len() { - current_layer.use_text(strs[i].clone(), 12, Mm(55.0), Mm(ypos+15.0-((i*5) as f64)), &font); - } -} - -fn add_pk_to_page(current_layer: &PdfLayerReference, font: &IndirectFontRef, font_bold: &IndirectFontRef, pk: &str, pos: u32) { - let (scaledimg, finalsize) = get_qrcode_scaled(pk); - - let ypos = 297.0 - 5.0 - (50.0 * ((pos+1) as f64)); - add_qrcode_image_to_page(current_layer, scaledimg, finalsize, Mm(145.0), Mm(ypos-17.5)); - - current_layer.use_text("Private Key", 14, Mm(10.0), Mm(ypos+27.5), &font_bold); - let strs = split_to_max(&pk, 51); - for i in 0..strs.len() { - current_layer.use_text(strs[i].clone(), 12, Mm(10.0), Mm(ypos+15.0-((i*5) as f64)), &font); - } -} - -fn add_qrcode_image_to_page(current_layer: &PdfLayerReference, qr: Vec, qrsize: usize, x: Mm, y: Mm) { - // you can also construct images manually from your data: - let image_file_2 = ImageXObject { - width: Px(qrsize), - height: Px(qrsize), - color_space: ColorSpace::Rgb, - 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: qr, - image_filter: None, /* does not work yet */ - clipping_bbox: None, /* doesn't work either, untested */ - }; - - let image2 = Image::from(image_file_2); - image2.add_to_layer(current_layer.clone(), Some(x), Some(y), None, None, None, None); -} - - -fn split_to_max(s: &str, max: usize) -> Vec { - let mut ans: Vec = Vec::new(); - - for i in 0..((s.len() / max)+1) { - let start = i * max; - let end = if start + max > s.len() { s.len() } else { start + max }; - ans.push(s[start..end].to_string()); - } - - return ans; + pdf::save_to_pdf(&addresses, "test_working.pdf"); } \ No newline at end of file diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 1047e5d..7d41e91 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -11,11 +11,13 @@ ff = { git = "https://github.com/zcash/librustzcash" } hex = "0.3" pairing = { git = "https://github.com/zcash/librustzcash" } protobuf = "2" -rand = "0.4" +rand = "0.5" rusqlite = { version = "0.15", features = ["bundled"], optional = true } sapling-crypto = { git = "https://github.com/zcash/librustzcash" } time = { version = "0.1", optional = true } zcash_primitives = { git = "https://github.com/zcash/librustzcash" } zcash_proofs = { git = "https://github.com/zcash/librustzcash" } zip32 = { git = "https://github.com/zcash/librustzcash" } -json = "0.11.14" \ No newline at end of file +json = "0.11.14" +qrcode = { version = "0.8", default-features = false } +printpdf = "0.2.8" \ No newline at end of file diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 617b234..200903b 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1 +1,2 @@ pub mod paper; +pub mod pdf; \ No newline at end of file diff --git a/lib/src/paper.rs b/lib/src/paper.rs index 4fdb927..603ad57 100644 --- a/lib/src/paper.rs +++ b/lib/src/paper.rs @@ -1,18 +1,21 @@ use zip32::{ChildIndex, ExtendedSpendingKey}; use bech32::{Bech32, u5, ToBase32}; -use rand::{OsRng, Rng}; +use rand::{Rng, ChaChaRng, FromEntropy}; use json::{array, object}; -pub fn gen_addresses_as_json(testnet: bool, count: i32) -> String { - let mut rng = OsRng::new().expect("Error opening random number generator"); +/** + * Generate a series of `count` addresses and private keys. + */ +pub fn generate_wallet(testnet: bool, count: u32) -> String { + let mut rng = ChaChaRng::from_entropy(); let mut seed:[u8; 32] = [0; 32]; - rng.fill_bytes(&mut seed); + rng.fill(&mut seed); return gen_addresses_with_seed_as_json(testnet, count, &seed); } -pub fn gen_addresses_with_seed_as_json(testnet: bool, count: i32, seed: &[u8; 32]) -> String { +fn gen_addresses_with_seed_as_json(testnet: bool, count: u32, seed: &[u8; 32]) -> String { let mut ans = array![]; for i in 0..count { diff --git a/lib/src/pdf.rs b/lib/src/pdf.rs new file mode 100644 index 0000000..d29d351 --- /dev/null +++ b/lib/src/pdf.rs @@ -0,0 +1,131 @@ +extern crate printpdf; + +use qrcode::QrCode; +use qrcode::types::Color; + +use std::io::BufWriter; +use std::convert::From; +use std::fs::File; +use printpdf::*; + +/** + * Save the list of wallets (address + private keys) to the given PDF file name. + */ +pub fn save_to_pdf(addresses: &str, filename: &str) { + let (doc, page1, layer1) = PdfDocument::new("Zec Sapling Paper Wallet", Mm(210.0), Mm(297.0), "Layer 1"); + let current_layer = doc.get_page(page1).get_layer(layer1); + let font = doc.add_builtin_font(BuiltinFont::Courier).unwrap(); + let font_bold = doc.add_builtin_font(BuiltinFont::CourierBold).unwrap(); + + let keys = json::parse(&addresses).unwrap(); + for kv in keys.members() { + add_address_to_page(¤t_layer, &font, &font_bold, kv["address"].as_str().unwrap(), 0); + + add_pk_to_page(¤t_layer, &font, &font_bold, kv["private_key"].as_str().unwrap(), 1); + + // Is the shape stroked? Is the shape closed? Is the shape filled? + let line1 = Line { + points: vec![(Point::new(Mm(5.0), Mm(160.0)), false), (Point::new(Mm(205.0), Mm(160.0)), false)], + is_closed: true, + has_fill: false, + has_stroke: true, + is_clipping_path: false, + }; + + let outline_color = printpdf::Color::Rgb(Rgb::new(0.0, 0.0, 0.0, None)); + + current_layer.set_outline_color(outline_color); + current_layer.set_outline_thickness(2.0); + + // Draw first line + current_layer.add_shape(line1); + }; + + doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap(); +} + +fn qrcode_scaled(data: &str, scalefactor: usize) -> (Vec, usize) { + let code = QrCode::new(data.as_bytes()).unwrap(); + let output_size = code.width(); + println!("Total QR Code Modules: {}", output_size); + + let imgdata = code.to_colors(); + + let padding = 10; + let scaledsize = output_size * scalefactor; + let finalsize = scaledsize + (2 * padding); + + // Build a scaled image + let scaledimg: Vec = (0..(finalsize*finalsize)).flat_map( |i| { + let x = i / finalsize; + let y = i % finalsize; + if x < padding || y < padding || x >= (padding+scaledsize) || y >= (padding+scaledsize) { + vec![255u8; 3] + } else { + if imgdata[(x - padding)/scalefactor * output_size + (y - padding)/scalefactor] != Color::Light {vec![0u8; 3] } else { vec![255u8; 3] } + } + }).collect(); + + return (scaledimg, finalsize); +} + + + +fn add_address_to_page(current_layer: &PdfLayerReference, font: &IndirectFontRef, font_bold: &IndirectFontRef,address: &str, pos: u32) { + let (scaledimg, finalsize) = qrcode_scaled(address, 10); + + let ypos = 297.0 - 5.0 - (50.0 * ((pos+1) as f64)); + add_qrcode_image_to_page(current_layer, scaledimg, finalsize, Mm(10.0), Mm(ypos)); + + current_layer.use_text("Address", 14, Mm(55.0), Mm(ypos+27.5), &font_bold); + let strs = split_to_max(&address, 44); + for i in 0..strs.len() { + current_layer.use_text(strs[i].clone(), 12, Mm(55.0), Mm(ypos+15.0-((i*5) as f64)), &font); + } +} + +fn add_pk_to_page(current_layer: &PdfLayerReference, font: &IndirectFontRef, font_bold: &IndirectFontRef, pk: &str, pos: u32) { + let (scaledimg, finalsize) = qrcode_scaled(pk, 10); + + let ypos = 297.0 - 5.0 - (50.0 * ((pos+1) as f64)); + add_qrcode_image_to_page(current_layer, scaledimg, finalsize, Mm(145.0), Mm(ypos-17.5)); + + current_layer.use_text("Private Key", 14, Mm(10.0), Mm(ypos+27.5), &font_bold); + let strs = split_to_max(&pk, 51); + for i in 0..strs.len() { + current_layer.use_text(strs[i].clone(), 12, Mm(10.0), Mm(ypos+15.0-((i*5) as f64)), &font); + } +} + +fn add_qrcode_image_to_page(current_layer: &PdfLayerReference, qr: Vec, qrsize: usize, x: Mm, y: Mm) { + // you can also construct images manually from your data: + let image_file_2 = ImageXObject { + width: Px(qrsize), + height: Px(qrsize), + color_space: ColorSpace::Rgb, + 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: qr, + image_filter: None, /* does not work yet */ + clipping_bbox: None, /* doesn't work either, untested */ + }; + + let image2 = Image::from(image_file_2); + image2.add_to_layer(current_layer.clone(), Some(x), Some(y), None, None, None, None); +} + + +fn split_to_max(s: &str, max: usize) -> Vec { + let mut ans: Vec = Vec::new(); + + for i in 0..((s.len() / max)+1) { + let start = i * max; + let end = if start + max > s.len() { s.len() } else { start + max }; + ans.push(s[start..end].to_string()); + } + + return ans; +} \ No newline at end of file