如何让 RegexBasedLocationExtractionStrategy 同时返回字体信息?
可以重写 toCRI() 保存字符的字体信息,再在 toRectangles() 中调用 super.toRectangles(cris) 计算位置,并生成自定义匹配结果。这样可以复用原有的跨行分组逻辑,无须复制整个方法,也无须直接访问 TextChunk::sameLine()。
一次正则匹配可能跨越多行,也可能涉及多种字体。设计结果数据结构时,应允许一个匹配对应多个矩形和多种字体。
为什么字体信息没有自动进入最终结果?
toCRI() 和 toRectangles() 提供了读取、保存额外信息的机会,但不会自动扩展结果接口。以 iText 9.5.0 的 API 为例,IPdfTextLocation 只定义了 getRectangle() 和 getText(),没有字体属性。需要将字体信息放进自定义结果对象,或通过额外的查询方法返回。IPdfTextLocation API
所核对的 develop 分支实现会把一次匹配涉及的 CharacterRenderInfo 列表传入 toRectangles(),再根据返回的矩形创建默认位置对象。可以利用这个扩展点保存一次匹配的矩形列表和字符信息,继续沿用原有的矩形计算算法。项目未提供具体版本,使用前应核对所依赖版本的调用流程。RegexBasedLocationExtractionStrategy 源码
示例:另外返回包含字体的匹配结果
下面的 Java 示例保留每次匹配的所有矩形,以及参与匹配的字符、字体和字号。它通过独立的 getFontMatches() 方法返回这些信息,不改变 IPdfTextLocation 接口。
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.listener.CharacterRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.listener.RegexBasedLocationExtractionStrategy;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class FontAwareStrategy
extends RegexBasedLocationExtractionStrategy {
private final List<FontMatch> matches = new ArrayList<>();
public FontAwareStrategy(Pattern pattern) {
super(pattern);
}
public static class FontCharacterInfo extends CharacterRenderInfo {
public final PdfFont font;
public final float fontSize;
public FontCharacterInfo(TextRenderInfo info) {
super(info);
font = info.getFont();
fontSize = info.getFontSize();
}
}
public static class FontMatch {
public final List<Rectangle> rectangles;
public final List<FontCharacterInfo> characters;
private FontMatch(List<Rectangle> rectangles,
List<FontCharacterInfo> characters) {
this.rectangles = new ArrayList<>(rectangles);
this.characters = new ArrayList<>(characters);
}
}
@Override
protected List<CharacterRenderInfo> toCRI(TextRenderInfo info) {
List<CharacterRenderInfo> result = new ArrayList<>();
info.getCharacterRenderInfos().forEach(character ->
result.add(new FontCharacterInfo(character)));
return result;
}
@Override
protected List<Rectangle> toRectangles(
List<CharacterRenderInfo> characters) {
List<Rectangle> rectangles = super.toRectangles(characters);
List<FontCharacterInfo> styledCharacters = new ArrayList<>();
for (CharacterRenderInfo character : characters) {
styledCharacters.add((FontCharacterInfo) character);
}
matches.add(new FontMatch(rectangles, styledCharacters));
return rectangles;
}
public List<FontMatch> getFontMatches() {
matches.clear();
super.getResultantLocations();
return new ArrayList<>(matches);
}
}
示例在创建字符对象时读取字体和字号,无须为了稍后访问它们而缓存整个 TextRenderInfo。getCharacterRenderInfos() 提供逐字形的渲染信息,getFont() 和 getFontSize() 提供相应字体属性。字形与 Java 字符并不保证一一对应。TextRenderInfo API
处理单页后,可以这样获取结果:
FontAwareStrategy strategy = new FontAwareStrategy(
Pattern.compile(Pattern.quote("$key1")));
new com.itextpdf.kernel.pdf.canvas.parser.PdfCanvasProcessor(strategy)
.processPageContent(page);
List<FontAwareStrategy.FontMatch> matches = strategy.getFontMatches();
这里用 Pattern.quote("$key1") 匹配字面标签,避免 $ 被解释为正则表达式中的行尾锚点。
跨行匹配和混合字体怎么处理?
示例中的 FontMatch 对应一次匹配,rectangles 保存该匹配的多个位置区域,characters 保存对应的字符信息。两个列表不是按下标一一对应的。
如果标签内的字体和字号一致,可以验证后选择其中一个字符的样式,用于绘制替换文本。跨行时也可以继续使用这套样式,同时保留多个矩形。
如果标签包含混合字体,应明确替换规则:统一采用某个字符的字体,或者按连续样式片段分别绘制。如果要求每个行片段直接携带精确的样式范围,就需要进一步保留“行片段到字符集合”的映射;仅保存最终矩形无法完整表达这种关系。
Pattern.DOTALL 只影响正则表达式中 . 对换行符的匹配行为。它不会改变 PDF 的阅读顺序,也不会为跨行文字确定统一字体。
使用时需要注意什么?
- 示例在
toRectangles()中记录每次匹配,所核对的实现随后还会对标准位置结果排序和去重。因此,不能认为getFontMatches()的条目数量或顺序与最终getResultantLocations()完全一致。 - 每页使用新的策略实例。该策略会保存页面内的字符位置,官方 API 明确要求处理多页时为每页创建新实例。RegexBasedLocationExtractionStrategy API
- 字体相同也不代表任意替换文本都能显示。替换前需要确认字体包含新文本所需的字形,尤其是 PDF 使用字体子集时。
- 位置矩形不足以完整恢复排版。如果模板存在旋转、缩放或特殊字符间距,还需要保存基线、变换和间距等信息。不要直接把矩形左下角当作文字基线。
- 覆盖框只改变可见效果,原标签通常仍在 PDF 内容流中。如果要求搜索、复制或提取时也找不到标签,需要另外处理原有内容。
更细粒度的扩展方法是否有帮助?
有帮助。单字符工厂方法可以减少 toCRI() 中重复的遍历代码;接收字符范围并创建结果对象的方法,则能更直接地保留匹配分组、行片段和字体信息。
这属于 API 设计建议,不能据此假定当前版本已经提供这些方法。使用现有扩展点时,可以先通过 super.toRectangles(cris) 复用位置计算,再返回自定义匹配对象,通常就能满足模板标签定位和字体保留的需求。
备注:内容仅供参考。
