PDFs created with a large number of text fragments per page take a very long time for Adobe Acrobat to print

We have an application that creates New PDF documents using Aspose.Pdf.Document version 17.8 for .NET

The application adds between 50 and 750 text fragments to each page of the PDF using an Aspose.Pdf.TextBuilder

The application is also using the xImage functionality to create a watermark annotation to every page.

When an end user attempts to print the Output PDF, Adobe Reader displays a “Flattening” progress bar for each page. This progress bar is on screen for approximately 30 seconds per page of the PDF.

It may not be related, but the output of a PDF validation tool reports that “Maximum depth of graphics state nesting by q and Q operators exceeded.” We investigated the output file and found that each page begins with a number of consecutive q operators equal to the number of text fragments + 15. (65 opening “q” operators for 50 text fragments)

Code Snippet
`
Public Sub SavePDF(szOutputFile As String)

    Dim sourcePage As PDF_Page
    Dim sourceText As PDF_Text



    Dim pdf As New Aspose.Pdf.Document()
    Dim curPage As Aspose.Pdf.Page

    Dim textBuilder As Aspose.Pdf.Text.TextBuilder

    pdf.Info.Author = "Rich Clark"
    pdf.Info.Subject = "EBE Rendtion Output PDF"

    For Each sourcePage In lstPages

        '''''''''PAGE LEVEL STUFF'''''''''''
        curPage = pdf.Pages.Add()
        curPage.SetPageSize(612, 792)
        '595 x 842 is 8.26 x 11.69

        curPage.PageInfo.Margin.Left = 0
        curPage.PageInfo.Margin.Right = 0
        curPage.PageInfo.Margin.Top = 0
        curPage.PageInfo.Margin.Bottom = 0

        textBuilder = New Aspose.Pdf.Text.TextBuilder(curPage)

        For Each sourceText In sourcePage.TextList

            With sourceText

                If .HasLink And .ModifyLinkSourceFont Then
                    WriteLinkTextToPDF(textBuilder, .Left, .Top, .FontName, .FontSize, .Bold, .Italic, .Underline, .Value)
                Else
                    WriteTextToPDF(textBuilder, .Left, .Top, .FontName, .FontSize, .Bold, .Italic, .Underline, .Value)
                End If



                If .HasLink Then

                    Dim link As Aspose.Pdf.Annotations.LinkAnnotation
                    Dim dLeft As Long
                    Dim dTop As Long
                    Dim dWidth As Long
                    Dim dHeight As Long

                    Dim targetColor As System.Drawing.Color


                    dLeft = .Left * (72 / m_X_DPI)
                    dWidth = .LinkWidth * (72 / m_X_DPI) ''365
                    dTop = (3300 - .Top) * (72 / m_Y_DPI) - (.FontSize * mFontScaleFactor) - mFontHeightOffset
                    dHeight = (.FontSize * mFontScaleFactor) * 1.05

                    link = New Aspose.Pdf.Annotations.LinkAnnotation(curPage, New Aspose.Pdf.Rectangle(dLeft, dTop, dLeft + dWidth, dTop + dHeight))

                    targetColor = System.Drawing.Color.FromName(.LinkBorderColor)

                    If targetColor = Drawing.Color.Transparent Then
                        link.Color = Aspose.Pdf.Color.FromArgb(0, 0, 0, 0)
                        link.Border = New Aspose.Pdf.Annotations.Border(link)
                        link.Border.Width = 0
                    Else
                        link.Color = Aspose.Pdf.Color.FromRgb(targetColor)
                    End If


                    'link.Border.Style = Aspose.Pdf.Annotations.BorderStyle.Underline
                    link.Action = New Aspose.Pdf.Annotations.GoToURIAction(.LinkTarget)

                    curPage.Annotations.Add(link)

                End If


            End With
        Next





    Next


    ''''''''''APPLY OVERLAY TO ALL PAGES
    'Dim imageStream As Object
    Using imageStream = System.IO.File.Open(mOverlayFilePath, IO.FileMode.Open)


        Dim image As Aspose.Pdf.XImage

        Dim imageRectangle As New Aspose.Pdf.Rectangle(0, 0, 612, 792)



        Dim form As Aspose.Pdf.XForm

        Dim szOverlay As String


        'form = Aspose.Pdf.XForm.CreateNewForm(pdf.Pages(1), pdf)
        'form.BBox = curPage.Rect

        Dim annotation As New Aspose.Pdf.Annotations.WatermarkAnnotation(curPage, curPage.Rect)

        form = annotation.Appearance("N")
        form.BBox = curPage.Rect


        If image Is Nothing Then

            'szOverlay = form.Resources.Images.Add(imageStream)
            form.Resources.Images.Add(imageStream)
            szOverlay = form.Resources.Images(1).Name
            image = form.Resources.Images(szOverlay)

        Else
            szOverlay = form.Resources.Images(1).Name

        End If

        form.Contents.Add(New Aspose.Pdf.Operator.GSave())
        form.Contents.Add(New Aspose.Pdf.Operator.ConcatenateMatrix(New Aspose.Pdf.Matrix(imageRectangle.Width, 0, 0, imageRectangle.Height, 0, 0)))
        form.Contents.Add(New Aspose.Pdf.Operator.Do(szOverlay))
        form.Contents.Add(New Aspose.Pdf.Operator.GRestore())


        For Each curPage In pdf.Pages

            


            curPage.Annotations.Add(annotation, False)
            imageRectangle = New Aspose.Pdf.Rectangle(0, 0, imageRectangle.Width, imageRectangle.Height)



        Next



    End Using

    'SAVE IT

    pdf.Save(szOutputFile)


End Sub

`

`

Private Sub WriteTextToPDF(textBuilder As Aspose.Pdf.Text.TextBuilder, sourceLeft As Integer, sourceTop As Integer, FontName As String, fontSize As Single, bBold As Boolean, bUnderline As Boolean, bItalic As Boolean, szText As String)




    Dim textFragment As New Aspose.Pdf.Text.TextFragment




    Dim xPointCord As Single
    Dim yPointCord As Single
    Dim AdjustedFontSize As Single
    Dim FontFactor As Single


    FontFactor = mFontHeightOffset
    AdjustedFontSize = fontSize * mFontScaleFactor

    xPointCord = sourceLeft * (72 / m_X_DPI)
    yPointCord = (3300 - sourceTop) * (72 / m_Y_DPI) - AdjustedFontSize - FontFactor


    textFragment.Text = szText
    textFragment.TextState.FontSize = AdjustedFontSize
    textFragment.TextState.Font = Aspose.Pdf.Text.FontRepository.FindFont(FontName)
    textFragment.TextState.BackgroundColor = Aspose.Pdf.Color.FromRgb(System.Drawing.Color.White)
    textFragment.TextState.ForegroundColor = Aspose.Pdf.Color.FromRgb(System.Drawing.Color.Black)


    If bItalic Then
        textFragment.TextState.FontStyle = Aspose.Pdf.Text.FontStyles.Italic
    End If

    If bUnderline Then
        textFragment.TextState.Underline = True
    End If

    If bBold Then
        textFragment.TextState.FontStyle = Aspose.Pdf.Text.FontStyles.Bold
    End If

    'textFragment.ZIndex = 99

    textFragment.Position = New Aspose.Pdf.Text.Position(xPointCord, yPointCord)


    textBuilder.AppendText(textFragment)


End Sub

`

sample PDF produced
001.pdf (1.0 MB)

@stevens-1,

Your code example is not in fully compilable form and including unknown objects. You are using an old version 17.8 of Aspose.Pdf for .NET API, please download and try the latest version 17.12 of Aspose.Pdf for .NET API, and then let us know how that goes into your environment. If the problem is reproducible with the latest version 17.12, then kindly create a small project application and make sure that it reproduces this problem in your environment, and then share a Zip of this project. We will investigate and share our findings with you.

after updating to version 17.12 the problem persists

the startup project is PDF_TestApp

the Test button at the top will do a test with 182 text fragments (output file test.pdf in startup directory)

clicking “Load XML” then “Write XML” will perform a real world operation (output file (testXML.pdf in startup directory)

the application is looking for “overlay.png” and “TruncatedSample.xml” in the startup directory

AsposePdfSupportSample_minusDLL.zip (1.7 MB)

third attempt to upload project

deleted file Aspose.Pdf.dll from project working directories to make it smaller
expected file is “Aspose.Pdf.dll” version 17.12 for .net 4.0

@stevens-1,

The project application has a reference of an assembly EBE_RenditionPDF.dll, which is showing a compile time error as follows:

Severity Code Description Project File Line Suppression State
Error Cannot register assembly “C:\Pdf\test527\AsposeSamplePDF\EBE_RenditionPDF\bin\Debug\EBE_RenditionPDF.dll” - access denied. Please make sure you’re running the application as administrator. Access to the registry key ‘HKEY_CLASSES_ROOT\EBE_RenditionPDF.PDF_Page’ is denied. EBE_RenditionPDF

Please review the project and remove dependency of this assembly as well as also simply the code, and then again send a Zip of this project. Your response is awaited.

PDF_TestApp.zip (1.6 MB)

I combined the DLL and UI projects uploaded above into a single project.
i again had to delete the Aspose.Pdf.dll file from the project for it to upload successfully.

let me know if you have any additional difficulty opening, compiling, or running the sample application

Thanks,

@stevens-1,

We managed to replicate the problem flattening progress bar for each page. It has been logged under the ticket ID PDFNET-43909 in our bug tracking system. We have linked your post to this ticket and will keep you informed regarding any available updates.

When we click on Write XML PDF button, it shows an error as below. Please review and simplify the code.

Error:
ContextSwitchDeadlock occurred
Message: Managed Debugging Assistant ‘ContextSwitchDeadlock’ has detected a problem in ‘C:\Pdf\test528\PDF_TestApp\bin\Debug\PDF_TestApp.vshost.exe’.
Additional information: The CLR has been unable to transition from COM context 0xc07730 to COM context 0xc077e8 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

That exception occurs while debugging long running operations on the UI thread and can be ignored.

I have added an Application.DoEvents() call inside of the for loop so that the exception will not be thrown.
I have also added protection to the UI so that no additional buttons can be pressed while the long operation is executing.

PDF_TestApp (2).zip (2.4 MB)

@stevens-1,

We managed to replicate the problem flattening progress bar for each page. It has been logged under the ticket ID PDFNET-43912 in our bug tracking system. We have linked your post to this ticket and will keep you informed regarding any available updates.

@stevens-1

Thanks for your patience.

We would like to inform you that we have escalated earlier logged issues to next level and are trying to get them investigated soon. We are in communication with respective team and as soon as we receive some significant progress update regarding investigation, we will inform you. We highly appreciate your patience and comprehension in this regard.

We are sorry for the inconvenience.

@stevens-1

Thanks for your patience.

Our product team has investigated the earlier logged issue PDFNET-43909 regarding slow printing of the PDF document. As per their initial finding about the issue, the issue seems to be related with Adobe Acrobat. However, if you will add the image to the page - instead of using WatermarkAnnotation, document will be printed faster. Please try to add image directly into page contents by following code snippet and in case you still face any issue, please let us know.

foreach (Aspose.Pdf.Page curPageWithinLoop in pdf.Pages)
                {
                    curPage = curPageWithinLoop;
                    Artifact watermark = new WatermarkArtifact();
                    watermark.SetImage(imageStream);
                    watermark.IsBackground = true;
                    watermark.ArtifactHorizontalAlignment = HorizontalAlignment.Center;
                    watermark.ArtifactVerticalAlignment = VerticalAlignment.Center;
                    watermark.RightMargin = 0;
                    watermark.TopMargin = 0;
                    watermark.LeftMargin = 0;
                    watermark.BottomMargin = 0;


//add image to the page resources and add operators to page contents
                    szOverlay = curPageWithinLoop.Resources.Images.Add(imageStream);
                    curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.GSave());
                    curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.ConcatenateMatrix(new Aspose.Pdf.Matrix(imageRectangle.Width, 0, 0, imageRectangle.Height, 0, 0)));
                    curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.Do(szOverlay));
                    curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.GRestore());
}

I will test this solution tomorrow.

in the mean time i have a question.

will using the above code snippet instead of the xForm object cause the background png to be embedded into every page of the PDF instead of only once for the entire PDF?

The reason we chose to use xForm watermarks was to reduce the file size of the final PDF so it could be emailed. Some of the PDF files we are generating are in excess of 300 pages so the final file would measure in the hundreds of MB if the background image was individually attached to every page (instead of the < 5 MB using xForm)

@stevens-1

Thanks for getting back to us.

While using suggested approach, yes, image will be embedded into each page of the PDF document - increasing the file size of resultant document. However, you may use OptimizeResources() method, in order to remove duplicate streams from the output PDF and optimize its size. Please use following code snippet on the PDF, obtained after adding image to each page:

pdf = new Document(dataDir + "output_obtained.pdf");
pdf.OptimizeResources(new Document.OptimizationOptions()
{
 LinkDuplcateStreams = true,
 RemoveUnusedObjects = true,
 RemoveUnusedStreams = true
});
pdf.Save(dataDir + "o_optimized.pdf");

Please use above suggested approach along with optimizing resources method and in case you face any issue, please let us know.

The above changes have reduced the “flattening” time in adobe to approximately 4 seconds per page.

i noticed that the first section of the provided code snippet that references:

Artifact watermark = new WatermarkArtifact();

never actually adds the watermark object to a page. When i comment out that section of the code, the resulting pdf and print timing is the same as leaving it in.

Have i missed (or misunderstood) how the watermark object is used?

In another note, I found the source of the nested q Q problem mentioned in the original post. i was adding many text fragments to each page. if instead add just one text fragment to the page with many text segments, the nested operator overflow problem is solved.

@stevens-1

Thanks for writing back.

It is good to know that your issue has been resolved by adopting another approach for adding text.

We really apologize for the confusion. In the above suggested code snippet, following line of code was missing which is actually used to add watermark:

curPage.Artifacts.Add(watermark);

The complete code to add watermark will be as follows:

foreach (Aspose.Pdf.Page curPageWithinLoop in pdf.Pages)
{
curPage = curPageWithinLoop;
Artifact watermark = new WatermarkArtifact();
watermark.SetImage(imageStream);
watermark.IsBackground = true;
watermark.ArtifactHorizontalAlignment = HorizontalAlignment.Center;
watermark.ArtifactVerticalAlignment = VerticalAlignment.Center;
watermark.RightMargin = 0;
watermark.TopMargin = 0;
watermark.LeftMargin = 0;
watermark.BottomMargin = 0;
curPage.Artifacts.Add(watermark);
}

Furthermore, as shared earlier, if you will add the image to the page directly, document will be printed faster. In order to add image directly to the page, following code snippet can be used:

foreach (Aspose.Pdf.Page curPageWithinLoop in pdf.Pages)
{
 //add image to the page resources and add operators to page contents
 var szOverlay = curPageWithinLoop.Resources.Images.Add(imageStream);
 curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.GSave());
 curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.ConcatenateMatrix(new Aspose.Pdf.Matrix(img.Width, 0, 0, img.Height, 0, 0)));
curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.Do(szOverlay));
curPageWithinLoop.Contents.Add(new Aspose.Pdf.Operator.GRestore());
}

You may please try both code snippets to add watermark inside your PDF and check the printing time in both cases. In case you still notice any delay, please let us know.