PDFsharp & MigraDoc Foundation
http://forum.pdfsharp.com/

IndexOutOfRangeException when generating a PDF
http://forum.pdfsharp.com/viewtopic.php?f=2&t=2248
Page 1 of 1

Author:  j-ch [ Thu Nov 29, 2012 9:22 am ]
Post subject:  IndexOutOfRangeException when generating a PDF

Hi
We're using PDFsharp (1.31.1789.0) to generate PDF files and it works most of the time but sometimes it creates PDF files with a size of 0kB.

According to our log PDFsharp throws the following exception :
Code:
[System.IndexOutOfRangeException] Index was outside the bounds of the array.
   at PdfSharp.Fonts.OpenType.GlyphDataTable.GetOffset(Int32 glyph)
   at PdfSharp.Fonts.OpenType.GlyphDataTable.GetGlyphSize(Int32 glyph)
   at PdfSharp.Fonts.OpenType.FontData.CreateFontSubSet(Dictionary`2 glyphs, Boolean cidFont)
   at PdfSharp.Pdf.Advanced.PdfCIDFont.PrepareForSave()
   at PdfSharp.Pdf.Advanced.PdfType0Font.PrepareForSave()
   at PdfSharp.Pdf.Advanced.PdfFontTable.PrepareForSave()
   at PdfSharp.Pdf.PdfDocument.PrepareForSave()
   at PdfSharp.Pdf.PdfDocument.DoSave(PdfWriter writer)
   at PdfSharp.Pdf.PdfDocument.Save(Stream stream, Boolean closeStream)
   at PdfSharp.Pdf.PdfDocument.Save(Stream stream)
   at PdfSharp.Pdf.PdfDocument.Save(String path)

This problem is hard to reproduce but do happen from time to time so it's hard to make any progress.

Has someone ever had this problem ? Have you any hints on what could cause this issue ?

Thanks

Author:  Thomas Hoevel [ Thu Nov 29, 2012 9:57 am ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

Hi!
j-ch wrote:
This problem is hard to reproduce
Maybe it's a follow-up error (e.g. no font data returned in an "out of memory" situation) and the exception occurs while accessing data that was not returned. Otherwise, font processing should be reproducible.

Author:  Encarmine [ Fri Oct 04, 2013 12:23 pm ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

I've got the same issue and wrote simple code which throws this exception every launch:
Code:
static void Main(string[] args)
      {
         while (true)
         {
            //RenderPdf(1);
            Enumerable.Range(0, 1000).AsParallel().ForAll(RenderPdf);
         }
      }

      static void RenderPdf(int i)
      {
         var document = new MigraDoc.DocumentObjectModel.Document();
         document.AddSection().AddParagraph("English text Русский текст 0123456789");
         var pdfRenderer = new MigraDoc.Rendering.PdfDocumentRenderer(true, PdfSharp.Pdf.PdfFontEmbedding.Automatic) { Document = document };
         pdfRenderer.RenderDocument();
         pdfRenderer.PdfDocument.Save(Guid.NewGuid() + ".pdf");
      }


Most of the time exception is thrown while saving first few files, sometimes it loops through while few times, but it never takes more than few seconds to crush.
What I found is that code works just fine if there is no Russian text in pdf OR RenderPdf called sequentially, not in parallel.
You can render PDFs in parallel with English letters and symbols, or you can render any number of PDFs with russian text in single thread just fine.

I tried to figure it out by myself but got overwhelmed with library code really fast, so I hope someone here will help me to solve this issue.

Author:  Thomas Hoevel [ Mon Oct 07, 2013 3:05 pm ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

PDFsharp is not (yet) thread safe.

Author:  rikas_nizar [ Tue Oct 29, 2013 4:37 pm ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

Hi Thomas,

I am executing the below code snippet using multiple threads at the same time, unfortunately due to multiple threads, part of the document is in landscape mode and the rest is in Portrait mode, there should be only one orientation per document as per the below code snippet.

My Use case is, I have a module which generates PDF, this module is being called from multiple threads at any given time.

Question is, PDFSharp not yet been thread safe is the cause for this issue?

/// <summary>
/// Render the PDF, this needs to be called only after calling the GeneratePdf() method.
/// </summary>
/// <param name="pdfDocument">PDF Document</param>
/// <param name="formType">Form Type</param>
/// <returns>Rendered PDF</returns>
public PdfDocumentRenderer RenderPdf(Document pdfDocument, EnumFormType formType)
{
PdfDocumentRenderer renderProvider = new PdfDocumentRenderer(true, PdfFontEmbedding.Always);

try
{
if (formType.Equals(EnumFormType.FoodSafety))
{
pdfDocument.DefaultPageSetup.Orientation = Orientation.Landscape;
}
else
{
pdfDocument.DefaultPageSetup.Orientation = Orientation.Portrait;
}

renderProvider.Document = new Document();
renderProvider.Document = pdfDocument;
renderProvider.RenderDocument();
}
catch (Exception ex)
{
//Logger.Error(" PdfHelper RenderPdf method failed with the error: " + ex.ToString() + " with thread ID: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
throw;
}

return renderProvider;
}

Author:  Thomas Hoevel [ Wed Oct 30, 2013 9:20 am ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

rikas_nizar wrote:
Question is, PDFsharp not yet been thread safe is the cause for this issue?
I don't know.
Serializing the requests or running them in AppDomains may be the solution if the problem comes from PDFsharp not being thread safe.

Author:  bradleypeet [ Wed Sep 28, 2016 11:15 pm ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

I believe I have a fix for the problem (as reported by the OP) if anyone is interested.

I've been battling the same thread safety bug on/off for about a week and FINALLY figured it out. As you may know, FontData instances are stored in a dictionary and shared globally (via FontDataStock and FontDescriptorStock). FontData.CreateFontSubset() calls over to GlyphDataTable.CompleteGlyphClosure() which in turn calls AddCompositeGlyphs() and that's where the trouble really begins. You wind up with multiple threads changing the FontData instance's Position property simultaneously while trying to read from its buffer. The whole concept of FontData having a "current position" (which is publicly settable) for reading the internal buffer is just asking for trouble in a multi-threaded environment where FontData instances are widely shared.

My fix for this issue involves significant (but non-breaking) changes to the FontData class and a couple small changes to GlyphDataTable. If anyone is interested I can elaborate and/or share the code. The PDFSharp version I'm working with is 1.32.2608.

Bradley

Author:  Thomas Hoevel [ Thu Sep 29, 2016 8:15 am ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

Hi!
Thanks for your feedback.
If the problem still exists with the latest version of PDFsharp 1.50 then we surely are interested in fixing it.
I don't know how complicated it will be to apply a fix from 1.32 to version 1.50.

If you zip your modified source files and share them here, then anybody interested can have a look.

Author:  bradleypeet [ Thu Sep 29, 2016 7:52 pm ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

In the attached zip file are the FontData and GlyphDataTable classes that I modified. I admit these changes are more than what is absolutely necessary to resolve the threading problem. So if you want the absolute minimum changes required and are willing to sacrifice a small sliver of performance, I suggest modifying the GlyphDataTable class as follows:
Code:
    public void CompleteGlyphClosure(Dictionary<int, object> glyphs)
    {
      int count = glyphs.Count;
      int[] glyphArray = new int[glyphs.Count];
      glyphs.Keys.CopyTo(glyphArray, 0);
      if (!glyphs.ContainsKey(0))
      {
          glyphs.Add(0, null);
      }
      // ensure no other threads can alter the Position property of this FontData instance
      lock (this.fontData)
      {
        for (int idx = 0; idx < count; idx++)
        {
          AddCompositeGlyphs(glyphs, glyphArray[idx]);
        }
      }
    }

Regarding the attached source, the centerpiece of what I did involves abstracting all of the Read and Seek methods and the Position property into an interface IFontDataReader. I created a class named FontData.Reader that implements the interface. FontData has a default instance of the FontData.Reader class that it uses internally plus a public method to obtain a new instance of the FontData.Reader class. For the sake of code compatibility the rest of the project, FontData also implements IFontDataReader (dispatching the calls to its internal default instance of the FontData.Reader class). With this approach, the modifications to GlyphDataTable are as follows:
Code:
    public void CompleteGlyphClosure(Dictionary<int, object> glyphs)
    {
      int count = glyphs.Count;
      int[] glyphArray = new int[glyphs.Count];
      glyphs.Keys.CopyTo(glyphArray, 0);
      if (!glyphs.ContainsKey(0))
      {
          glyphs.Add(0, null);
      }
      IFontDataReader fontDataReader = this.fontData.NewReader();
      for (int idx = 0; idx < count; idx++)
      {
          AddCompositeGlyphs(fontDataReader, glyphs, glyphArray[idx]);
      }
    }

    void AddCompositeGlyphs(IFontDataReader fontDataReader, Dictionary<int, object> glyphs, int glyph)
    {
      //int start = this.fontData.loca.GetOffset(glyph);
      int start = GetOffset(glyph);
      // Has no contour?
      if (start == GetOffset(glyph + 1))
        return;
      fontDataReader.Position = start;
      int numContours = fontDataReader.ReadShort();
      // Is not a composite glyph?
      if (numContours >= 0)
        return;
      fontDataReader.SeekOffset(8);
      for (; ; )
      {
        int flags = fontDataReader.ReadUFWord();
        int cGlyph = fontDataReader.ReadUFWord();
        if (!glyphs.ContainsKey(cGlyph))
          glyphs.Add(cGlyph, null);
        if ((flags & MORE_COMPONENTS) == 0)
          return;
        int offset = (flags & ARG_1_AND_2_ARE_WORDS) == 0 ? 2 : 4;
        if ((flags & WE_HAVE_A_SCALE) != 0)
          offset += 2;
        else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0)
          offset += 4;
        if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0)
          offset += 8;
        fontDataReader.SeekOffset(offset);
      }
    }


Attachments:
File comment: Modifications for thread safety.
PDFSharp-fontdata-glyphdatatable.zip [10.93 KiB]
Downloaded 645 times

Author:  Vojta [ Mon Jul 30, 2018 6:42 am ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

Is this problem solved? In what version?

Author:  0xced [ Wed Jun 19, 2019 7:07 am ]
Post subject:  Re: IndexOutOfRangeException when generating a PDF

This problem is not yet solved as of version 1.50.5147. I have opened a pull request (#91) on GitHub with @bradleypeet proposed solution.

Page 1 of 1 All times are UTC
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/