当前位置: 首页 > news >正文

【Rust在WASM中实现pdf文件的生成】

Rust在WASM中实现pdf文件的生成

  • 概念和依赖
  • 问题描述
  • 分步实现
  • pdf转Blod的两种方式
  • 最后

概念和依赖

. WASM
WebAssembly(简称WASM)是一个虚拟指令集体系架构(virtual ISA),旨在为C/C++等语言编写的程序提供一种高效的二进制格式,使其能够在Web平台上以接近原生应用的运行速度运行‌。‌

跨平台‌:WebAssembly兼容所有主流浏览器,如Chrome、Firefox、Safari和Edge。Rust编写的代码可以轻松移植到不同的平台‌。
. Rust
Rust是一种系统编程语言,以其内存安全和高性能著称,是开发WebAssembly应用的理想选择‌‌
. Trunk
Trunk 是一款专为 Rust 语言设计的 WASM 网页应用打包工具。它能够帮助开发者轻松构建、打包并发布 Rust 编写的 WASM 应用到 Web 平台。Trunk 的设计理念是简单、高效,通过一个源 HTML 文件,Trunk 可以自动处理 WASM、JS 片段以及其他资源(如图片、CSS、SCSS)的打包工作。
本文的大部分依照这个而来:https://trunkrs.dev/guide/getting-started/index.html
项目:https://github.com/trunk-rs/trunk/tree/main
. printpdf
目前使用0.7的版本
https://docs.rs/printpdf/latest/printpdf/index.html
这是一个rust实现的生成pdf的工具,目前documnet-info.rs会在wasm中有两处错误。删除出错行就能在wasm环境使用。

问题描述

https://blog.csdn.net/wjcroom/article/details/143548767
在这个文章中,虽然使用了后端flask生成pdf,但唯一目的是打印,后端完全无必要来回传数据。本文要实现的wasm将图片在本地转pdf并显示。
因为这是一个验证性的工作,不具备很大的实际意义。但此架构可以构造一些功能强大本地wasn应用,比如,批量格式化一批数据到pdf格式,套用模板打印等等。

分步实现

  • 图片到pdf的转换
    先建立空项目
$ cargo new hello_cargo
$ cd hello_cargo

本文所用printpdf,要想开始image功能需要在Cargo.toml文件定义以下内容

[dependencies]
printpdf = { version = "0.7.0", features = [ "embedded_images" ] }

然后在main中测试主逻辑,拷贝printpdf在演示代码

fn main() {println!("Hello, world!");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 formats are bmp/jpeg/png
// this is an issue of the image library, not a fault of printpdf
let mut image_file = File::open("tmp.png").unwrap();
let image = Image::try_from( image_crate::codecs::png::PngDecoder::new(&mut image_file).unwrap()).unwrap();
let rotation_center_x = Px((image.image.width.0 as f32 / 2.0) as usize);
let rotation_center_y = Px((image.image.height.0 as f32 / 2.0) as usize);// layer,
image.add_to_layer(current_layer.clone(),ImageTransform {rotate: Some(ImageRotation {angle_ccw_degrees: 0.0,rotation_center_x,rotation_center_y,}),scale_x:  Some( 2.4),scale_y:   Some( 2.4),translate_x: Some(Mm(9.0)),translate_y: Some(Mm(9.0)),..Default::default()},
);doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap();
}

最终会生成来自png的一个pdf,
scale_x: Some( 2.4),
scale_y: Some( 2.4), 这两个参数是x,y的放大倍数。以适应页面。translate_x这是距离左下角的,位置。

  • 建立trunk项目

rustup target add wasm32-unknown-unknown
cargo install --locked trunk
cargo new trunk-hello-world
cd trunk-hello-world

从https://github.com/trunk-rs/trunk/tree/main 的example目录,拷贝一个示例的内容,集成了printpdf是这样,
大家可以删除pdf在部分,只体会一下trunk在能量。

 use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
use web_sys::window;
fn start_app( show:&str) {let document = window().and_then(|win| win.document()).expect("Could not access document");let body = document.body().expect("Could not access document.body");let text_node = document.create_text_node( &format!("Hello, world from Vanilla Rust!{}",show) );body.append_child(text_node.as_ref()).expect("Failed to append text");}fn main() {console_error_panic_hook::set_once();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");
let pdf_bytes = doc.save_to_bytes().unwrap();// doc.save(&mut BufWriter::new(File::create("/tmp/test_working.pdf").unwrap())).unwrap();   start_app(&String::from_utf8_lossy(&pdf_bytes)) ;
}

这部分代码会显示刚生成的pdf最简单的单页文件在 字节码。

在这里插入图片描述

pdf转Blod的两种方式

Blod做为一个对二进制的包装,可以保存pdf的二进制数据,的rust的printpdf中是vec,要想呈现有两个办法,

  1. 传出vect,用js呈现.在trunk的main看加入如下定义
#[wasm_bindgen]
pub fn png2pdf(pnginput: &[u8]) -Vec<u8>> {------
let pdf_bytes = doc.save_to_bytes().unwrap();
rturn pdf_bytes
}

然后的js人代码中,当 加载和启动wasm模块已经结束.就可以如同js函数一样调用.

const pdfbuf=wasmBindings.png2pdf(bufferArray);const blob = new Blob([pdfbuf], { type: 'application/pdf' }); // 创建一个URL指向Blob对象,必须用数组const blobUrl = URL.createObjectURL(blob);// 打开新的标签页并导航到PDF文件的URLwindow.open(blobUrl,"_self");

其中 const blob = new Blob([pdfbuf], …必须用数组包裹pdfbuf.下一个方法因为没有用数组,使用Blob失败.

2… 在rust中使用web_sys,调用window,document等实现显示pdf的blob.主要涉及插入iframe,并生成pdf,blob的URL对象
首先的Cargo.toml定义一些引用.

[dependencies]
console_error_panic_hook = "0.1.7"
wasm-bindgen = "0.2.96"
//js-sys = "0.3.10"
printpdf = { version = "0.7.0", features = [ "embedded_images" ] }
web-sys = { version = "0.3.73", features = ["Window", "Document", "HtmlElement","Blob", "Url", "BlobPropertyBag","Element","Text"] }

定义显示pdf的函数

use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
use web_sys::window;
use web_sys::Element;
use web_sys::Blob;
use web_sys::Url;
use web_sys::BlobPropertyBag;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;fn start_app( show:Vec<u8>) {let document = window().and_then(|win| win.document()).expect("Could not access document");let body = document.body().expect("Could not access document.body");let text_node = document.create_text_node( &format!("Hello, world from Vanilla Rust!{}","show") );let  theiframe :Element = document.create_element_ns(Some(""),"div").unwrap();let jsv=JsValue::from(show);let s:Vec<JsValue>=vec![jsv]; //注意必须包装成数组.JsValue是数组类型.is_array()let jsv1=JsValue::from(s);let tye=BlobPropertyBag::new();tye.set_type("application/pdf");let blob=Blob::new_with_u8_array_sequence_and_options(&jsv1,&tye).unwrap();let curl=Url::create_object_url_with_blob(&blob).unwrap(); theiframe.set_inner_html(&format!("<iframe src='{}' width='1100px' height='600px'></iframe>",curl ));// let ifm= theiframe.dyn_into::<HtmlIFrameElement>().unwrap().clone();//ifm.set_src="d";body.append_child(text_node.as_ref()).expect("Failed to append text");body.append_child(theiframe.as_ref()).expect("Failed to append text");}
fn main() {console_error_panic_hook::set_once();let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
let (page2, layer1) = doc.add_page(Mm(20.0), Mm(250.0),"Page 2, Layer 1");
let pdf_bytes = doc.save_to_bytes().unwrap();// doc.save(&mut BufWriter::new(File::create("/tmp/test_working.pdf").unwrap())).unwrap();   start_app(pdf_bytes) ;}

这两种方法就是我找到的展现pdf的方式,其中也可接受png的字节数据.
剩下的就是集成了.
在这里插入图片描述

最后

pdf的blob显示在一个iframe 中 的js代码:

<!DOCTYPE html>
<html>
<head><title>PDF Viewer</title>
</head>
<body><iframe id="pdfViewer" width="100%" height="800px"></iframe><script>// 假设这是你已经有的PDF Blob数据var pdfBlob = yourPDFBlobData; // 这里应该是你实际的Blob数据// 创建一个新的URL,指向这个Blobvar url = URL.createObjectURL(pdfBlob);// 获取iframe元素并设置其src属性为PDF的URLvar viewer = document.getElementById('pdfViewer');viewer.src = url;</script>
</body>
</html>

trunk sever是边改,边自动编译的命令,但是有个别时候,需要手工中止,ctr +c、然后再启动一次。

运行无错,均已实现后可truck build,生成的内容,一个*.wasm , 一个js,一个html、
参考html加入两个库到现有的页面,即可实现本文的要求了。
细节请根据文档调整。
由于时间仓促,且实际已无大的阻碍,当前没有实现最终版。所以本文暂时如此,后期看情形,将更新为上线运行版的代码。

特别感谢 【前端柒八九】提供的 Rust赋能前端:纯血前端将table导出excel提供的启发。


http://www.mrgr.cn/news/78784.html

相关文章:

  • 手机中的核心SOC是什么?
  • C++游戏开发需要具备哪些能力
  • Spring Web:深度解析与实战应用
  • Qt Serial Bus 前置介绍篇
  • Vue根据Div内容的高度给其Div设置style height
  • Oracle--表空间Tablespace
  • debian 11 虚拟机环境搭建过坑记录
  • Flink常见面试题
  • 嵌入式C编程:宏定义与typedef的深入对比与应用
  • Python知识分享第十六天
  • Hadoop生态圈框架部署(九)- Hive部署
  • MySQL中如何减少回表
  • 微服务即时通讯系统的实现(服务端)----(3)
  • 基础Web安全|SQL注入
  • 《装甲车内的气体卫士:上海松柏 S-M4 智能型气体传感器详解》
  • 从0开始学PHP面向对象内容之常用设计模式(策略,观察者)
  • 腾讯微众银行前端面试题及参考答案
  • 提升分布式系统响应速度:分布式系统远程调用性能提升之道
  • 开源多媒体处理工具ffmpeg是什么?如何安装?使用ffmpeg将M3U8格式转换为MP4
  • Flink问题总结
  • 预处理详解(完结篇)
  • 4399大数据面试题及参考答案(数据分析和数据开发)
  • IDEA自定义帆软函数步骤详解
  • 解决“ VMware Tools for Windows Vista and later“报错问题
  • Hive元数据表解析
  • 联合汽车电子嵌入式面试题及参考答案