首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >LocationTextExtractionStrategy/HorizontalTextExtractionStrategy将文本拆分为单个字符

LocationTextExtractionStrategy/HorizontalTextExtractionStrategy将文本拆分为单个字符
EN

Stack Overflow用户
提问于 2016-11-17 15:39:30
回答 2查看 1.9K关注 0票数 0

我使用了LocationTextExtractionStrategy的扩展版本来提取一个pdf的连接文本及其位置/大小。我是通过使用locationalResult来做到这一点的。在我测试一个包含不同字体文本的pdf格式之前,这是很好的。突然,这些文字被分割成单个字符或小片段。

例如,"Detail“不是locationalResult列表中的任何一个对象,而是拆分为六个项(D、e、t、a、i、l)。

我尝试通过公开HorizontalTextExtractionStrategy方法来使用getLocationalResult:

代码语言:javascript
运行
复制
public List<TextChunk> GetLocationalResult()
{
    return (List<TextChunk>)locationalResultField.GetValue(this);
}

并使用PdfReaderContentParser提取文本:

代码语言:javascript
运行
复制
reader = new PdfReader("some_pdf");
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
var strategy = parser.ProcessContent(i, HorizontalTextExtractionStrategy());

foreach (HorizontalTextExtractionStrategy.HorizontalTextChunk chunk in strategy.GetLocationalResult())
{
    // Do something with the chunk     
}

但这也会返回相同的结果。有没有其他方法从pdf中提取连接的文本?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-11-21 08:28:59

在深入调试iTextSharp库之后,我发现我的文本是用TJ操作符绘制的,mkl也提到了这一点。

代码语言:javascript
运行
复制
[<0027> -0.2<00280037> 0.2<0024002c> 0.2<002f> -0.2<0003>] TJ

iText不是将这些文本作为一个PdfString来处理,而是作为一个PdfObjects数组来处理,该数组的结果是为其中的每个PdfString项调用renderListener.RenderText(renderInfo) (参见ShowTextArray类和DisplayPdfString方法)。然而,在RenderText方法中,有关数组中pdf字符串关系的信息丢失了,每个项目都作为一个独立的对象添加到locationalResult中。

因为我的目标是提取“单个文本绘图指令的参数”,所以我扩展了PdfContentStreamProcessor类关于一个新方法ProcessTexts,该方法返回这些原子字符串的列表。我的解决方法不是很好,因为我不得不从原始源复制一些私有字段和方法,但是它对我很有用。

代码语言:javascript
运行
复制
class PdfContentStreamProcessorEx : PdfContentStreamProcessor
{
    private IDictionary<int, CMapAwareDocumentFont> cachedFonts = new Dictionary<int, CMapAwareDocumentFont>();
    private ResourceDictionary resources = new ResourceDictionary();
    private CMapAwareDocumentFont font = null;

    public PdfContentStreamProcessorEx(IRenderListener renderListener) : base(renderListener)
    {
    }

    public List<string> ProcessTexts(byte[] contentBytes, PdfDictionary resources)
    {
        this.resources.Push(resources);
        var texts = new List<string>();
        PRTokeniser tokeniser = new PRTokeniser(new RandomAccessFileOrArray(new RandomAccessSourceFactory().CreateSource(contentBytes)));
        PdfContentParser ps = new PdfContentParser(tokeniser);
        List<PdfObject> operands = new List<PdfObject>();
        while (ps.Parse(operands).Count > 0)
        {
            PdfLiteral oper = (PdfLiteral)operands[operands.Count - 1];
            if ("Tj".Equals(oper.ToString()))
            {
                texts.Add(getText((PdfString)operands[0]));
            }
            else if ("TJ".Equals(oper.ToString()))
            {
                string text = string.Empty;
                foreach (PdfObject entryObj in (PdfArray)operands[0])
                {
                    if (entryObj is PdfString)
                    {
                        text += getText((PdfString)entryObj);
                    }
                }
                texts.Add(text);
            }
            else if ("Tf".Equals(oper.ToString()))
            {
                PdfName fontResourceName = (PdfName)operands[0];
                float size = ((PdfNumber)operands[1]).FloatValue;

                PdfDictionary fontsDictionary = resources.GetAsDict(PdfName.FONT);
                CMapAwareDocumentFont _font;
                PdfObject fontObject = fontsDictionary.Get(fontResourceName);
                if (fontObject is PdfDictionary)
                    _font = GetFont((PdfDictionary)fontObject);
                else
                    _font = GetFont((PRIndirectReference)fontObject);

                font = _font;
            }
        }

        this.resources.Pop();

        return texts;
    }

    string getText(PdfString @in)
    {
        byte[] bytes = @in.GetBytes();
        return font.Decode(bytes, 0, bytes.Length);
    }

    private CMapAwareDocumentFont GetFont(PRIndirectReference ind)
    {
        CMapAwareDocumentFont font;
        cachedFonts.TryGetValue(ind.Number, out font);
        if (font == null)
        {
            font = new CMapAwareDocumentFont(ind);
            cachedFonts[ind.Number] = font;
        }
        return font;
    }

    private CMapAwareDocumentFont GetFont(PdfDictionary fontResource)
    {
        return new CMapAwareDocumentFont(fontResource);
    }

    private class ResourceDictionary : PdfDictionary
    {
        private IList<PdfDictionary> resourcesStack = new List<PdfDictionary>();

        virtual public void Push(PdfDictionary resources)
        {
            resourcesStack.Add(resources);
        }

        virtual public void Pop()
        {
            resourcesStack.RemoveAt(resourcesStack.Count - 1);
        }

        public override PdfObject GetDirectObject(PdfName key)
        {
            for (int i = resourcesStack.Count - 1; i >= 0; i--)
            {
                PdfDictionary subResource = resourcesStack[i];
                if (subResource != null)
                {
                    PdfObject obj = subResource.GetDirectObject(key);
                    if (obj != null) return obj;
                }
            }
            return base.GetDirectObject(key); // shouldn't be necessary, but just in case we've done something crazy
        }
    }
}
票数 0
EN

Stack Overflow用户

发布于 2016-11-20 22:48:46

我使用了LocationTextExtractionStrategy的扩展版本来提取一个pdf的连接文本及其位置/大小。我是通过使用locationalResult来做到这一点的。这是很好的工作,直到我测试了一个pdf包含不同字体的文本(ttf)。突然,这些文字被分割成单个字符或小片段。

这个问题是由于对LocationTextExtractionStrategy.locationalResult私有列表成员变量内容的错误期望造成的。

这个TextChunk实例列表包含从解析框架转发到策略的文本片段(或者可能是某些过滤器类预处理的文本),该框架分别转发它在内容流中遇到的每个字符串。

因此,如果内容流中看似连接的单词实际上是使用多个字符串绘制的,则可以获得多个TextChunk实例。

实际上,getResultantText方法中有一些“智能”,可以正确地连接这些块,在必要时添加一个空间等等。

在您的文档中,“详细信息”通常如下所示:

代码语言:javascript
运行
复制
[<0027> -0.2<00280037> 0.2<0024002c> 0.2<002f> -0.2<0003>] TJ 

如您所见,在“D”和“E”、“T”和“A”、“I”和“L”以及“L”和“”之间有轻微的文本插入点移动。(这种小幅度的移动通常代表着市场竞争。)因此,您将得到“D”、“ET”、“AI”和“L”的单独TextChunk实例。

诚然,LocationTextExtractionStrategy.locationalResult成员没有很好的文档;但由于它是一个私人成员,这个IMHO是可以原谅的。

对于许多文档来说,这是因为许多PDF创建者没有应用kerning和使用单个字符串对象简单地绘制连接的文本。

HorizontalTextExtractionStrategy是从LocationTextExtractionStrategy派生而来的,主要区别在于它将TextChunk实例排列成一个字符串的方式。因此,您将在这里看到同样的碎片。

有没有其他方法从pdf中提取连接的文本?

如果您想要像“内容流中的原子字符串对象”中的“连接文本”,那么您已经拥有它们了。

如果您想要“连接文本”(如“可视连接文本,无论组成字母在内容流中绘制在哪里”),则必须将这些TextChunk实例(如getResultantText中的LocationTextExtractionStrategyHorizontalTextExtractionStrategy do )与它们各自的TextChunkLocationDefaultImpHorizontalTextChunkLocation实现中的比较方法结合在一起。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40659056

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档