PDF提取:格式化文本转HTML实战
2025-01-03 03:55:40
PDF 内容提取:格式化与 HTML 输出
PDF 文件因其跨平台兼容性和固定布局而成为文档共享的标准。不过,当需要提取 PDF 中的文本内容,并保留其原有的格式或者转化为 HTML 格式时,问题就来了。 这往往成为开发中的一个挑战。纯文本提取会丢失许多关键的信息,例如段落结构、列表、图片、以及标题等元素,无法满足我们后续的操作。下面分析几种实现 PDF 文本格式化提取的方案。
方案一: 基于 PDFBox 的定制文本提取器
Apache PDFBox 提供了解析 PDF 文件结构的基础能力,但是默认的 PDFTextStripper
只提取纯文本。我们需要继承这个类,重写其方法来定制输出。 这样做能够使我们有机会处理每一个文本元素及其属性,实现一定的格式化能力。
工作原理:
这种方法依赖于 PDFBox 提供的底层文本提取逻辑。通过重写 writeString()
和 processTextPosition()
方法,我们可以捕获每个字符和其所在的位置。根据位置和字体等信息生成 HTML 标签。
这涉及到一些逻辑,诸如检测换行、段落开始与结束,粗体,斜体等样式。这是一种更加控制粒度的方式,但开发起来也会稍微复杂一点。
代码示例与步骤:
在前面的示例中,你尝试使用了 PDFToHTMLConverter
和 NullWriter
类,但是没有得到正确的 Arabic 文本格式化输出。主要问题是代码将文本简单地翻转,且没有正确的考虑 HTML 结构,对阿拉伯文本的处理也是不恰当的。可以改进这个类。需要创建一个能保留换行、段落和简单样式处理的版本, 比如粗体、斜体,处理阿拉伯字符的展示顺序和方向。
需要确保 pdfbox-android 库已添加到你的 Android 项目中。
改进后的 PDFToHTMLConverter
:
import com.tom_roush.pdfbox.pdmodel.PDDocument;
import com.tom_roush.pdfbox.text.PDFTextStripper;
import com.tom_roush.pdfbox.text.TextPosition;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PDFToHTMLConverter extends PDFTextStripper {
private StringBuilder html;
private float lastY = -1f;
private float lineStart;
public PDFToHTMLConverter() throws IOException {
super();
this.html = new StringBuilder();
lineStart = -1;
}
@Override
protected void startDocument(PDDocument document) {
html.append("<html><body>");
}
@Override
protected void endDocument(PDDocument document) {
if (lineStart != -1) {
html.append("</p>");
}
html.append("</body></html>");
}
@Override
protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
if (textPositions.isEmpty()) {
return;
}
TextPosition firstPos = textPositions.get(0);
if(Math.abs(firstPos.getY() - lastY)>0.05){
if (lineStart != -1) {
html.append("</p>");
}
html.append("<p>");
lastY = firstPos.getY();
}
html.append(escapeHTML(text));
}
// 处理特殊字符
private String escapeHTML(String text) {
return text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'");
}
public String getHTMLText(File pdfFile) throws IOException {
try (PDDocument document = PDDocument.load(pdfFile)) {
this.writeText(document, new NullWriter());
}
String output = html.toString();
// 移除所有结尾的空行
output = output.replaceAll("(<p></p>)+import com.tom_roush.pdfbox.pdmodel.PDDocument;
import com.tom_roush.pdfbox.text.PDFTextStripper;
import com.tom_roush.pdfbox.text.TextPosition;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PDFToHTMLConverter extends PDFTextStripper {
private StringBuilder html;
private float lastY = -1f;
private float lineStart;
public PDFToHTMLConverter() throws IOException {
super();
this.html = new StringBuilder();
lineStart = -1;
}
@Override
protected void startDocument(PDDocument document) {
html.append("<html><body>");
}
@Override
protected void endDocument(PDDocument document) {
if (lineStart != -1) {
html.append("</p>");
}
html.append("</body></html>");
}
@Override
protected void writeString(String text, List<TextPosition> textPositions) throws IOException {
if (textPositions.isEmpty()) {
return;
}
TextPosition firstPos = textPositions.get(0);
if(Math.abs(firstPos.getY() - lastY)>0.05){
if (lineStart != -1) {
html.append("</p>");
}
html.append("<p>");
lastY = firstPos.getY();
}
html.append(escapeHTML(text));
}
// 处理特殊字符
private String escapeHTML(String text) {
return text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'");
}
public String getHTMLText(File pdfFile) throws IOException {
try (PDDocument document = PDDocument.load(pdfFile)) {
this.writeText(document, new NullWriter());
}
String output = html.toString();
// 移除所有结尾的空行
output = output.replaceAll("(<p></p>)+$", "");
output = output.trim(); // 移除首尾的空白字符
return output;
}
}
quot;, "");
output = output.trim(); // 移除首尾的空白字符
return output;
}
}
需要创建一个NullWriter
import java.io.Writer;
import java.io.IOException;
public class NullWriter extends Writer {
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
// Do nothing
}
@Override
public void flush() throws IOException {
// Do nothing
}
@Override
public void close() throws IOException {
// Do nothing
}
}
使用这个类:
try {
PDFToHTMLConverter converter = new PDFToHTMLConverter();
File pdfFile = new File("path/to/your/pdf.pdf"); // 替换为你的PDF文件路径
String htmlContent = converter.getHTMLText(pdfFile);
// 使用 htmlContent 处理你想要的操作, 例如设置 webview 或者进一步解析
Log.d("HTML output", htmlContent);
} catch (IOException e) {
e.printStackTrace();
}
注意,这里的改进版本添加了对换行的基本判断,将同一行内容放置在一个 <p>
标签中,简单的HTML 转义。处理阿拉伯文本需要更深入的支持,需要解析字符的方向和组合。 这仍然只是一个基础实现,如果要完全呈现复杂 PDF 文档,比如处理各种复杂样式、图片,则需要在文本分析的逻辑上,下更多的功夫,才能生成尽可能符合要求的HTML代码。
安全性: 在解析 PDF 内容时,注意文件来源。从不可信来源加载PDF 文件可能会带来安全风险,要小心处理。在写入数据或执行与 PDF 数据相关操作前,执行有效的验证,或者使用可信任的库解析内容。
方案二: 使用第三方 HTML 生成库
许多第三方库提供了更高级的 PDF 到 HTML 的转换功能,例如 PdfPig, PDFTron SDK。它们通常能够更好地处理复杂的布局,保留图片,提供更高的准确性和输出质量。
工作原理:
这些库使用不同的算法解析 PDF 文件结构,能够生成更加精准的 HTML 文件,包含更丰富的格式和布局信息。一些库还允许你自定义转换过程和样式。
代码示例与步骤:
由于不同的第三方库差异比较大,这里提供使用 pdfTron SDK的步骤(你需要拥有授权和环境):
-
添加 PDFTron SDK 到项目中: 你需要从他们的官网下载 Android 版本的 SDK,添加到你的 Android 项目中。
-
代码:
import com.pdftron.pdf.Convert;
import com.pdftron.pdf.PDFDoc;
import java.io.File;
public class PdfTronHelper {
public static String convertPDFtoHTML(String inputPath, String outputPath) {
try (PDFDoc doc = new PDFDoc(inputPath)) {
Convert.ConversionResult result = Convert.toHtml(doc, outputPath);
if (result == Convert.ConversionResult.e_success) {
return outputPath; // 返回输出 html 文件的路径
} else {
// 输出失败 log 信息
return ""; //返回失败标志,具体原因查看日志。
}
} catch (Exception ex) {
// 处理异常,包括异常的错误输出等。
ex.printStackTrace();
return "";
}
}
// 调用该方法转换
String htmlPath = PdfTronHelper.convertPDFtoHTML("/path/to/input.pdf", "/path/to/output.html");
}
```
你需要注册 pdftron SDK ,并通过 api 文档去了解更高级的功能使用,这会比较耗时。需要依据项目的情况,选择不同的 SDK 产品。
### 总结
不同的方法各有优缺点。如果只需要基本的文本提取和简单的格式保留,可以考虑定制的PDFBox 解析。如果要处理复杂的布局、高质量的转换结果,并且对输出质量要求较高,推荐使用成熟的商业库或者第三方库。 对于复杂的项目,结合两者的优势,或者根据需求选择合适的工具,才能更好地完成 PDF 内容提取和处理。
希望这些方案对你有帮助。