iTextSharp получает ссылку на графическую разметку

Я пару часов прорабатывал, как это сделать, но ударил кирпичную стену. У меня есть файл PDF, и один из объектов - стрелка Север. Это простая линейная графика (я считаю, что они называются Graphic Markups в Acrobat), которая будет обозначать, какой путь «вверх». Я хочу прочитать эту графику и определить ее поворот. Первый шаг, который я сделал, - проверить, могу ли я перечислить содержимое PDF с помощью этого кода:

Imports it = iTextSharp.text
Imports ip = iTextSharp.text.pdf

Dim pdfRdr As New ip.PdfReader("C:city.pdf")
Dim page As ip.PdfDictionary = pdfRdr.GetPageN(1)
Dim objectReference As ip.PdfIndirectReference = CType(page.Get(ip.PdfName.CONTENTS), ip.PdfIndirectReference)
Dim stream As ip.PRStream = CType(ip.PdfReader.GetPdfObject(objectReference), ip.PRStream)
Dim streamBytes() As Byte = ip.PdfReader.GetStreamBytes(stream)
Dim tokenizer As New ip.PRTokeniser(New ip.RandomAccessFileOrArray(streamBytes))

'Loop through each PDf token
While tokenizer.NextToken
     Debug.Print("token of type={0} and value={1}", tokenizer.TokenType.ToString, tokenizer.StringValue)
End While

Я возвращаю некоторые данные, но боюсь, я просто не понимаю, как их расшифровать.

token of type=OTHER and value=q
token of type=NUMBER and value=0.86275
token of type=NUMBER and value=0
token of type=NUMBER and value=0
token of type=NUMBER and value=0.86275
token of type=NUMBER and value=54
token of type=NUMBER and value=30
token of type=OTHER and value=cm
token of type=NAME and value=Fm0
token of type=OTHER and value=Do
token of type=OTHER and value=Q
token of type=OTHER and value=q
token of type=NUMBER and value=1
token of type=NUMBER and value=0
token of type=NUMBER and value=0
token of type=NUMBER and value=1
token of type=NUMBER and value=54
token of type=NUMBER and value=18
token of type=OTHER and value=cm
token of type=NAME and value=Fm1
token of type=OTHER and value=Do
token of type=OTHER and value=Q

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

тестовый файл находится здесь https://drive.google.com/file/d/1dYFkvLMvznsx6sN-1GsNZVIBtDpgzwCU/view?usp=sharing

Я иду по правильному пути или есть другой способ получить ссылку на графическую разметку?

vb.net,pdf,graphics,itext,markup,

0

Ответов: 2


1

Я считаю, что они называются графическими разметками в Acrobat

Может быть, вы можете отправить пример PDF? Это важно.

Вы можете получить поворот строки аннотации довольно тривиально: зациклируйте аннотации на странице, посмотрите, является ли аннотация аннотацией линии, получает ее координаты и вычисляет вращение относительно горизонтальной оси.

Использование iText выглядит следующим образом:

    PdfReader reader = new PdfReader(INPUT_FILE);
    PdfDocument pdfDocument = new PdfDocument(reader);
    PdfDictionary firstPage = pdfDocument.getFirstPage().getPdfObject();
    PdfArray annots = firstPage.getAsArray(PdfName.Annots);

    if (annots != null) {
        for (int x = 0; x < annots.size(); x++) {
            if (annots.get(x).isDictionary() && annots.getAsDictionary(x).getAsName(PdfName.Subtype) == PdfName.Line) {
                PdfArray coordinates = annots.getAsDictionary(x).getAsArray(PdfName.L);

                float x2 = coordinates.getAsNumber(0).floatValue();
                float y2 = coordinates.getAsNumber(1).floatValue();
                float x1 = coordinates.getAsNumber(2).floatValue();
                float y1 = coordinates.getAsNumber(3).floatValue();

                double deltaY = y2 - y1;
                double deltaX = x2 - x1;

                double angle = Math.atan2(deltaY , deltaX) * 180 / Math.PI;
                System.out.println(angle);

            }
        }
    }

Если вы используете iText 5 annots, нужно получить немного по-другому, но все остальное должно быть более или менее одинаковым. Вот как это сделать в 5:

 PdfReader reader = new PdfReader(src);
 PdfDictionary firstPage = reader.getPageN(1);
 PdfArray annots = firstPage.getAsArray(PdfName.ANNOTS);

Вот мой PDF:

введите описание изображения здесь

Обратите внимание, что строки являются строковыми аннотациями: введите описание изображения здесь

И это мой вывод:

131.9086081625848
90.0

Это выглядит правильно. Первая строка составляет 132 градуса относительно горизонтальной оси, а вторая - 90 градусов относительно горизонтальной оси.

Проблема становится намного сложнее, если это не аннотации строк, потому что вам нужно отслеживать операции линии в графическом потоке, как вы делали раньше. Если это так:

  1. Вы должны как-то знать, что линия, нарисованная, - это линия, которую вы хотите.
  2. Вы должны знать весь контекст состояния графики (преобразования и где вы сейчас находитесь).

0

В отличие от первоначального впечатления, северная стрелка не находится в аннотации PDF, а вместо этого является частью обычного содержимого страницы. (@Jon создал свой ответ при этом первоначальном впечатлении).

В PDF-файле, совместно используемом OP, стрелка является частью непосредственного содержимого страницы. С другой стороны, в скриншоте Adobe Acrobat, совместно используемом OP, стрелка находится в форме XObject (которая, в свою очередь, будет ссылаться на непосредственное содержимое страницы).

Следующий подход должен получить инструкции по векторной графике для обоих случаев.

Вы можете получить инструкции по векторной графике, нарисовав стрелку, используя структуру анализатора iText.

Например, используя текущий iText 5.5.x, вам необходимо реализовать IExtRenderListenerи использовать эту реализацию при PdfReaderContentParserвыполнении, например:

Public Class VectorParser
    Implements IExtRenderListener

    Public Sub ModifyPath(renderInfo As PathConstructionRenderInfo) Implements IExtRenderListener.ModifyPath
        pathInfos.Add(renderInfo)
    End Sub

    Public Function RenderPath(renderInfo As PathPaintingRenderInfo) As parser.Path Implements IExtRenderListener.RenderPath
        Dim GraphicsState As GraphicsState = getGraphicsState(renderInfo)
        Dim ctm As Matrix = GraphicsState.GetCtm()

        If (Not (renderInfo.Operation And PathPaintingRenderInfo.FILL) = 0) Then
            Console.Write("FILL ({0}) ", ToString(GraphicsState.FillColor))
            If (Not (renderInfo.Operation And PathPaintingRenderInfo.STROKE) = 0) Then
                Console.Write("and ")
            End If
        End If

        If (Not (renderInfo.Operation And PathPaintingRenderInfo.STROKE) = 0) Then
            Console.Write("STROKE ({0}) ", ToString(GraphicsState.StrokeColor))
        End If

        Console.Write("the path ")

        For Each pathConstructionRenderInfo In pathInfos
            Select Case pathConstructionRenderInfo.Operation
                Case PathConstructionRenderInfo.MOVETO
                    Console.Write("move to {0} ", ToString(transform(ctm, pathConstructionRenderInfo.SegmentData)))
                Case PathConstructionRenderInfo.CLOSE
                    Console.Write("close {0} ", ToString(transform(ctm, pathConstructionRenderInfo.SegmentData)))
                Case PathConstructionRenderInfo.CURVE_123
                    Console.Write("curve123 {0} ", ToString(transform(ctm, pathConstructionRenderInfo.SegmentData)))
                Case PathConstructionRenderInfo.CURVE_13
                    Console.Write("curve13 {0} ", ToString(transform(ctm, pathConstructionRenderInfo.SegmentData)))
                Case PathConstructionRenderInfo.CURVE_23
                    Console.Write("curve23 {0} ", ToString(transform(ctm, pathConstructionRenderInfo.SegmentData)))
                Case PathConstructionRenderInfo.LINETO
                    Console.Write("line to {0} ", ToString(transform(ctm, pathConstructionRenderInfo.SegmentData)))
                Case PathConstructionRenderInfo.RECT
                    Console.Write("rectangle {0} ", ToString(transform(ctm, expandRectangleCoordinates(pathConstructionRenderInfo.SegmentData))))
            End Select
        Next

        Console.WriteLine()

        pathInfos.Clear()
        Return Nothing
    End Function

    Public Sub ClipPath(rule As Integer) Implements IExtRenderListener.ClipPath
    End Sub

    Public Sub BeginTextBlock() Implements IRenderListener.BeginTextBlock
    End Sub

    Public Sub RenderText(renderInfo As TextRenderInfo) Implements IRenderListener.RenderText
    End Sub

    Public Sub EndTextBlock() Implements IRenderListener.EndTextBlock
    End Sub

    Public Sub RenderImage(renderInfo As ImageRenderInfo) Implements IRenderListener.RenderImage
    End Sub

    Function expandRectangleCoordinates(rectangle As IList(Of Single)) As List(Of Single)
        If rectangle.Count < 4 Then
            Return New List(Of Single)
        End If

        Return New List(Of Single)() From
        {
            rectangle(0), rectangle(1),
            rectangle(0) + rectangle(2), rectangle(1),
            rectangle(0) + rectangle(2), rectangle(1) + rectangle(3),
            rectangle(0), rectangle(1) + rectangle(3)
        }
    End Function

    Function transform(ctm As Matrix, coordinates As IList(Of Single)) As List(Of Single)
        Dim result As List(Of Single) = New List(Of Single)
        If Not coordinates Is Nothing Then
            For i = 0 To coordinates.Count - 1 Step 2
                Dim vector As Vector = New Vector(coordinates(i), coordinates(i + 1), 1)
                vector = vector.Cross(ctm)
                result.Add(vector(Vector.I1))
                result.Add(vector(Vector.I2))
            Next
        End If
        Return result
    End Function

    Public Function ToString(coordinates As IList(Of Single)) As String
        Dim result As StringBuilder = New StringBuilder()
        result.Append("[ ")
        For i = 0 To coordinates.Count - 1
            result.Append(coordinates(i))
            result.Append(" ")
        Next
        result.Append("]")
        Return result.ToString()
    End Function

    Public Function ToString(baseColor As BaseColor) As String
        If (baseColor Is Nothing) Then
            Return "DEFAULT"
        End If
        Return String.Format("{0},{1},{2}", baseColor.R, baseColor.G, baseColor.B)
    End Function

    Function getGraphicsState(renderInfo As PathPaintingRenderInfo) As GraphicsState
        Dim gsField As Reflection.FieldInfo = GetType(PathPaintingRenderInfo).GetField("gs", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
        Return CType(gsField.GetValue(renderInfo), GraphicsState)
    End Function

    Dim pathInfos As List(Of PathConstructionRenderInfo) = New List(Of PathConstructionRenderInfo)
End Class

который использовался так

Using pdfReader As New PdfReader("test.pdf")
    Dim extRenderListener As IExtRenderListener = New VectorParser

    For page = 1 To pdfReader.NumberOfPages
        Console.Write(vbCrLf + "Page {0}" + vbCrLf + "====" + vbCrLf, page)
        Dim parser As PdfReaderContentParser = New PdfReaderContentParser(pdfReader)
        parser.ProcessContent(page, extRenderListener)
    Next
End Using

для возврата ваших общих документов

Page 1
====
STROKE (0,0,255) the path move to [ 277,359 434,2797 ] line to [ 311,5242 434,2797 ] 
STROKE (0,0,255) the path move to [ 277,3591 434,2797 ] line to [ 315,0443 424,1336 ] 
STROKE (0,0,255) the path move to [ 304,2772 425,376 ] line to [ 304,4842 426,6183 ] 
STROKE (0,0,255) the path move to [ 304,6913 426,2042 ] line to [ 310,075 425,376 ] 
STROKE (0,0,255) the path move to [ 304,6913 426,8254 ] line to [ 307,5902 425,9972 ] 
FILL (0,0,255) the path move to [ 303,656 425,3759 ] line to [ 303,656 425,3759 ] line to [ 306,1407 425,1689 ] line to [ 306,1407 425,1689 ] 
STROKE (0,0,255) the path move to [ 303,656 425,376 ] line to [ 303,656 425,376 ] line to [ 306,1407 425,1689 ] line to [ 306,1407 425,1689 ] close [ ] 
FILL (0,0,255) the path move to [ 306,969 424,9618 ] line to [ 306,969 424,9618 ] line to [ 309,4538 424,7548 ] line to [ 309,4538 424,7548 ] 
STROKE (0,0,255) the path move to [ 306,969 424,9619 ] line to [ 306,969 424,9619 ] line to [ 309,4538 424,7548 ] line to [ 309,4538 424,7548 ] close [ ] 
FILL (0,0,255) the path move to [ 309,8679 424,9618 ] line to [ 309,8679 424,9618 ] line to [ 312,3527 424,5477 ] line to [ 312,3527 424,5477 ] 
STROKE (0,0,255) the path move to [ 309,868 424,9619 ] line to [ 309,868 424,9619 ] line to [ 312,3527 424,5477 ] line to [ 312,3527 424,5477 ] close [ ] 
STROKE (0,0,255) the path move to [ 313,1809 424,3407 ] line to [ 314,8374 424,1336 ] 
STROKE (0,0,255) the path move to [ 304,2772 425,7901 ] line to [ 309,8679 424,9619 ] line to [ 312,9738 424,7548 ] 
STROKE (0,0,255) the path move to [ 304,2772 425,9972 ] line to [ 309,8679 425,1689 ] line to [ 311,5244 424,9619 ] 
STROKE (0,0,255) the path move to [ 304,6914 426,8254 ] line to [ 315,0445 424,1336 ] 
STROKE (0,0,255) the path move to [ 311,7315 435,7292 ] line to [ 311,7315 432,8303 ] 
STROKE (0,0,255) the path move to [ 321,2564 434,2797 ] line to [ 315,4587 434,2797 ] 
STROKE (0,0,255) the path move to [ 315,4586 434,2797 ] line to [ 311,7315 434,2797 ] 
STROKE (0,0,255) the path move to [ 311,7315 434,6938 ] line to [ 317,7363 434,0727 ] line to [ 311,7315 433,6585 ] 
STROKE (0,0,255) the path move to [ 311,7315 434,4868 ] line to [ 314,8374 434,2797 ] line to [ 311,7315 434,2797 ] 
STROKE (0,0,255) the path move to [ 310,6963 436,1433 ] line to [ 317,3222 434,9009 ] line to [ 322,2917 434,2797 ] line to [ 317,3222 433,6585 ] line to [ 310,6963 432,6232 ] 
STROKE (0,0,255) the path move to [ 311,7315 435,5221 ] line to [ 317,3222 434,6938 ] line to [ 321,0493 434,2797 ] line to [ 317,3222 433,8656 ] line to [ 311,7315 433,0374 ] 
STROKE (0,0,255) the path move to [ 311,7315 435,108 ] line to [ 317,3222 434,4868 ] line to [ 319,3928 434,2797 ] line to [ 317,3222 434,2797 ] line to [ 311,7315 433,4515 ]

Это похоже на множество инструкций для простой стрелки, но при масштабировании в PDF вы видите, что стрела действительно построена из множества небольших линий:

Скриншот

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


Вышеупомянутый код по существу представляет собой порт анонимной ExtRenderListenerреализации для Java и iText 5.5.x в этом ответе .

Это также легко реализовать с помощью iText 7.


В стороне: К сожалению, инструкции по рисованию стрелки не обозначены специально; если на той же странице есть другая векторная графика, вам нужно будет фильтровать результаты, полученные парсером, по некоторым конкретным критериям, например, цвет (в данном случае чистый синий RGB) или приблизительный диапазон координат (например, внутри учитывая й и у координат только диапазон).

vb.net, PDF, графика, IText, разметка,
Похожие вопросы