提取文本从PDF文件使用iTextSharp的文本矩形的坐标 [英] Extract text and text rectangle coordinates from a Pdf file using itextsharp
问题描述
我试图让所有的单词和从PDF文件中的位置坐标。我已经使用Acrobat的API上 .NET
成功。现在,我试图让使用免费的API,如iTextSharp的(即 .NET
版)相同的结果。我可以在文本的 PRTokeniser
(逐行),但我不知道如何获得该行的坐标,更何况每个单词的。
I'm trying to get all words and their location coordinates from a PDF file. I've succeeded using the Acrobat API on .NET
. Now, I'm trying to get the same result using a free API, such as iTextSharp (the .NET
version). I can get the text (line by line) with PRTokeniser
, but I have no idea of how to get the coordinates of the line, let alone of each word.
推荐答案
我的帐户是马克·斯托勒的回答太新的回复。
My account is too new reply to Mark Storer's answer.
我不能够直接使用LocationTextExtracationStrategy(我想我必须做一些错误的)。当我用LocationTextExtracationStrategy我能得到的文本,但我无法弄清楚如何让COORDS每串(或串线)。
I wasn't able to directly use the LocationTextExtracationStrategy (I think I must be doing something wrong). When I used the LocationTextExtracationStrategy I was able to get the text but I couldn't figure out how to get the coords for each string (or line of strings).
我最终继承了LocationTextExtracationStrategy和暴露,我想是因为它内部有它的数据。
I ended up subclassing the LocationTextExtracationStrategy and exposing the data I wanted because it does have it internally.
我也希望它在.NET ......所以这里是一个草率的C#版本是我放在一起。
I also wanted it in .net... so here is a sloppy C# version of what I put together.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTextSharp.text.pdf.parser;
namespace PdfHelper
{
/// <summary>
/// Taken from http://www.java-frameworks.com/java/itext/com/itextpdf/text/pdf/parser/LocationTextExtractionStrategy.java.html
/// </summary>
class LocationTextExtractionStrategyEx : LocationTextExtractionStrategy
{
private List<TextChunk> m_locationResult = new List<TextChunk>();
private List<TextInfo> m_TextLocationInfo = new List<TextInfo>();
public List<TextChunk> LocationResult
{
get { return m_locationResult; }
}
public List<TextInfo> TextLocationInfo
{
get { return m_TextLocationInfo; }
}
/// <summary>
/// Creates a new LocationTextExtracationStrategyEx
/// </summary>
public LocationTextExtractionStrategyEx()
{
}
/// <summary>
/// Returns the result so far
/// </summary>
/// <returns>a String with the resulting text</returns>
public override String GetResultantText()
{
m_locationResult.Sort();
StringBuilder sb = new StringBuilder();
TextChunk lastChunk = null;
TextInfo lastTextInfo = null;
foreach (TextChunk chunk in m_locationResult)
{
if (lastChunk == null)
{
sb.Append(chunk.Text);
lastTextInfo = new TextInfo(chunk);
m_TextLocationInfo.Add(lastTextInfo);
}
else
{
if (chunk.sameLine(lastChunk))
{
float dist = chunk.distanceFromEndOf(lastChunk);
if (dist < -chunk.CharSpaceWidth)
{
sb.Append(' ');
lastTextInfo.addSpace();
}
//append a space if the trailing char of the prev string wasn't a space && the 1st char of the current string isn't a space
else if (dist > chunk.CharSpaceWidth / 2.0f && chunk.Text[0] != ' ' && lastChunk.Text[lastChunk.Text.Length - 1] != ' ')
{
sb.Append(' ');
lastTextInfo.addSpace();
}
sb.Append(chunk.Text);
lastTextInfo.appendText(chunk);
}
else
{
sb.Append('\n');
sb.Append(chunk.Text);
lastTextInfo = new TextInfo(chunk);
m_TextLocationInfo.Add(lastTextInfo);
}
}
lastChunk = chunk;
}
return sb.ToString();
}
/// <summary>
///
/// </summary>
/// <param name="renderInfo"></param>
public override void RenderText(TextRenderInfo renderInfo)
{
LineSegment segment = renderInfo.GetBaseline();
TextChunk location = new TextChunk(renderInfo.GetText(), segment.GetStartPoint(), segment.GetEndPoint(), renderInfo.GetSingleSpaceWidth(), renderInfo.GetAscentLine(), renderInfo.GetDescentLine());
m_locationResult.Add(location);
}
public class TextChunk : IComparable, ICloneable
{
string m_text;
Vector m_startLocation;
Vector m_endLocation;
Vector m_orientationVector;
int m_orientationMagnitude;
int m_distPerpendicular;
float m_distParallelStart;
float m_distParallelEnd;
float m_charSpaceWidth;
public LineSegment AscentLine;
public LineSegment DecentLine;
public object Clone()
{
TextChunk copy = new TextChunk(m_text, m_startLocation, m_endLocation, m_charSpaceWidth, AscentLine, DecentLine);
return copy;
}
public string Text
{
get { return m_text; }
set { m_text = value; }
}
public float CharSpaceWidth
{
get { return m_charSpaceWidth; }
set { m_charSpaceWidth = value; }
}
public Vector StartLocation
{
get { return m_startLocation; }
set { m_startLocation = value; }
}
public Vector EndLocation
{
get { return m_endLocation; }
set { m_endLocation = value; }
}
/// <summary>
/// Represents a chunk of text, it's orientation, and location relative to the orientation vector
/// </summary>
/// <param name="txt"></param>
/// <param name="startLoc"></param>
/// <param name="endLoc"></param>
/// <param name="charSpaceWidth"></param>
public TextChunk(string txt, Vector startLoc, Vector endLoc, float charSpaceWidth, LineSegment ascentLine, LineSegment decentLine)
{
m_text = txt;
m_startLocation = startLoc;
m_endLocation = endLoc;
m_charSpaceWidth = charSpaceWidth;
AscentLine = ascentLine;
DecentLine = decentLine;
m_orientationVector = m_endLocation.Subtract(m_startLocation).Normalize();
m_orientationMagnitude = (int)(Math.Atan2(m_orientationVector[Vector.I2], m_orientationVector[Vector.I1]) * 1000);
// see http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
// the two vectors we are crossing are in the same plane, so the result will be purely
// in the z-axis (out of plane) direction, so we just take the I3 component of the result
Vector origin = new Vector(0, 0, 1);
m_distPerpendicular = (int)(m_startLocation.Subtract(origin)).Cross(m_orientationVector)[Vector.I3];
m_distParallelStart = m_orientationVector.Dot(m_startLocation);
m_distParallelEnd = m_orientationVector.Dot(m_endLocation);
}
/// <summary>
/// true if this location is on the the same line as the other text chunk
/// </summary>
/// <param name="textChunkToCompare">the location to compare to</param>
/// <returns>true if this location is on the the same line as the other</returns>
public bool sameLine(TextChunk textChunkToCompare)
{
if (m_orientationMagnitude != textChunkToCompare.m_orientationMagnitude) return false;
if (m_distPerpendicular != textChunkToCompare.m_distPerpendicular) return false;
return true;
}
/// <summary>
/// Computes the distance between the end of 'other' and the beginning of this chunk
/// in the direction of this chunk's orientation vector. Note that it's a bad idea
/// to call this for chunks that aren't on the same line and orientation, but we don't
/// explicitly check for that condition for performance reasons.
/// </summary>
/// <param name="other"></param>
/// <returns>the number of spaces between the end of 'other' and the beginning of this chunk</returns>
public float distanceFromEndOf(TextChunk other)
{
float distance = m_distParallelStart - other.m_distParallelEnd;
return distance;
}
/// <summary>
/// Compares based on orientation, perpendicular distance, then parallel distance
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public int CompareTo(object obj)
{
if (obj == null) throw new ArgumentException("Object is now a TextChunk");
TextChunk rhs = obj as TextChunk;
if (rhs != null)
{
if (this == rhs) return 0;
int rslt;
rslt = m_orientationMagnitude - rhs.m_orientationMagnitude;
if (rslt != 0) return rslt;
rslt = m_distPerpendicular - rhs.m_distPerpendicular;
if (rslt != 0) return rslt;
// note: it's never safe to check floating point numbers for equality, and if two chunks
// are truly right on top of each other, which one comes first or second just doesn't matter
// so we arbitrarily choose this way.
rslt = m_distParallelStart < rhs.m_distParallelStart ? -1 : 1;
return rslt;
}
else
{
throw new ArgumentException("Object is now a TextChunk");
}
}
}
public class TextInfo
{
public Vector TopLeft;
public Vector BottomRight;
private string m_Text;
public string Text
{
get { return m_Text; }
}
/// <summary>
/// Create a TextInfo.
/// </summary>
/// <param name="initialTextChunk"></param>
public TextInfo(TextChunk initialTextChunk)
{
TopLeft = initialTextChunk.AscentLine.GetStartPoint();
BottomRight = initialTextChunk.DecentLine.GetEndPoint();
m_Text = initialTextChunk.Text;
}
/// <summary>
/// Add more text to this TextInfo.
/// </summary>
/// <param name="additionalTextChunk"></param>
public void appendText(TextChunk additionalTextChunk)
{
BottomRight = additionalTextChunk.DecentLine.GetEndPoint();
m_Text += additionalTextChunk.Text;
}
/// <summary>
/// Add a space to the TextInfo. This will leave the endpoint out of sync with the text.
/// The assumtion is that you will add more text after the space which will correct the endpoint.
/// </summary>
public void addSpace()
{
m_Text += ' ';
}
}
}
}
我添加了一个TextLocationInfo财产双手回的文本行+的COORDS这些行的列表(左上和右下),它可以用来给你一个边界框。
I added a TextLocationInfo property which hands back a List of lines of text + the coords for those lines (upper left and lower right) which can be used to give you a bounding box.
我也看到了一些奇怪的与我最初的玩弄。它看起来像我一样COORDS,如果我把起始点和放大器;端点从基线(我认为正确的事情,而我所做的,就是从ascentLine和DecentLine拉这些点)。我最初的传球我刚才使用的基线。奇怪的是,我没有看到在结果COORDS的差异。所以字的警惕......我不知道,如果我提供COORDS是正确的......我只是觉得他们是/应该的。
I also saw something odd with my initial playing around. It looked like I got the same coords if I pulled the startPoint & endPoint from the baseline (I think the right thing to do, and the thing I did, was to pull those points from ascentLine and DecentLine). My initial pass I just used the baseline. Odd that I didn't see a difference in the resulting coords. So word to the wary... I'm not sure if the coords I'm providing are right... I just think they are/should be.
这篇关于提取文本从PDF文件使用iTextSharp的文本矩形的坐标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!