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

news2024/12/27 6:04:12

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

  • 概念和依赖
  • 问题描述
  • 分步实现
  • 最后

概念和依赖

. 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在bytes,传递给js,显示在一个iframe 中,是我最后的想法

<!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,指向这个Blob
        var url = URL.createObjectURL(pdfBlob);
 
        // 获取iframe元素并设置其src属性为PDF的URL
        var 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.coloradmin.cn/o/2251655.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java开发每日一课:Java开发岗位要求

找过工作的朋友应该知道&#xff0c;现在招聘Java开发工程师的公司特别多。那么Java开发这个岗位具体是做什么的&#xff1f;又有什么招聘要求呢&#xff1f; 我毕业的时候也面试过很多公司&#xff0c;当时对Java开发这个岗位的要求不甚了解&#xff0c;因为懂Java语法知识就能…

Spring Boot日志总结

文章目录 1.我们的日志2.日志的作用3.使用日志对象打印日志4.日志框架介绍5.深入理解门面模式(外观模式)6.日志格式的说明7.日志级别7.1日志级别分类7.2配置文件添加日志级别 8.日志持久化9.日志文件的拆分9.1官方文档9.2IDEA演示文件分割 10.日志格式的配置11.更简单的日志输入…

2025年Java面试八股文大全

很多人会问Java面试八股文有必要背吗&#xff1f; 我的回答是&#xff1a;很有必要。你可以讨厌这模式&#xff0c;但你一定要去背&#xff0c;因为不背你就进不了大厂。 国内的互联网面试&#xff0c;恐怕是现存的、最接近科举考试的制度。 而且&#xff0c;我国的八股文确…

DPDK用户态协议栈-Tcp Posix API 1

和udp一样&#xff0c;我们需要实现和系统调用一样的接口来实现我们的tcp server。先来看看我们之前写的unix_tcp使用了哪些接口&#xff0c;这边我加上两个系统调用&#xff0c;分别是接收数据和发送数据。 #include <stdio.h> #include <arpa/inet.h> #include …

记一次搞校园网的经历

接教室的校园网&#xff0c;到另一个屋子玩电脑&#xff0c;隔墙想放大一下AP的信号&#xff0c;发现死活不行 这是现状 由于校园网认证的存在&#xff0c;无法用桥接&#xff0c;桥接需要路由器有IP&#xff0c;而这个IP无法用未刷机的路由器来打开校园网页面认证 解决 将一…

RTC 实时时钟实验

利用 ALIENTEK 2.8 寸 TFTLCD 模块来显示日期和时间&#xff0c;实现一个简单的时钟。 STM32F1 RTC 时钟简介 STM32 的实时时钟&#xff08; RTC &#xff09;是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数 的计数器&#xff0c;在相应软件配置下&#xf…

接口性能优化宝典:解决性能瓶颈的策略与实践

目录 一、直面索引 &#xff08;一&#xff09;索引优化的常见场景 &#xff08;二&#xff09;如何检查索引的使用情况 &#xff08;三&#xff09;如何避免索引失效 &#xff08;四&#xff09;强制选择索引 二、提升 SQL 执行效率 &#xff08;一&#xff09;避免不必…

2021陇剑杯-内存取证

内存分析&#xff08;问1&#xff09; 网管小王制作了一个虚拟机文件&#xff0c;让您来分析后作答&#xff1a; 虚拟机的密码是_____________。&#xff08;密码中为flag{xxxx}&#xff0c;含有空格&#xff0c;提交时不要去掉&#xff09;。 mimikatz一把梭了 flag{W31C0M3…

Ubuntu 安装 MariaDB

安装 MariaDB具体步骤 1、更新软件包索引&#xff1a; sudo apt update2、安装 MariaDB 服务器&#xff1a; sudo apt install mariadb-server3、启动 MariaDB 服务&#xff08;如果未自动启动&#xff09;&#xff1a; sudo systemctl start mariadb4、设置 MariaDB 开机启…

深度学习Python基础(2)

二 数据处理 一般来说PyTorch中深度学习训练的流程是这样的&#xff1a; 1. 创建Dateset 2. Dataset传递给DataLoader 3. DataLoader迭代产生训练数据提供给模型 对应的一般都会有这三部分代码 # 创建Dateset(可以自定义) dataset face_dataset # Dataset部分自定义过的…

Linux下的三种 IO 复用

目录 一、Select 1、函数 API 2、使用限制 3、使用 Demo 二、Poll 三、epoll 0、 实现原理 1、函数 API 2、简单代码模板 3、LT/ET 使用过程 &#xff08;1&#xff09;LT 水平触发 &#xff08;2&#xff09;ET边沿触发 4、使用 Demo 四、参考链接 一、Select 在…

Windows常用DOS指令(附案例)

文章目录 1.dir 查看当前目录2.cd 进入指定目录3.md 创建指定目录4.cd> 创建指定文件5.rd 删除指定空目录6.del 删除指定文件7.copy 复制文件8.xcopy 批量复制9.ren 改名10.type 在命令行空窗口打开文件11.cls 清空DOS命令窗口12.chkdsk 检查磁盘使用情况13.time 显示和设置…

【Linux】匿名管道通信场景——进程池

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…

C#基础之集合讲解

文章目录 1 集合1.1 数组1.1.1 简介1.1.2 声明使用1.1.2.1 声明 & 初始化1.1.2.2 赋值给数组1.1.2.3 访问数组元素 1.1.3 多维数组1.1.3.1 声明1.1.3.2 初始化二维数组1.1.3.3 访问二维数组元素 1.1.4 交错数组1.1.5 传递数组给函数1.1.6 Array1.1.6.1 简介1.1.6.2 属性1.1…

Azure DevOps Server:使用甘特图Gantt展示需求进度

自从Azure DevOps Server取消与Project Server的集成后&#xff0c;许多用户都在关注如何使用甘特图来展示项目进度。 在Azure DevOps Server开放扩展Extension功能后&#xff0c;许多开发者或专业开发团队做了很多甘特图Gantt相关的开发工作&#xff0c;使用比较多的是(GANTT …

数据湖的概念(包含数据中台、数据湖、数据仓库、数据集市的区别)--了解数据湖,这一篇就够了

文章目录 一、数据湖概念1、企业对数据的困扰2、什么是数据湖3、数据中台、数据湖、数据仓库、数据集市的区别 网上看了好多有关数据湖的帖子&#xff0c;还有数据中台、数据湖、数据仓库、数据集市的区别的帖子&#xff0c;发现帖子写的都很多&#xff0c;而且专业名词很多&am…

Kali Linux怎么开python虚拟环境

相信很多朋友再学习的过程中都会遇到一些pip失效&#xff0c;或者报错的时候&#xff0c;他们要求我们要使用虚拟环境&#xff0c;但是不知道怎么搭建&#xff0c;下面这篇文章就来告诉你如何搭建虚拟环境&#xff0c;这个方法在所有Linux的服务器都通用&#xff0c;就两行命令…

Flink四大基石之State(状态) 的使用详解

目录 一、有状态计算与无状态计算 &#xff08;一&#xff09;概念差异 &#xff08;二&#xff09;应用场景 二、有状态计算中的状态分类 &#xff08;一&#xff09;托管状态&#xff08;Managed State&#xff09;与原生状态&#xff08;Raw State&#xff09; 两者的…

【数据结构计数排序】计数排序

非比较排序概念 非比较排序是一种排序算法&#xff0c;它不是通过比较元素大小进行排序的&#xff0c;而是基于元素的特征和属性排序。这种排序方法在特定情况下&#xff0c;可以做到比元素比较排序&#xff08;快排&#xff0c;归并&#xff09;更有效率。尤其是在处理大量数…

Java GET请求 请求参数在Body中使用Json格式传参

业务需要调个三方接口 使用GET请求方式 但是&#xff01;请求参数不在Query中&#xff0c;竟然在Body中&#xff0c;使用Json格式传参 在API调试工具里面可以调通 在java代码里&#xff0c;死活调不通 网上搜了搜&#xff0c;找到一个靠谱的&#xff0c;记录一下 import o…