C# iText7以错误的顺序读取行
我试图读出一个pdf文档表,但我面临一个问题 如果我定期打开PDF 它显示为:C# iText7以错误的顺序读取行,c#,itext7,C#,Itext7,我试图读出一个pdf文档表,但我面临一个问题 如果我定期打开PDF 它显示为: item[tab]item[tab]item[tab]item[tab]item item[tab]item[tab]item[tab]item[tab]item item[tab]item[tab]item[tab]item[tab]item 我使用以下方法转换PDF: StringBuilder result = new StringBuilder(); PdfDocument pdfDoc = new Pdf
item[tab]item[tab]item[tab]item[tab]item
item[tab]item[tab]item[tab]item[tab]item
item[tab]item[tab]item[tab]item[tab]item
我使用以下方法转换PDF:
StringBuilder result = new StringBuilder();
PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC));
LocationTextExtractionStrategy strategy = new LocationTextExtractionStrategy();
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (int i = 1; i <= pdfDoc.GetNumberOfPages(); i++)
{
result.AppendLine("INFO_START_PAGE");
string output = PdfTextExtractor.GetTextFromPage(pdfDoc.GetPage(i));
/*Note, in the GetTextFromPage i replaced the method to output [tab] instead of a regular space on
big spaces*/
foreach(string data in output.Replace("\r\n", "\n").Replace("\n", "×").Split('×'))
{
result.AppendLine(data.Trim().Replace(" ", "[tab]"));
}
result.AppendLine("INFO_END_PAGE");
}
pdfDoc.Close();
return result.ToString();
有没有办法解决这个问题
提取为
阿蒂克尔纳。奥姆施里金安塔尔酒店
佩尔斯图克·科斯滕
VERHUUR L.GELEVERDE ARBEID PDC 8欧元43,70欧元349,60欧元
VERHUUR O.GELEVERDE ARBEID PDC 3 60,95欧元182,85欧元
VERHUUR L.L.GELEVERDE ARBEID EM 24
€ 32,20 € 772,80
首先,为什么会这样
正如在对问题的评论中推测的那样,确实存在一个小的垂直台阶,在所有行中,前三列设置在相同的垂直位置,后两列的垂直位置略有不同
行第一列y最后列y
标题行536 535.893
第一排516516.229
第二排495 495.478
第三排475 474.788
人们特别认识到,通过文本提取中断的行是其中y位置的小数点前数字不同于536 vs 535、475 vs 474的行,而具有相等小数点前数字的行没有中断
这是因为类TextChunkLocationDefaultImp在默认情况下用于存储文本块位置和比较这些位置的方法,它存储块的y位置,实际上是块的一个抽象,也适用于未在整数变量private readonly int distvertical和测试中水平写入的文本方法SameLine要求距离垂直值相等
命名空间iText.Kernel.Pdf.Canvas.Parser.Listener{
内部类TextChunkLocationDefaultImp:ITextChunkLocation{
...
///方向单位向量的垂直距离,即未旋转坐标系中的Y位置。
///
///
///方向单位向量的垂直距离,即未旋转坐标系中的Y位置。
///我们四舍五入到最接近的整数来处理比较浮点数的模糊性。
///
私有只读int;
...
///要比较的位置
///如果此位置与另一个位置在同一条线上,则为true
公共虚拟bool SameLineITextChunkLocation@as{
...
float dist垂直度diff=distvertical-@as.distvertical;
如果dist垂直度diff==0{
返回true;
}
...
}
...
}
}
实际上,如果比较的文本块中有一个长度为零,则向下的SameLine允许一个小偏差。显然,长度为零的语块有时被用作变音标记,这种标记有时被应用于不同的高度。不过,在您的示例文件中,这并不重要
如何修复它
如上所述,问题是由TextChunkLocationDefaultImp.SameLine的行为造成的。因此,我们必须改变这种行为。但是,通常我们不想更改iText类本身的代码
幸运的是,LocationTextExtractionStrategy有一个构造函数,允许注入ITextChunkLocationStrategy实现,即ITextChunkLocation实例的工厂对象
因此,对于我们的任务,我们必须编写一个不那么严格的替代ITextChunkLocation实现,以及一个生成ITextChunkLocation实现实例的ITextChunkLocationStrategy实现
不幸的是,TextChunkLocationDefaultImp是iText的内部变量,并且有许多私有变量。因此,我们不能简单地从它派生实现,而是必须将它作为一个整体进行复制和粘贴,并将更改应用于该副本
因此,
类LaxTextChunkLocationStrategy:LocationTextExtractionStrategy.ITextChunkLocationStrategy
{
公共场所选址策略
{
}
公共虚拟ITextChunkLocation CreateLocation TextRenderInfo renderInfo,线段基线
{
返回新的TextChunkLocationLaxImpbaseline.GetStartPoint、baseline.GetEndPoint、renderInfo.GetSingleSpaceWidth;
}
}
类TextChunkLocationLaxImp:ITextChunkLocation
{
私有常量浮点变音符号允许的垂直偏差=2;
私有只读向量定位;
专用只读向量结束位置;
私有只读向量方向向量;
私有只读int方向大小;
私有只读int;
私有只读浮点启动;
私有只读浮点数;
私有只读浮点字符空间宽度;
public TextChunkLocation LaximpVector startLocation、Vector endLocation、float charSpaceWidth
{
this.startolocation=startolocation;
this.endLocation=endLocation;
this.charSpaceWidth=charSpaceWidth;
Vector oVector=endLocation.oc
反倾销;
如果oVector.Length==0
{
oVector=新向量1,0,0;
}
方向向量=OVERECTOR.规格化;
OrientationMagnite=intMath.Atan2orientationVector.GetVector.I2,orientationVector.GetVector.I1*1000;
向量原点=新向量0,0,1;
DistVertical=intstartLocation.Subtractorigin.CrossOriginationVector.GetVector.I3;
distParallelStart=orientationVector.dotstarlocation;
distParallelEnd=方向向量.DotendLocation;
}
公共虚拟国际定位
{
返回方向大小;
}
公共虚拟整数域
{
返回垂直;
}
公共虚拟浮点数启动
{
返回并启动;
}
公共虚拟浮点数
{
返回端;
}
公共虚拟矢量定位
{
返回定位;
}
公共虚拟向量GetEndLocation
{
返回端定位;
}
公共虚拟浮点GetCharSpaceWidth
{
返回字符空间宽度;
}
公共虚拟bool SameLineITextChunkLocation@as
{
如果方向重要性!=@as.orientationmagnity
{
返回false;
}
int dist垂直度diff=distvertical-@as.distvertical;
如果数学横坐标垂直度差<2
{
返回true;
}
LineSegment mySegment=新的LineSegment位置、结束位置;
线段otherSegment=新建LineSegment@as.GetStartLocation,@as.GetEndLocation;
返回数学横坐标垂直度差
提取为
Omschrijving Aantal美术馆
佩尔斯图克·科斯滕
VERHUUR L.GELEVERDE ARBEID PDC 8欧元43,70欧元349,60欧元
VERHUUR O.GELEVERDE ARBEID PDC 3 60,95欧元182,85欧元
VERHUUR L.L.GELEVERDE ARBEID EM 24
€ 32,20 € 772,80
首先,为什么会这样
正如在对问题的评论中推测的那样,确实存在一个小的垂直台阶,在所有行中,前三列设置在相同的垂直位置,后两列的垂直位置略有不同
行第一列y最后列y
标题行536 535.893
第一排516516.229
第二排495 495.478
第三排475 474.788
人们特别认识到,通过文本提取中断的行是其中y位置的小数点前数字不同于536 vs 535、475 vs 474的行,而具有相等小数点前数字的行没有中断
这是因为类TextChunkLocationDefaultImp在默认情况下用于存储文本块位置和比较这些位置的方法,它存储块的y位置,实际上是块的一个抽象,也适用于未在整数变量private readonly int distvertical和测试中水平写入的文本方法SameLine要求距离垂直值相等
命名空间iText.Kernel.Pdf.Canvas.Parser.Listener{
内部类TextChunkLocationDefaultImp:ITextChunkLocation{
...
///方向单位向量的垂直距离,即未旋转坐标系中的Y位置。
///
///
///方向单位向量的垂直距离,即未旋转坐标系中的Y位置。
///我们四舍五入到最接近的整数来处理比较浮点数的模糊性。
///
私有只读int;
...
///要比较的位置
///如果此位置与另一个位置在同一条线上,则为true
公共虚拟bool SameLineITextChunkLocation@as{
...
float dist垂直度diff=distvertical-@as.distvertical;
如果dist垂直度diff==0{
返回true;
}
...
}
...
}
}
实际上,如果比较的文本块中有一个长度为零,则向下的SameLine允许有一个小的偏差。显然,长度为零的文本块有时用于变音标记,而这些标记有时应用于不同的高度。不过,在您的示例文件中,这并不重要
如何修复它
如上所述,问题是由于TextChunkLocationDefaultImp.SameLine的行为造成的。因此,我们必须更改此行为。不过,通常我们不想更改iText类本身的代码
幸运的是,LocationTextExtractionStrategy有一个构造函数,允许注入ITextChunkLocationStrategy实现,即ITextChunkLocation实例的工厂对象
因此,对于我们的任务,我们必须编写一个不那么严格的替代ITextChunkLocation实现,以及一个生成ITe实例的ITextChunkLocationStrategy实现
xtChunkLocation实现
不幸的是,TextChunkLocationDefaultImp是iText的内部变量,并且有许多私有变量。因此,我们不能简单地从它派生实现,而是必须将它作为一个整体进行复制和粘贴,并将更改应用于该副本
因此,
类LaxTextChunkLocationStrategy:LocationTextExtractionStrategy.ITextChunkLocationStrategy
{
公共场所选址策略
{
}
公共虚拟ITextChunkLocation CreateLocation TextRenderInfo renderInfo,线段基线
{
返回新的TextChunkLocationLaxImpbaseline.GetStartPoint、baseline.GetEndPoint、renderInfo.GetSingleSpaceWidth;
}
}
类TextChunkLocationLaxImp:ITextChunkLocation
{
私有常量浮点变音符号允许的垂直偏差=2;
私有只读向量定位;
专用只读向量结束位置;
私有只读向量方向向量;
私有只读int方向大小;
私有只读int;
私有只读浮点启动;
私有只读浮点数;
私有只读浮点字符空间宽度;
public TextChunkLocation LaximpVector startLocation、Vector endLocation、float charSpaceWidth
{
this.startolocation=startolocation;
this.endLocation=endLocation;
this.charSpaceWidth=charSpaceWidth;
向量OVERECTR=结束位置。位置;
如果oVector.Length==0
{
oVector=新向量1,0,0;
}
方向向量=OVERECTOR.规格化;
OrientationMagnite=intMath.Atan2orientationVector.GetVector.I2,orientationVector.GetVector.I1*1000;
向量原点=新向量0,0,1;
DistVertical=intstartLocation.Subtractorigin.CrossOriginationVector.GetVector.I3;
distParallelStart=orientationVector.dotstarlocation;
distParallelEnd=方向向量.DotendLocation;
}
公共虚拟国际定位
{
返回方向大小;
}
公共虚拟整数域
{
返回垂直;
}
公共虚拟浮点数启动
{
返回并启动;
}
公共虚拟浮点数
{
返回端;
}
公共虚拟矢量定位
{
返回定位;
}
公共虚拟向量GetEndLocation
{
返回端定位;
}
公共虚拟浮点GetCharSpaceWidth
{
返回字符空间宽度;
}
公共虚拟bool SameLineITextChunkLocation@as
{
如果方向重要性!=@as.orientationmagnity
{
返回false;
}
int dist垂直度diff=distvertical-@as.distvertical;
如果数学横坐标垂直度差<2
{
返回true;
}
LineSegment mySegment=新的LineSegment位置、结束位置;
线段otherSegment=新建LineSegment@as.GetStartLocation,@as.GetEndLocation;
return Math.absdist垂直度diff请共享有问题的PDF。很可能PDF中的垂直位置有一个小的变化,其中提取的文本有额外的换行符。Hi@mkl从我的角度看,看起来没有,但可能有1个像素。我在这里上传了文件,我不得不过滤掉机密数据。对于vertical位置,你应该检查页面内容流,而不是查看呈现的页面。欢迎来到地狱。幸运的是,mkl可能会给你一些启示。旋转、大小、cropbox、用户单位、iTextractionStrategy…上帝不。简言之:你看到的不是你得到的!是的,这一切都很复杂,我真的希望@mkl能如此我来说说这个例子。请分享有问题的PDF。最有可能的是,在PDF的垂直位置有一个小的变化,其中提取的文本有额外的换行符。您好@mkl从我看来,它看起来没有,但它可能像1个像素。我在这里上传了文件,我不得不过滤掉机密数据。对于垂直位置,您可以应该检查页面内容流,而不是查看呈现的页面。欢迎来到地狱。幸运的是,mkl可能会给你一些启示。旋转、大小、cropbox、用户单位、iTextractionStrategy…上帝不。简言之:你看到的不是你得到的!是的,这一切都很复杂,我真的希望@mkl能对此有所启示s案例。谢谢你的详细解释,这很好!我可以问你如何验证pdf以了解行的准确位置吗?@thekguy我可以问你如何验证pdf以了解行的准确位置吗?-请参阅新的部分如何检查pdf以了解我在答案中编辑的行的准确位置。谢谢你他详细解释说,这很好!我可以问一下你是如何解读pdf以了解行的确切位置的吗?@thekguy我可以问一下你是如何解读p的吗 df是否知道行的确切位置?-请参阅新的部分如何检查pdf以了解我在答案中编辑的行的确切位置。
item[tab]item[tab]item[tab]item[tab]item
item[tab]item[tab]item[tab]
item[tab]item
item[tab]item[tab]item[tab]item[tab]item