Как можно редактировать линии, кривые и свойства векторной графики в существующем PDF? - PullRequest
0 голосов
/ 15 февраля 2019

У меня есть большая коллекция PDF-файлов с векторной графикой (в основном, линиями и кривыми), которые необходимо каким-то образом редактировать в пакетном режиме, чтобы изменить их основные свойства, такие как типы углов и типы концов.Это может также применяться для редактирования толщины и цвета.

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

using iTextSharp.text;
using iTextSharp.text.pdf;

// open the reader
PdfReader reader = new PdfReader(refPath);
Rectangle size = reader.GetPageSizeWithRotation(1);
Document document = new Document(size);

// open the writer
FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();

// the pdf content
PdfContentByte cb = writer.DirectContent;

//get an image to be inserted.
var screenshot = System.Drawing.Image.FromFile("somefile.png");

//Create iTextSharp image
Image bg = Image.GetInstance(screenshot, System.Drawing.Imaging.ImageFormat.Png);
bg.SetDpi(dpi, dpi);
bg.ScaleToFit(size);
bg.SetAbsolutePosition(0, 0);
bg.Alignment = Image.UNDERLYING;

cb.AddImage(bg);

/**
Get and edit linework properties in here somewhere???
**/

// create the new page and add it to the pdf
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);

// close the streams
document.Close();
fs.Close();
writer.Close();
reader.Close();

В идеале вывод всех линийбудет выглядеть примерно так:

Любые идеи приветствуются!

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Ваше изображение показывает, что вы хотите редактировать заглавные и строчные строки во всех путях, чтобы они были круглыми.

К сожалению, вы не предоставили доступ к типичным файлам примеров, поэтому мне пришлось создавать их самостоятельно, используя смесь различныхстили cap и join и форма пути, напоминающая ваш:

Paths.pdf

Я бы предложил для вашей задачи использовать общий PdfContentStreamEditor из этот ответ , поскольку он выполняет всю тяжелую работу, и мы можем сосредоточиться на поставленной задаче.

Итак, что должна делать наша реализация редактора потоков?Он должен установить cap и join style на "round" и предотвратить переопределение этих настроек.Изучив спецификацию PDF, мы увидим, что стили cap и join являются параметрами текущего графического состояния и могут быть установлены напрямую с помощью инструкций J и j соответственно или с помощью Записи LC и LJ в Словаре параметров состояния графики.

Таким образом, мы можем реализовать наш редактор потоков, просто инициализировав cap и стиль соединения для round, а затем отбросив все J и j инструкции и повторно инициализировать cap и стили соединения после каждого графического состояния gs инструкция.

class PathMakeCapAndJoinRound : PdfContentStreamEditor
{
    protected override void Write(PdfContentStreamProcessor processor, PdfLiteral operatorLit, List<PdfObject> operands)
    {
        if (start)
        {
            initializeCapAndJoin(processor);
            start = false;
        }
        if (CAP_AND_JOIN_OPERATORS.Contains(operatorLit.ToString()))
        {
            return;
        }
        base.Write(processor, operatorLit, operands);
        if (GSTATE_OPERATOR == operatorLit.ToString())
        {
            initializeCapAndJoin(processor);
        }
    }

    void initializeCapAndJoin(PdfContentStreamProcessor processor)
    {
        PdfLiteral operatorLit = new PdfLiteral("J");
        List<PdfObject> operands = new List<PdfObject> { new PdfNumber(PdfContentByte.LINE_CAP_ROUND), operatorLit };
        base.Write(processor, operatorLit, operands);

        operatorLit = new PdfLiteral("j");
        operands = new List<PdfObject> { new PdfNumber(PdfContentByte.LINE_JOIN_ROUND), operatorLit };
        base.Write(processor, operatorLit, operands);
    }

    List<string> CAP_AND_JOIN_OPERATORS = new List<string> { "j", "J" };
    string GSTATE_OPERATOR = "gs";
    bool start = true;
}

Применяя его таким образом кPDF выше

using (PdfReader pdfReader = new PdfReader(testDocument))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(@"Paths-Rounded.pdf", FileMode.Create, FileAccess.Write), (char)0, true))
{
    pdfStamper.RotateContents = false;
    PdfContentStreamEditor editor = new PathMakeCapAndJoinRound();

    for (int i = 1; i <= pdfReader.NumberOfPages; i++)
    {
        editor.EditPage(pdfStamper, i);
    }
}

мы получаем результат:

Paths-Rounded.pdf


Осторожно, ограничения из ссылочного ответа остаются.В частности, этот редактор редактирует только поток содержимого страницы.Чтобы получить полное решение, вы также должны отредактировать все потоки форм XObject и Pattern, а также обработать аннотации.


Чтобы разрешить воспроизведение, я создал свой тестовый документ так:

byte[] createMixedPathsPdf()
{
    using (MemoryStream memoryStream = new MemoryStream())
    {
        using (Document document = new Document())
        {
            PdfWriter writer = PdfWriter.GetInstance(document, memoryStream);
            document.Open();
            var canvas = writer.DirectContent;
            canvas.SetLineWidth(10);

            canvas.MoveTo(100, 700);
            canvas.CurveTo(180, 720, 180, 720, 200, 800);
            canvas.CurveTo(220, 720, 220, 720, 350, 700);
            canvas.MoveTo(350, 700);
            canvas.CurveTo(220, 680, 220, 680, 210, 650);
            canvas.Stroke();

            canvas.SetLineCap(PdfContentByte.LINE_CAP_BUTT);
            canvas.SetLineJoin(PdfContentByte.LINE_JOIN_BEVEL);
            canvas.SetGState(createGState(PdfContentByte.LINE_CAP_BUTT, PdfContentByte.LINE_JOIN_BEVEL));
            canvas.MoveTo(100, 500);
            canvas.CurveTo(180, 520, 180, 520, 200, 600);
            canvas.CurveTo(220, 520, 220, 520, 350, 500);
            canvas.MoveTo(350, 500);
            canvas.CurveTo(220, 480, 220, 480, 210, 450);
            canvas.Stroke();

            canvas.SetLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
            canvas.SetLineJoin(PdfContentByte.LINE_JOIN_MITER);
            canvas.SetGState(createGState(PdfContentByte.LINE_CAP_PROJECTING_SQUARE, PdfContentByte.LINE_JOIN_MITER));
            canvas.MoveTo(100, 300);
            canvas.CurveTo(180, 320, 180, 320, 200, 400);
            canvas.CurveTo(220, 320, 220, 320, 350, 300);
            canvas.MoveTo(350, 300);
            canvas.CurveTo(220, 280, 220, 280, 210, 250);
            canvas.Stroke();

            canvas.SetLineCap(PdfContentByte.LINE_CAP_ROUND);
            canvas.SetLineJoin(PdfContentByte.LINE_JOIN_ROUND);
            canvas.SetGState(createGState(PdfContentByte.LINE_CAP_ROUND, PdfContentByte.LINE_JOIN_ROUND));
            canvas.MoveTo(100, 100);
            canvas.CurveTo(180, 120, 180, 120, 200, 200);
            canvas.CurveTo(220, 120, 220, 120, 350, 100);
            canvas.MoveTo(350, 100);
            canvas.CurveTo(220, 080, 220, 080, 210, 050);
            canvas.Stroke();
        }
        return memoryStream.ToArray();
    }
}

PdfGState createGState(int lineCap, int lineJoin)
{
    PdfGState pdfGState = new PdfGState();
    pdfGState.Put(new PdfName("LC"), new PdfNumber(lineCap));
    pdfGState.Put(new PdfName("LJ"), new PdfNumber(lineJoin));
    return pdfGState;
}
0 голосов
/ 18 февраля 2019

Соблазнительно попробовать и кругосветное путешествие такого рода в удобном для себя формате.Так что, возможно, преобразование в SVG, затем манипуляции, а затем обратно в PDF.

Однако я бы посоветовал вам держаться подальше от этого типа соблазна, потому что такое кругосветное путешествие неизбежно приведет к искажению и потере.

Вместо этого я бы призвал вас работать непосредственно с сырымОператор PDF поток.Поначалу это выглядит немного устрашающе, но на самом деле все довольно просто, когда вы освоите его.Например (проценты обозначают комментарии),

q  % save state
0 0 10 10 re % define rectangle path
s % stroke
Q % restore state

В спецификации Adobe PDF вы найдете все детали.Он большой, но хорошо написан и понятен.См. Приложение A для получения списка всех операторов и ссылок на соответствующие разделы.

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf

Итак, возникает вопрос, как вы работаете с существующим потоком контента?

Разбор этих вещей нетривиален, поэтому я бы посоветовал вам поработать с инструментом.Например, ABCpdf позволит вам разбить поток на атомы, изменить последовательность, а затем вставить их обратно в исходный документ.Для примера кода см.

https://www.websupergoo.com/helppdfnet/default.htm?page=source%2F7-abcpdf.atoms%2Fopatom%2F1-methods%2Ffind.htm

Это довольно элегантный и мощный механизм с точки зрения анализа и манипуляции.Я уверен, что есть другие инструменты, которые позволяют подобные вещи, но я знаю об ABCpdf.: -)

...