从PDF到OFD,国产化浪潮下多种文档格式导出的完美解决方案
前言
近年来,中国在信息技术领域持续追求自主创新和供应链安全,伴随信创上升为国家战略,一些行业也开始明确要求文件导出的格式必须为 OFD 格式。OFD 格式目前在政府、金融、税务、教育、医疗等需要文件开放、共享和长期保存的行业中广泛应用。这种趋势在未来几年内将进一步增强。
相较于 PDF,OFD 在以下方面展现了明显的优势,具体体现在:
- 开放性
PDF 是 Adobe 公司开发的专有格式,虽然也被广泛应用,但受制于 Adobe 公司的软件和许可。OFD 则是基于国际开放标准制定的开放式文档格式,任何人或组织都可以自由使用和开发相关软件。
- 功能特性
PDF 主要用于文档展示和打印,功能较为单一。OFD 在文档展示、打印、编辑等方面都有更强大的功能支持。
- 文件大小
PDF 文件通常会略大于 OFD 文件,因为 PDF 包含更多的元数据和功能,OFD 文件在保持良好的视觉效果的前提下,通常体积更小。
- 兼容性
PDF 虽然跨平台性强,但在不同软件和系统中的表现可能会有差异,OFD 则具有更好的跨平台一致性。
- 安全性
PDF 文件可能包含隐藏的功能和潜在的安全隐患,OFD 则更加透明,安全性更高。
如何将 PDF 转化为 OFD?
既然导出 OFD 格式如此重要,然而目前市面上的报表工具,前端导出时通常只支持 PDF 格式。那么在这种情况下,如何实现一键在前端将报表导出为 OFD 格式呢?今天,小编将以葡萄城的嵌入式 BI 工具——Wyn 商业智能作为例子,向大家介绍如何将 PDF 转换为 OFD 格式。

首先小编先带大家一起了解下OFD文件解析的底层原理:
OFD 文件底层结构:
OFD 文件采用XML作为其基本结构,这意味着文件内容是以文本形式存储的,便于编辑和搜索。OFD 文件主要由以下几个部分组成:
- 文档头(Document Header):包含文档的基本信息,如标题、作者、创建日期等。
- 文档体(Document Body):包含文档的实际内容,如文字、图片、表格等。
- 资源文件(Resource Files):包括文档中使用到的图片、字体、样式等资源。
- 元数据(Metadata):提供有关文档内容的额外信息,如关键词、摘要等。

PDF 转换为 OFD 的流程:

首先,通过使用 Wyn 报表工具,可以轻松设计出符合需求的报表样式。这些报表样式可以包含各种元素,例如表格、图表、图片、文本、超链接等等。设计完成后,可以直接在 Web 端进行预览,同时还支持将报表导出为PDF 格式。这样的设计流程和功能使得报表的创建和预览变得更加便捷和直观。

前端支持 PDF 导出只是第一步,为了实现从 PDF 转换为 OFD,还需要前端提供导出 PDF 的 API 接口,以便前端能够获取到 PDF 文件的流数据。幸运的是,Wyn 提供了丰富的 API 接口,使得前端可以通过接口直接实现PDF 的导出功能。这样的设计使得 PDF 转换为 OFD 变得更加便捷和可行。
PDF 转 OFD 的实现步骤
前端导出PDF文件的API接口:
http://localhost:51980/api/v2/reporting/export-templates/{exportTemplateId}
后端进行PDF文件解析的方法
- 继承 PDFGraphicsStreamEngine 类,便于分析 PDF 数据图层和资源归类
public class OFDPageDrawer extends PDFGraphicsStreamEngine {
}
- 重写构造方法,分析 PDF 每页的资源,并初始化 OFD 生成器
/**
构造器,调用super(page),这个操作的目的是将page资源准备好,并且添加对应的操作符,
当下一次调用processPage或者processPageContentStream时执行对应的操作符对应的操作
@param idx
@param page
@param ofdCreator
@param scale
@throws IOException
*/
protected OFDPageDrawer(int idx, PDPage page, OFDCreator ofdCreator, float scale)
throws IOException {
super(page);
this.page = page;
this.ofdCreator = ofdCreator;
ctLayer = this.ofdCreator.createLayer();
this.scale = scale;
}
- 重写 drawImage 方法收集整理 PDF 中分析出来的图片资源
/**
作用:将 PDF 图像对象转换为 OFD 格式进行绘制。此方法包括:
*
将图像写入字节流并保存。
根据当前变换矩阵计算图像在页面上的位置和大小。
创建 OFD 图像对象并设置其相关属性,然后添加到当前层中。
*
@param pdImage
@throws IOException
*/
@Override
public void drawImage(PDImage pdImage) throws IOException {
ByteArrayOutputStream bosImage = new ByteArrayOutputStream();
String suffix = "png";
ImageIO.write(pdImage.getImage(), suffix, bosImage);
String name = String.format("%s.%s", bcMD5(bosImage.toByteArray()), suffix);
ofdCreator.putImage(name, bosImage.toByteArray(), suffix);
// 根据当前变换矩阵计算图像在页面上的位置和大小,实际上就是将PDF中该图像的属性信息转换成OFD中的形式
Matrix ctmNew = this.getGraphicsState().getCurrentTransformationMatrix();
float imageXScale = ctmNew.getScalingFactorX();
float imageYScale = ctmNew.getScalingFactorY();
double x = ctmNew.getTranslateX() * scale;
double y = (page.getCropBox().getHeight() - ctmNew.getTranslateY() - imageYScale) * scale;
double w = imageXScale * scale;
double h = imageYScale * scale;
ImageObject imageObject = new ImageObject(ofdCreator.getNextRid());
imageObject.setBoundary(x, y, w, h);
imageObject.setResourceID(new ST_RefID(ST_ID.getInstance(ofdCreator.getImageMap().get(name))));
imageObject.setCTM(ST_Array.getInstance(String.format("%.0f 0 0 %.0f 0 0", w, h)));
setImageClip(imageObject, x, y, w, h);
ctLayer.add(imageObject);
}
- 通过继承 PDFGraphicsStreamEngine 类分析得到的文字内容重绘
public void addPageContent(int idx, CT_Layer ctLayer, float width, float height) {
PageDir pageDirInv = new PageDir();// 资源归类
pageDirInv.setIndex(idx);
org.ofdrw.core.basicStructure.pageObj.Page pageInv = new org.ofdrw.core.basicStructure.pageObj.Page();
CT_PageArea areaInv = new CT_PageArea();// ofd可视区域(PDF的裁剪区)
areaInv.setPhysicalBox(0, 0, width, height);
pageInv.setArea(areaInv);
Content contentInv = new Content();// 内容
contentInv.addLayer(ctLayer);
pageInv.setContent(contentInv);
pageDirInv.setContent(pageInv);
docDir.getPages().add(pageDirInv);
}
- 将收集到的资源进行打包生成 OFD 文件
/**
打包OFD文件包二进制数据
*
@param virtualFileMap
@return
@throws IOException
*/
public static void zip(Map<String, byte[]> virtualFileMap,OutputStream output) throws IOException {
ZipArchiveOutputStream zaos = new ZipArchiveOutputStream(output);
for (Map.Entry<String, byte[]> entry : virtualFileMap.entrySet()) {
zaos.putArchiveEntry(new ZipArchiveEntry(entry.getKey()));
zaos.write(entry.getValue());
zaos.closeArchiveEntry();
}
zaos.finish();
}
最终效果展示:

完整代码的链接:
总结
在当今时代,对于国产化的支持,OFD(Office Open XML for Developers)变得越来越重要。本文首先介绍了OFD 文件的底层结构,并阐述了 OFD 相对于 PDF 的优势。接着,介绍如何通过葡萄城的嵌入式 BI 工具——Wyn 商业智能,进行报表设计和导出 PDF 。同时,还展示了如何使用 Wyn 商业智能的 API 接口将 PDF 转换为 OFD,除此之外,在企业级复杂系统中,除了 OFD 之外,Wyn还同时支持Word、Excel、图片、Text、JSON等多种格式的导出。
通过本文的介绍,我们可以清楚地看到,将 PDF 转换为 OFD 不再是一个困扰。借助 Wyn 强大的功能和丰富的 API 接口支持,能够轻松高效地实现文档格式转换。这一解决方案为用户提供了便捷、灵活的操作方式,满足了行业对 OFD 格式的要求。
扩展链接:
从PDF到OFD,国产化浪潮下多种文档格式导出的完美解决方案的更多相关文章
- 【原】中文Ubuntu主目录下的文档文件夹改回英文
想把中文Ubuntu主目录下的文档文件夹改回英文,在Terminal下面操作的时候要输入中文特别不方便,于是便用了更改名字的想法 方法一: 首先把那几个中文名称修改成相应的英文,比如 Desktop. ...
- QJsonDocument实现Qt下JSON文档读写
版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:QJsonDocument实现Qt下JSON文档读写 本文地址:http://tech ...
- 教你用java统计目录下所有文档的词频
本文是统计目录下所有文档的词频top10,非单个文档,包含中文和英文. 直接上代码: package com.huawei.wordcount; import java.io.BufferedRead ...
- 利用altium怎么生成PDF及怎么1:1打印文档
画完板子之后,还要生成原理图PDF文档,供其他设计人员参考和指正. 上图红框标注的两个地方,分别用于打印预览设置和生成原理图PDF.那么若是生成原理图PDF文档,则选择smart PDF即可. 点击s ...
- Linux下实现文档在线浏览
使用php实现百度文库功能,网上搜索到的方案,实现doc转pdf,pdf转swf,然后显示出来. 这里简单的记录下,[doc转pdf,pdf转swf]两个功能的搭建流程. doc转pdf 使用到下列程 ...
- vim下处理文档中的\r\n\t字符
问题复现 拿到的文档中包含了大量的\r.\n.\t等字符,形如: \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\ ...
- ubuntu打开windows下txt文档乱码问题的解决
昨天晚上安装了Ubuntu11.10,打开TXT文件的时候发现中文乱码问题,在网上查了一下,一些网友提供了下面的方法: “按Alt+F2,打开“运行应用程序”对话框,输入“gconf-editor”, ...
- EPICS Archiver Appliance在Debian11下安装文档
本文很想标注转发,可是要转发链接,只好标注原创. 首先声明:本文档是合肥光源控制组孙晓康博士踩坑后整理的.我尝试过在Debian11和Rocky下安装,碰到坎过不去,这段时间各种事就没继续了,请教晓康 ...
- Jasperreports以及iReport4.5报表PDF导出字体完美解决方案
在使用Jasperreports以及iReport设计报表时,导出PDF是一个常见的需求.网上解决PDF导出中文显示问题相关的文章很多,无非就是设置控件的pdf font name和pdf encod ...
- (转)Ctrl+H 浪潮Raid配置文档
说明 本手册适用于LSI芯片Raid卡 包括但不限于Inspur 2008/2108 Raid卡.LSI 9240/9260/9261/9271 等Raid卡. 不同型号的Raid卡在某些功能上的支持 ...
随机推荐
- 11.17~11.18暨noip2023游寄
11.17 我们DZ不负众望又干了点nt事,但是为了按时间顺序记叙,所以说放到最后再讲 上午 平常的起床+吃饭,然后就发手机啥的,坐大巴去德州东再坐会高铁去秦皇岛,这些简单记一下就行了 重点来了 先拜 ...
- 【图形数据集】CIFAR-10 dataset数据集
文献引用:https://www.cs.toronto.edu/~kriz/cifar.html The CIFAR-10 dataset The CIFAR-10 dataset consists ...
- go 操作elasticsearch
olivere/elastic 包 github.com/olivere/elastic doc: https://pkg.go.dev/github.com/olivere/elastic?utm_ ...
- 让AnaTraf成为您网络流量分析的最佳利器
在快速发展的数字时代,企业对网络性能的监测与分析需求愈加旺盛.作为网络性能监测与诊断(NPMD)领域的佼佼者,AnaTraf网络流量分析仪凭借其出色的性能和易用性,正成为网络管理人员的首选工具. An ...
- pod(四):pod的重启策略和生命周期
目录 一.系统环境 二.前言 三.pod的重启策略 四.pod的生命周期 一.系统环境 服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架构 CentOS Linux ...
- 详解Spring循环依赖
一. 什么是循环依赖 循环依赖,就是两个或则两个以上的bean互相依赖对方,最终形成闭环.比如"A对象依赖B对象,而B对象也依赖A对象",或者"A对象依赖B对象,B对象依 ...
- JDK8之前,匿名内部类访问的局部变量为什么必须要用final修饰
更多博文请关注:https://blog.bigcoder.cn 前不久在学习中意外发现了自己原来忽略的一个小知识点,挺有意思的,现在我来给大家分享一下! 我们先来看一段代码 public class ...
- windows下IPv6组播(C++、MFC)
Server #include <stdio.h> #include <Ws2tcpip.h> #include <winsock2.h> #pragma comm ...
- 霍夫变换原理及实现(Opencv C++)
已知一幅图像中的n个点,假设我们希望找到这些点中位于直线上的子集.一种可能的解决方法是,首先找到由每对点确定的所有直线,然后寻找靠近特定直线的那些点的所有子集.这种方法涉及寻找n(n-1)/2~n2条 ...
- python+k8s——基础练习
列表 core_api = client.CoreV1Api() # 管理核心资源(Pod, Service, ConfigMap 等) apps_api = client.AppsV1Api() # ...