C# Extract text from PDF using PdfSharp
Asked Answered
R

4

67

Is there a possibility to extract plain text from a PDF-File with PdfSharp? I don't want to use iTextSharp because of its license.

Recall answered 13/4, 2012 at 12:48 Comment(2)
Just wondering, why downvotes? (There are no clarifying comments to help author to improve the question.)Vela
You need to extract the ToUnicode CMaps from the document to convert the binary indexes of the text-strings, unless you're lucky and the binary indexes are ASCII values themselves.Haar
F
57

Took Sergio's answer and made some extension methods. I also changed the accumulation of strings into an iterator.

public static class PdfSharpExtensions
{
    public static IEnumerable<string> ExtractText(this PdfPage page)
    {       
        var content = ContentReader.ReadContent(page);      
        var text = content.ExtractText();
        return text;
    }   

    public static IEnumerable<string> ExtractText(this CObject cObject)
    {   
        if (cObject is COperator)
        {
            var cOperator = cObject as COperator;
            if (cOperator.OpCode.Name== OpCodeName.Tj.ToString() ||
                cOperator.OpCode.Name == OpCodeName.TJ.ToString())
            {
                foreach (var cOperand in cOperator.Operands)
                    foreach (var txt in ExtractText(cOperand))
                        yield return txt;   
            }
        }
        else if (cObject is CSequence)
        {
            var cSequence = cObject as CSequence;
            foreach (var element in cSequence)
                foreach (var txt in ExtractText(element))
                    yield return txt;
        }
        else if (cObject is CString)
        {
            var cString = cObject as CString;
            yield return cString.Value;
        }
    }
}
Fundus answered 4/6, 2014 at 19:37 Comment(9)
I am using PDFsharp library but it say ContentReader Class is out of context.What could be the problem?Deadeye
ContentReader Class is out of context.Fundus
Couldn't resist. IDK what that means or how to fix it. I try to avoid working with PDF's like the plague because the tools to work with them are crap and pretending that a human readable format is machine readable is a total fools errand.Fundus
PdfSharp v1.32.3057 has a bug where ContentReader.ReadContent hangs. To fix, there are some changes needed (see here). After fixing the bug, I can confirm this works. :-)Fascista
Namespace for ContentReader : PdfSharp.Pdf.Content.ContentReader.Despoil
Although this is promising, it does not work for Unicode texts.Despoil
It seems that it works fine when OpCode.Name == "Tj" (which, I guess, is related to ASCII) and return gibberish when OpCode.Name == "TJ" (which, I guess, is Unicode).Despoil
TJ allows glyph-spacing, and to that end, will have integer values between its strings. Neither Tj nor TJ are related to ASCII: both use binary indexes which cannot be depended on to be ASCII.Haar
This works OOTB copy/paste for me in Jan 2021 against PDFs we create from our AS400 / i5. Many ThanksWeldonwelfare
T
23

I have implemented it somehow similar to how David did it. Here is my code:

...
{
    // ....
    var page = document.Pages[1];
    CObject content = ContentReader.ReadContent(page);
    var extractedText = ExtractText(content);
    // ...
}

private IEnumerable<string> ExtractText(CObject cObject)
{
    var textList = new List<string>();
    if (cObject is COperator)
    {
        var cOperator = cObject as COperator;
        if (cOperator.OpCode.Name == OpCodeName.Tj.ToString() ||
            cOperator.OpCode.Name == OpCodeName.TJ.ToString())
        {
            foreach (var cOperand in cOperator.Operands)
            {
                textList.AddRange(ExtractText(cOperand));
            }
        }
    }
    else if (cObject is CSequence)
    {
        var cSequence = cObject as CSequence;
        foreach (var element in cSequence)
        {
            textList.AddRange(ExtractText(element));
        }
    }
    else if (cObject is CString)
    {
        var cString = cObject as CString;
        textList.Add(cString.Value);
    }
    return textList;
}
Thighbone answered 15/5, 2014 at 1:2 Comment(1)
You shouldn't have stripped down the StringBuilder, PDFs are quite big and that solution will cause a huge unnecessary memory consumption.Champaign
K
14

PDFSharp provides all the tools to extract the text from a PDF. Use the ContentReader class to access the commands within each page and extract the strings from TJ/Tj operators.

I've uploaded a simple implementation to github.

Khan answered 1/8, 2013 at 8:36 Comment(5)
On many PDFs CString.Value returns just some junk (e.g. create a PDF using OpenOffice.org and try to import it using this method).Champaign
No, PdfSharp does not provide all the tools for text extraction. Functionality has yet to be added for ToUnicode CMaps, which are necessary to extract the text of Unicode PDFs.Haar
Because that's the choice I made.Khan
it doesn't seem to be perfect, one word could be split in few lines, eg: Pre dic t ion i s veJavanese
@Javanese Yeah, you'll need a strong AI then to salvage the text from your PDF.Khan
M
-2

Using this method I actually recently figured out how to do it for what you guys are calling unicode. But it's not exactly unicode, its PdfEncoding. Embedded Fonts causes the pdf to make differences tables called CMaps that you have to store and swap out the pdfEncoding unicode values, until you find one in the cmap table and put it there instead. I turned symbols into readable text and it took 3 weeks of learning about pdf file structures. You'll also need sharpZipLib to inflate the cmap tables as they are compressed.

Mesocarp answered 31/3, 2023 at 3:41 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Ahmad

© 2022 - 2024 — McMap. All rights reserved.