文件示例:here 问题:我正在尝试确定文本是否在页面上可见。为此,对于每个 Fill 命令,我保存其路径和颜色,如下所示:
public class FillNonZeroRule extends OperatorProcessor {
@Override
public final void process(Operator operator, List<COSBase> operands) throws IOException {
PDGraphicsState gs = getGraphicsState();
linePath.setWindingRule(GeneralPath.WIND_NON_ZERO);
addFillPath(gs.getNonStrokingColor());
linePath.reset();
}
@Override
public String getName() {
return "f";
}
}
void addFillPath(PDColor color) {
filledPaths.put((GeneralPath)linePath.clone(), color);
}
而且,这就是我以后为每个角色获取背景的方法:
private PDColor getCharacterBackgroundColor(TextPosition text) {
PDColor color = null;
for (Map.Entry<GeneralPath, PDColor> filledPath : filledPaths.entrySet()) {
Vector center = getTextPositionCenterPoint(text);
if (filledPath.getKey().contains(lowerLeftX + center.getX(), lowerLeftY + center.getY())) {
color = filledPath.getValue();
}
}
return color;
}
也请为每个文本位置保存颜色。 然后,我尝试确定该背景色是否与字符色相同。 有趣的是,对于首页的背景颜色和标题的文本颜色(顶部带有背景的行)均为2301728(int RGB值)-这是不正确的,但是,对于第二页,文本颜色为2301728,背景颜色为14145754(正确!)。 所以我的问题是什么导致首页上的背景不正确... 预先感谢!
整个课程如下:
public class PdfToTextInfoConverter extends PDFTextStripper {
private int rotation = 0;
private float lowerLeftX = 0;
private float lowerLeftY = 0;
private PDPage page = null;
private GeneralPath linePath;
private Map<GeneralPath, PDColor> filledPaths;
private Map<TextPosition, PDColor> nonStrokingColors;
public PdfToTextInfoConverter(PDDocument pddfDoc) throws IOException {
addOperator(new SetStrokingColorSpace());
addOperator(new SetNonStrokingColorSpace());
addOperator(new SetNonStrokingColorN());
addOperator(new SetStrokingColor());
addOperator(new SetNonStrokingColor());
addOperator(new SetStrokingDeviceGrayColor());
addOperator(new SetNonStrokingDeviceGrayColor());
addOperator(new SetStrokingDeviceRGBColor());
addOperator(new SetNonStrokingDeviceRGBColor());
addOperator(new SetStrokingDeviceCMYKColor());
addOperator(new SetNonStrokingDeviceCMYKColor());
addOperator(new AppendRectangleToPath());
addOperator(new ClipEvenOddRule());
addOperator(new ClipNonZeroRule());
addOperator(new ClosePath());
addOperator(new CurveTo());
addOperator(new CurveToReplicateFinalPoint());
addOperator(new CurveToReplicateInitialPoint());
addOperator(new EndPath());
addOperator(new FillEvenOddAndStrokePath());
addOperator(new FillEvenOddRule());
addOperator(new FillNonZeroAndStrokePath());
addOperator(new FillNonZeroRule());
addOperator(new LineTo());
addOperator(new MoveTo());
addOperator(new StrokePath());
document = pddfDoc;
}
public void stripPage(int pageNum, int resolution) throws IOException {
this.setStartPage(pageNum + 1);
this.setEndPage(pageNum + 1);
page = document.getPage(pageNum);
rotation = page.getRotation();
linePath = new GeneralPath();
filledPaths = new LinkedHashMap<>();
nonStrokingColors = new HashMap<>();
Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
writeText(document, dummy); // This call starts the parsing process and calls writeString repeatedly.
}
@Override
public void processPage(PDPage page) throws IOException {
PDRectangle pageSize = page.getCropBox();
lowerLeftX = pageSize.getLowerLeftX();
lowerLeftY = pageSize.getLowerLeftY();
super.processPage(page);
}
private Integer getCharacterBackgroundColor(TextPosition text) {
Integer fillColorRgb = null;
try {
for (Map.Entry<GeneralPath, PDColor> filledPath : filledPaths.entrySet()) {
Vector center = getTextPositionCenterPoint(text);
if (filledPath.getKey().contains(lowerLeftX + center.getX(), lowerLeftY + center.getY())) {
fillColorRgb = filledPath.getValue().toRGB();
}
}
} catch (IOException e) {
logger.error("Could not convert color to RGB", e);
}
return fillColorRgb;
}
private int getCharacterColor(TextPosition text) {
int colorRgb = 0; // assume it's black even if we could not convert to RGB
try {
colorRgb = nonStrokingColors.get(text).toRGB();
} catch (IOException e) {
logger.error("Could not convert color to RGB", e);
}
return colorRgb;
}
@Override
protected void processTextPosition(TextPosition text) {
PDGraphicsState gs = getGraphicsState();
// check opacity for stroke and fill text
if (gs.getAlphaConstant() < Constants.EPSILON && gs.getNonStrokeAlphaConstant() < Constants.EPSILON) {
return;
}
Vector center = getTextPositionCenterPoint(text);
Area area = gs.getCurrentClippingPath();
if (area == null || area.contains(lowerLeftX + center.getX(), lowerLeftY + center.getY())) {
nonStrokingColors.put(text, gs.getNonStrokingColor());
super.processTextPosition(text);
}
}
@Override
protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
for (TextPosition text : textPositions) {
Integer characterColor = getCharacterColor(text);
Integer characterBackgroundColor = getCharacterBackgroundColor(text);
}
}
private Vector getTextPositionCenterPoint(TextPosition text) {
Matrix textMatrix = text.getTextMatrix();
Vector start = textMatrix.transform(new Vector(0, 0));
Vector center = null;
switch (rotation) {
case 0:
center = new Vector(start.getX() + text.getWidth()/2, start.getY());
break;
case 90:
center = new Vector(start.getX(), start.getY() + text.getWidth()/2);
break;
case 180:
center = new Vector(start.getX() - text.getWidth()/2, start.getY());
break;
case 270:
center = new Vector(start.getX(), start.getY() - text.getWidth()/2);
break;
default:
center = new Vector(start.getX() + text.getWidth()/2, start.getY());
break;
}
return center;
}
void addFillPath(PDColor color) {
filledPaths.put((GeneralPath)linePath.clone(), color);
}
}
答案 0 :(得分:2)
这是PDFBox中的错误。
(嗯,您的代码中还有一个问题,但是当前问题的原因是基于PDFBox的。)
问题是PDColor.toRGB()
的呼叫
fillColorRgb = filledPath.getValue().toRGB();
损坏相关特定颜色的颜色值本身!
有问题的颜色空间是分色颜色空间。因此,PDColor.toRGB()
使用其PDSeparation.toRGB(float[])
成员作为参数来调用components
。
如果给定参数的RGB值尚未在色彩空间中缓存,则PDSeparation.toRGB(float[])
将为给定参数评估tintTransform
。对于所讨论的色彩空间,色调转换是PDFunctionType0
实例。因此,称为PDFunctionType0.eval(float[])
。
不幸的是,PDFunctionType0.eval(float[])
假定它可以出于自身目的使用数组参数input
:
input[i] = clipToRange(input[i], domain.getMin(), domain.getMax());
input[i] = interpolate(input[i], domain.getMin(), domain.getMax(),
encodeValues.getMin(), encodeValues.getMax());
input[i] = clipToRange(input[i], 0, sizeValues[i] - 1);
但是此数组是原始PDColor
成员components
。因此,此评估将颜色对象的单一成分从0.172更改为43.688。
后来toRGB
调用该颜色时,发现其43.688(或由于其他不必要的更改而导致的其他值)远远超过最大值1.0,因此他们将其裁剪为1.0并从那里进行变换。但是组件1.0的颜色空间中的颜色恰好是用于前景文本的颜色。因此,您的代码认为背景和前景是相同的。
要解决此问题,方法PDFunctionType0.eval(float[])
应该重写为不要写入其参数数组。一种快速的方法是添加
input = input.clone();
该方法PDFunctionType0.eval(float[])
的顶部。
您的方法getTextPositionCenterPoint
使用页面旋转来确定给定TextPosition
的基线中心。但是,这仅对于页面旋转后垂直绘制的文本是正确的。但是,对于您的文档,情况并非如此。因此,您无需分析页面旋转,而是需要分析文本矩阵以了解文本的实际方向。
但是,这对您而言并没有太大区别,因为您用作字符宽度的TextPosition.getWidth()
值也是基于页面旋转来计算的。由于相关页面未旋转,但文本方向旋转了90°,因此TextPosition.getWidth()
始终返回0 ...您可能想使用getWidthDirAdj()
来代替...