Skip to content
Demo示例API 参考文档FAQ

PDF 生成模板编辑器

开源可视化 PDF 生成引擎,支持自定义模板,并提供面向开发者的 API,灵活构建文档生成流程。

查看 GitHub

邮件转换为 HTML 和 PDF ​

格式与初始化 ​

1.3.0 版本新增 CEmailConverter,支持 EML/MIME 邮件和 Outlook MSG 文件。输入可以是文件路径、File 对象、字节数组或输入流;输出可以是 HTML 资源包,或包含邮件正文及可选附件列表的 PDF。

请使用版本匹配的 JAR 和 native 库。运行以下 PDF 示例前,应按应用许可证密钥使用有效许可证初始化 SDK。仅解析邮件或导出 HTML 包不需要许可证,但仍须加载 native 库;只使用 HTML 功能的应用可通过 System.load() 按绝对路径加载匹配的 native 库。

CEmailFormat 提供 EMAIL_FORMAT_AUTO、EMAIL_FORMAT_EML 和 EMAIL_FORMAT_MSG。AUTO 根据 Compound File Binary 文件头识别 MSG,其余输入按 EML 解析,不依赖文件扩展名。二进制 MSG 应直接以文件、字节或流传入,不要先转成 Java 字符串。

邮件文件直接转换为 PDF ​

java
import CPDFGeneration.CEmailConverter;
import CPDFGeneration.CEmailDiagnostic;
import CPDFGeneration.CEmailResult;

try (CEmailResult result = CEmailConverter.ConvertToPdf("message.eml", "message.pdf")) {
    if (!result.isSuccess()) {
        for (int i = 0; i < result.GetDiagnosticCount(); i++) {
            CEmailDiagnostic diagnostic = result.GetDiagnostic(i);
            System.err.println(diagnostic.code + ": " + diagnostic.detail);
        }
        throw new IllegalStateException("Email conversion failed: " + result.GetStatus(),
                result.GetLastCallbackException());
    }
    System.out.println("Converted format: " + result.GetFormat());
}

将 message.eml 换为 MSG 路径,即可转换 MSG。输出 PDF 必须尚不存在:路径输出会保留已有文件;失败时会删除本次操作创建的部分文件。无论成功或失败,都应关闭返回的 CEmailResult。EMAIL_SUCCESS 和 EMAIL_SUCCESS_WITH_WARNINGS 都表示成功,isSuccess() 会同时接受这两种状态。

PDF 包含选定的邮件正文和启用时的附件列表,不会嵌入附件文件,也不会把附件内容转换成额外页面。PDF 中不保留附件下载链接;需要附件文件时,应导出 HTML 包或读取附件数据。

分别导出 HTML 与生成 PDF ​

先通过 ConvertToHtml() 解析一次,再根据保留的内容导出离线 HTML 包或生成 PDF。下面的示例同时查询主正文来源,并设置自定义页边距。

java
import CPDFGeneration.CConverterProperties;
import CPDFGeneration.CEmailBodySource;
import CPDFGeneration.CEmailConverter;
import CPDFGeneration.CEmailResult;
import CPDFGeneration.CEmailStatus;

CConverterProperties properties = new CConverterProperties();
try {
    properties.setPageMargins(48, 40, 48, 40);
    try (CEmailResult result = CEmailConverter.ConvertToHtml("message.msg")) {
        if (!result.isSuccess()) {
            throw new IllegalStateException("Email parsing failed: " + result.GetStatus());
        }
        CEmailBodySource body = result.GetSelectedBodySource();
        if (body.status.isSuccess()) {
            System.out.println(body.mediaType + " / " + body.origin);
        }
        CEmailStatus exported = result.ExportHtml("mail-package");
        if (!exported.isSuccess()) {
            throw new IllegalStateException("HTML export failed: " + exported);
        }
        CEmailStatus written = result.WritePdf("message.pdf", properties);
        if (!written.isSuccess()) {
            throw new IllegalStateException("PDF write failed: " + written,
                    result.GetLastCallbackException());
        }
    }
} finally {
    properties.release();
}

HTML 包建议使用新的输出目录。ExportHtml() 导出 index.html、正文资源和启用的附件文件,不覆盖已有输出文件。失败时会清理本次导出创建的文件,并保留调用方原有数据。GetHtml() 只返回 UTF-8 HTML 字符串,其中可能包含相对资源路径;需要完整离线资源包时,应使用 ExportHtml()。

必须检查每次导出或写入返回的状态。GetStatus() 表示最近一次操作,可能替换最初的转换状态。新的 native 输出操作会根据初始 HTML 转换状态重新计算状态和诊断;此前取得的 Java 快照仍然有效。

PDF、授权或写入失败后,已经生成成功的 HTML、资源和附件可以保留,用于再次导出或重试 PDF。输入解析失败则不保证存在 HTML。GetSelectedBodySource().status 仅表示正文来源查询的状态,不修改结果对象的操作状态。该接口只标识选定的主正文,不枚举所有显示的 multipart 片段;来源 ID 是结果内部的不透明标识。 返回的 CEmailBodySource 包含 bodyId、sourceId、mediaType 和 origin;媒体类型为 text/plain 或 text/html,来源类别为 original、recovered、fallback 或 generated。

PDF 属性传 null 时,邮件转换默认使用 A4,上下边距为 32 CSS px,左右为 40 CSS px。传入属性对象后,将使用其页面大小、页边距(包括 0)、字体提供者和页眉页脚设置;操作不会修改调用方的属性或 base URI。HTML 预览的内边距不会叠加到 PDF 页边距上。打印配置详见页面边距和页眉与页脚。

选项与取消 ​

选项传 null 时使用默认值,也可以创建 CEmailOptions。转换开始时会复制选项;复制期间不要并发修改同一个对象。

字段默认值说明
flags7显示邮件头、显示附件列表并启用附件导出。
maxInputBytes100 MiB原始输入的最大字节数。
maxDecodedBytes256 MiB解码内容的字节预算。
maxHtmlBytes64 MiB生成 HTML 的字节预算。
cancellationCallbacknull可选 CEmailCancelCallback,返回 true 请求取消。

CEmailFlags 可通过按位 OR 组合:

标志作用
EMAIL_INCLUDE_HEADERS显示邮件头摘要,默认开启。
EMAIL_INCLUDE_ATTACHMENT_LIST显示附件列表,默认开启。
EMAIL_EXPORT_ATTACHMENTS随 HTML 包导出可用的附件文件,默认开启。
EMAIL_INLINE_RESOURCES在生成的 HTML 中内联正文资源。
EMAIL_RECOVER_EML对不规范 EML 启用有限恢复;应通过诊断检查降级或跳过的内容。
EMAIL_INCLUDE_BCC显示默认隐藏的 Bcc。
java
import CPDFGeneration.CEmailConverter;
import CPDFGeneration.CEmailFlags;
import CPDFGeneration.CEmailFormat;
import CPDFGeneration.CEmailOptions;
import CPDFGeneration.CEmailResult;
import java.util.concurrent.atomic.AtomicBoolean;

CEmailOptions options = new CEmailOptions();
options.flags |= CEmailFlags.EMAIL_INLINE_RESOURCES;
options.maxInputBytes = 20L * 1024 * 1024;
AtomicBoolean cancelled = new AtomicBoolean(false);
options.cancellationCallback = cancelled::get;
try (CEmailResult result = CEmailConverter.ConvertToHtml(
        "message.eml", CEmailFormat.EMAIL_FORMAT_AUTO, options)) {
    if (!result.isSuccess()) {
        throw new IllegalStateException("Email parsing failed: " + result.GetStatus(),
                result.GetLastCallbackException());
    }
    System.out.println("HTML characters: " + result.GetHtml().length());
}

字节预算必须为非负数;0 表示真实的零上限,不代表无限。负预算在传给转换器时抛出 IllegalArgumentException。未知标志位返回 EMAIL_ERROR_INVALID_PARAMETER。即使没有超过配置的字节预算,仍可能触发解析器内部的结构限制。

取消采用协作式检查,发生在阶段之间和回调 I/O 中,不会中断正在执行的解析或排版操作,也不提供严格的超时保证。回调在当前调用 Java 线程上同步执行,应快速返回。其他线程可将示例中的 cancelled 标志设为 true 来请求取消。

结果对象会持有回调,后续 ExportHtml() 和 WritePdf() 仍使用该回调,直到结果关闭。修改原选项对象不会替换已保留的回调。回调异常会被视为取消,并保存在 GetLastCallbackException() 中。回调内不要重入 SDK、关闭正在使用的结果、释放其属性,或同步等待另一项 PDF 任务。

流与内存输入 ​

使用输入流时,应提供从当前位置开始要读取的邮件字节数,不要把 InputStream.available() 当成邮件长度。文件输入可使用 Files.size(),并确保读取期间输入不发生变化。

java
import CPDFGeneration.CEmailConverter;
import CPDFGeneration.CEmailResult;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

Path input = Paths.get("message.eml");
Path output = Paths.get("message-stream.pdf");
long length = Files.size(input);
try (InputStream source = Files.newInputStream(input);
     OutputStream destination = Files.newOutputStream(output,
             StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
     CEmailResult result = CEmailConverter.ConvertToPdf(source, length, destination)) {
    if (!result.isSuccess()) {
        throw new IllegalStateException("Stream conversion failed: " + result.GetStatus(),
                result.GetLastCallbackException());
    }
}

短读会循环补足。提前结束会返回 EMAIL_ERROR_READ,并在结果中保留原始 EOFException。SDK 不关闭或 flush 调用方的流;上面的 try-with-resources 由调用方负责关闭。失败或取消后,应丢弃部分输出,包括调用方为输出流自行创建的文件。

完全使用内存进行转换:

java
import CPDFGeneration.CEmailConverter;
import CPDFGeneration.CEmailResult;
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

byte[] email = Files.readAllBytes(Paths.get("message.msg"));
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (CEmailResult result = CEmailConverter.ConvertToPdf(email, output)) {
    if (!result.isSuccess()) {
        throw new IllegalStateException("Memory conversion failed: " + result.GetStatus(),
                result.GetLastCallbackException());
    }
    byte[] pdf = output.toByteArray();
    System.out.println("PDF bytes: " + pdf.length);
}

内存输入和输出会占用 Java 堆空间。较大的邮件建议使用文件或流输入,只有需要附件内容时才复制附件字节。

下列重载组都提供简化方法和带显式格式、选项的完整方法;PDF 完整方法还接受 CConverterProperties:

操作输入输出完整方法的附加参数
ConvertToHtmlString 路径或 FileCEmailResultCEmailFormat、CEmailOptions
ConvertToHtmlbyte[]CEmailResultCEmailFormat、CEmailOptions
ConvertToHtmlInputStream、long lengthCEmailResultCEmailFormat、CEmailOptions
ConvertToPdfString 路径String PDF 路径CEmailFormat、CEmailOptions、CConverterProperties
ConvertToPdfFileFile PDF 输出CEmailFormat、CEmailOptions、CConverterProperties
ConvertToPdfbyte[]OutputStreamCEmailFormat、CEmailOptions、CConverterProperties
ConvertToPdfInputStream、long lengthOutputStreamCEmailFormat、CEmailOptions、CConverterProperties

简化方法使用 AUTO 格式及默认选项;PDF 简化方法还使用邮件的默认页面属性。所有转换方法均返回需要关闭的自有 CEmailResult。

结果数据与状态码 ​

结果方法用途
GetStatus() / isSuccess()最近一次转换或 native 输出操作的状态。
GetFormat()解析器报告的输入格式。
GetPdfError()PDF 或授权失败时的底层 CHtmlConverter.CErrorCode。
GetHtml()HTML 字符串;未生成 HTML 时为空字符串。
GetResourceCount() / GetResource(index)正文资源数量及元数据、字节快照。
GetAttachmentCount() / GetAttachment(index)附件数量及元数据、字节快照。
GetDiagnosticCount() / GetDiagnostic(index)错误或警告诊断,包括代码、来源 ID、详情和降级处理。
GetSelectedBodySource()主正文来源的查询状态、ID、媒体类型和来源类别。
GetLastCallbackException()保留的 Java 流或取消回调异常;没有时为 null。
ExportHtml(String / File)导出离线 HTML 包。
WritePdf(String / File / OutputStream [, properties])从保留的 HTML 生成或重试 PDF。

GetResource() 返回 CEmailResource,GetAttachment() 返回 CEmailAttachment,GetDiagnostic() 返回 CEmailDiagnostic。资源和附件提供媒体类型、相对路径、字节数等元数据。附件还提供 id、originalName、displayName、hasData、hidden、referencedByBody 和 nestedMessage。只有元数据的附件满足 hasData == false 和 getData() == null,与合法的零字节附件不同。

集合索引从 0 开始,越界时抛出 IndexOutOfBoundsException。元数据及字节快照归 Java 所有,结果关闭后仍然有效。每次 getData() 还会返回一个新的独立字节数组,因此较大的内容会额外占用堆空间。

CEmailStatus数值含义
EMAIL_SUCCESS0无警告的成功。
EMAIL_SUCCESS_WITH_WARNINGS1成功,但有警告或降级内容。
EMAIL_ERROR_INVALID_PARAMETER-1参数、格式或选项无效。
EMAIL_ERROR_READ-2读取输入失败。
EMAIL_ERROR_PARSE-3邮件解析失败。
EMAIL_ERROR_HTML-4HTML 生成失败。
EMAIL_ERROR_WRITE-5输出写入失败。
EMAIL_ERROR_PDF-6PDF 渲染失败。
EMAIL_ERROR_LICENSE-7PDF 授权检查失败。
EMAIL_ERROR_LIMIT-8超出字节预算或内部结构限制。
EMAIL_ERROR_CANCELLED-9已请求取消。
EMAIL_ERROR_OUT_OF_MEMORY-10native 内存分配失败。
EMAIL_ERROR_INTERNAL-11内部错误或未知的 native 状态。

一般转换错误通过状态返回。负预算、路径含 NUL 等 Java 参数验证错误可能抛出 IllegalArgumentException;Java 或 JNI 内存分配失败仍可能抛出 OutOfMemoryError。流与取消回调异常保存在 GetLastCallbackException() 中,下一次 native 输出操作会替换该值。Java 参数验证失败不会改变前一次操作的状态。

生命周期与并发 ​

  • 每个 CEmailResult 都应使用 try-with-resources。close() 和 release() 可重复调用;关闭后的 native 查询或输出会抛出 IllegalStateException,仍可调用 isClosed()。
  • 转换期间保持属性对象及其字体提供者有效且不被修改。邮件操作与属性 release() 使用同一锁;已释放的属性不能再次传给邮件 API。
  • 独立结果可以并发解析或导出;同一个 Java 结果的操作与关闭会串行执行。
  • 邮件 PDF 渲染在内部串行化。不要同时通过旧 HTML API 渲染,也不要在 PDF 渲染期间修改全局授权或字体状态。
  • 遵守选项与取消的回调限制,避免重入或等待正在执行的转换。
  • Java 路径和元数据采用 UTF-8。Linux JDK 8 的文件系统 locale 也需使用 UTF-8,才能正确处理非 ASCII 路径。

邮件示例 ​

从发布包的 samples 目录运行:

bash
cd EmailConvertTest
bash RunTest.sh

示例直接读取 samples/TestFiles/email 中固定的 EML/MSG 文件,将 HTML 包、带页眉页脚的 PDF、嵌套邮件 PDF 和流转换 PDF 输出到新的 samples/out/email-example-* 目录。Linux 脚本会从 PATH 对应的 Java 安装中定位并预加载 libjsig.so,保持 JVM 信号处理器兼容性。在线授权缓存的后台刷新仍默认开启。

PrintOptionsTest 演示页边距和页眉页脚配置;FamilyHeaderFooterTest 使用 TestFiles/family_html/family_all_pages.html 演示封面跳页及页码设置。