Сторонняя библиотека PDF значительно медленнее при запуске NUnit - PullRequest
1 голос
/ 13 января 2012

Я оцениваю библиотеку Winnovative PdfToText и столкнулся с тем, что меня беспокоит.

Все работает нормально, и я могу сразу извлечь текстовое содержимое из небольшого pdf размером 20 КБ или меньше, если я запускаю консольное приложение. Тем не менее, если я вызываю тот же код из выполняемого графического интерфейса NUnit, это занимает 15-25 секунд (я проверил, что это PdfToText, поместив точку останова на строку, которая извлекает текст, и нажал F10, чтобы увидеть, сколько времени требуется, чтобы перейти к следующая строка).

Это касается меня, потому что я не знаю, где обвинить, потому что я не знаю причину. Есть ли проблема с NUnit или PdfToText? Все, что я хочу сделать, это извлечь текст из PDF-файла, но 20 секунд совершенно неразумно, если я собираюсь увидеть такое поведение при определенных условиях. Если это просто при запуске NUnit, это приемлемо, но в противном случае мне придется искать в другом месте.

Проще продемонстрировать проблему с помощью полного решения VS (2010), поэтому вот ссылка, облегчающая настройку и запуск (не нужно загружать NUnit или PdfToText или даже образец pdf): http://dl.dropbox.com/u/273037/PdfToTextProblem.zip (Возможно, вам придется изменить ссылку на PdfToText, чтобы использовать dll x86, если вы работаете на 32-разрядной машине).

Просто нажмите F5 и загрузится бегун NUnit Gui.

Я не привязан к этой библиотеке, если у вас есть предложения, я попробовал iTextSharp (слишком дорого для 2 строк кода) и посмотрел на Aspose (я не пробовал, но лицензия SaaS $ 11k). Но им либо не хватает необходимой функциональности, либо они слишком дороги.

Ответы [ 2 ]

0 голосов
/ 18 января 2012

Ниже приведен код, который я использовал для извлечения текста из PDF с помощью iTextSharp v4.1.6 . Если это кажется слишком многословным, это связано с тем, как я его использую, и необходимой гибкостью.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTextSharp.text.pdf;

namespace ClassLibrary1
{
    public class PdfToken
    {
        private PdfToken(int type, string value)
        {
            Type = type;
            Value = value;
        }

        public static PdfToken Create(PRTokeniser tokenizer)
        {
            return new PdfToken(tokenizer.TokenType, tokenizer.StringValue);
        }

        public int Type { get; private set; }
        public string Value { get; private set; }
        public bool IsOperand
        {
            get
            {
                return Type == PRTokeniser.TK_OTHER;
            }
        }
    }

    public class PdfOperation
    {
        public PdfOperation(PdfToken operationToken, IEnumerable<PdfToken> arguments)
        {
            Name = operationToken.Value;
            Arguments = arguments;
        }

        public string Name { get; private set; }
        public IEnumerable<PdfToken> Arguments { get; private set; }
    }

    public interface IPdfParsingStrategy
    {
        void Execute(PdfOperation op);
    }

    public class PlainTextParsingStrategy : IPdfParsingStrategy
    {
        StringBuilder text = new StringBuilder();

        public PlainTextParsingStrategy()
        {

        }

        public String GetText()
        {
            return text.ToString();
        }

        #region IPdfParsingStrategy Members

        public void Execute(PdfOperation op)
        {
            // see Adobe PDF specs for additional operations
            switch (op.Name)
            {
                case "TJ":
                    PrintText(op);
                    break;
                case "Tm":
                    SetMatrix(op);
                    break;
                case "Tf":
                    SetFont(op);
                    break;
                case "S":
                    PrintSection(op);
                    break;
                case "G":
                case "g":
                case "rg":
                    SetColor(op);
                    break;
            }
        }

        #endregion

        bool newSection = false;

        private void PrintSection(PdfOperation op)
        {
            text.AppendLine("------------------------------------------------------------");
            newSection = true;
        }

        private void PrintNewline(PdfOperation op)
        {
            text.AppendLine();
        }

        private void PrintText(PdfOperation op)
        {
            if (newSection)
            {
                newSection = false;
                StringBuilder header = new StringBuilder();
                PrintText(op, header);
            }

            PrintText(op, text);
        }

        private static void PrintText(PdfOperation op, StringBuilder text)
        {
            foreach (PdfToken t in op.Arguments)
            {
                switch (t.Type)
                {
                    case PRTokeniser.TK_STRING:
                        text.Append(t.Value);
                        break;
                    case PRTokeniser.TK_NUMBER:
                        text.Append(" ");
                        break;
                }
            }
        }

        String lastFont = String.Empty;
        String lastFontSize = String.Empty;

        private void SetFont(PdfOperation op)
        {
            var args = op.Arguments.ToList();
            string font = args[0].Value;
            string size = args[1].Value;

            //if (font != lastFont || size != lastFontSize)
            //    text.AppendLine();

            lastFont = font;
            lastFontSize = size;
        }

        String lastX = String.Empty;
        String lastY = String.Empty;

        private void SetMatrix(PdfOperation op)
        {
            var args = op.Arguments.ToList();
            string x = args[4].Value;
            string y = args[5].Value;

            if (lastY != y)
                text.AppendLine();
            else if (lastX != x)
                text.Append(" ");

            lastX = x;
            lastY = y;
        }

        String lastColor = String.Empty;

        private void SetColor(PdfOperation op)
        {
            lastColor = PrintCommand(op).Replace(" ", "_");
        }

        private static string PrintCommand(PdfOperation op)
        {
            StringBuilder text = new StringBuilder();
            foreach (PdfToken t in op.Arguments)
                text.AppendFormat("{0} ", t.Value);
            text.Append(op.Name);
            return text.ToString();
        }

    }
}

А вот как я это называю:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using iTextSharp.text.pdf;

namespace ClassLibrary1
{
    public class PdfExtractor
    {
        public static string GetText(byte[] pdfBuffer)
        {
            PlainTextParsingStrategy strategy = new PlainTextParsingStrategy();
            ParsePdf(pdfBuffer, strategy);
            return strategy.GetText();
        }

        private static void ParsePdf(byte[] pdf, IPdfParsingStrategy strategy)
        {
            PdfReader reader = new PdfReader(pdf);

            for (int i = 1; i <= reader.NumberOfPages; i++)
            {
                byte[] page = reader.GetPageContent(i);
                if (page != null)
                {
                    PRTokeniser tokenizer = new PRTokeniser(page);
                    List<PdfToken> parameters = new List<PdfToken>();

                    while (tokenizer.NextToken())
                    {
                        var token = PdfToken.Create(tokenizer);
                        if (token.IsOperand)
                        {
                            strategy.Execute(new PdfOperation(token, parameters));
                            parameters.Clear();
                        }
                        else
                        {
                            parameters.Add(token);
                        }
                    }
                }
            }

        }
    }
}
0 голосов
/ 17 января 2012

(комментарий превратился в ответ)

Насколько сложны ваши PDF-файлы? Версия iText для 4.1.6 позволяет использовать закрытое решение. Хотя 4.1.6 напрямую не имеет экстрактора текста, его не так уж сложно написать с помощью PdfReader и GetPageContent ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...