PDFsharp & MigraDoc Foundation

PDFsharp - A .NET library for processing PDF & MigraDoc Foundation - Creating documents on the fly
It is currently Tue Oct 15, 2019 7:18 am

All times are UTC


Forum rules


Please read this before posting on this forum: Forum Rules



Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Thu Nov 29, 2012 9:22 am 
Offline

Joined: Thu Nov 29, 2012 9:01 am
Posts: 1
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


Top
 Profile  
Reply with quote  
PostPosted: Thu Nov 29, 2012 9:57 am 
Offline
PDFsharp Guru
User avatar

Joined: Mon Oct 16, 2006 8:16 am
Posts: 2980
Location: Cologne, Germany
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.

_________________
Regards
Thomas Hoevel
PDFsharp Team


Top
 Profile  
Reply with quote  
PostPosted: Fri Oct 04, 2013 12:23 pm 
Offline

Joined: Fri Oct 04, 2013 12:05 pm
Posts: 3
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.


Top
 Profile  
Reply with quote  
PostPosted: Mon Oct 07, 2013 3:05 pm 
Offline
PDFsharp Guru
User avatar

Joined: Mon Oct 16, 2006 8:16 am
Posts: 2980
Location: Cologne, Germany
PDFsharp is not (yet) thread safe.

_________________
Regards
Thomas Hoevel
PDFsharp Team


Top
 Profile  
Reply with quote  
PostPosted: Tue Oct 29, 2013 4:37 pm 
Offline

Joined: Tue Oct 29, 2013 4:21 pm
Posts: 1
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;
}


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 30, 2013 9:20 am 
Offline
PDFsharp Guru
User avatar

Joined: Mon Oct 16, 2006 8:16 am
Posts: 2980
Location: Cologne, Germany
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.

_________________
Regards
Thomas Hoevel
PDFsharp Team


Top
 Profile  
Reply with quote  
PostPosted: Wed Sep 28, 2016 11:15 pm 
Offline

Joined: Wed Sep 28, 2016 10:44 pm
Posts: 5
Location: Kona, Hawaii
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


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 29, 2016 8:15 am 
Offline
PDFsharp Guru
User avatar

Joined: Mon Oct 16, 2006 8:16 am
Posts: 2980
Location: Cologne, Germany
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.

_________________
Regards
Thomas Hoevel
PDFsharp Team


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 29, 2016 7:52 pm 
Offline

Joined: Wed Sep 28, 2016 10:44 pm
Posts: 5
Location: Kona, Hawaii
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 176 times
Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 30, 2018 6:42 am 
Offline

Joined: Mon Jul 30, 2018 6:41 am
Posts: 1
Is this problem solved? In what version?


Top
 Profile  
Reply with quote  
PostPosted: Wed Jun 19, 2019 7:07 am 
Offline

Joined: Fri Jun 07, 2019 10:27 am
Posts: 1
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.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 329 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Privacy Policy, Data Protection Declaration, Impressum
Powered by phpBB® Forum Software © phpBB Group