|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
Hi. I am using EO.Pdf version 4.0.12.2 to generate a pdf via PDF Creator. When I run this from visual studio, it works just fine. The generated pdf ends up being 133 pages (3251371 bytes). However, when I run my app outside of visual studio, this same code gives me an out of memory exception as it is building the pdf. Any thoughts on what might be occurring? Is there any object cleanup that needs to be done?
The code is a little long to post, but I will mention that it is using the BeforeRenderEvent to render a header and footer.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
Actually, let me show you some of the code. There are essentially two methods - one to render the pdf and the other to convert it to a byte array. So, I have some code that calls 'GeneratePDF' and for each of my result strings, it adds an AcmBlock to the render object. Note the call to 'FormatResult' just adds children to the AcmBlock based on the result object.
The code then calls 'GetBinaryPDF' and stores the byte array to a database.
With an earlier version of EO.Pdf, the out of memory occurred during the GeneratePDF logic. With the 4.0.12.2 version, it seems to get farther and happens when generating the byte array.
Is there a way to break this up somehow? Not sure how to do that. Thanks!
public override void GeneratePDF(List<InHouseResult> results) { foreach (InHouseResult result in results) { AcmBlock resultsBlock = new AcmBlock(); resultsBlock.Style.Padding = new AcmPadding(0, 0, 0, 0.4f); resultsBlock.Style.Border.Bottom = new AcmLineInfo(AcmLineStyle.Dotted, Color.LightGray, 0.01f); resultsBlock.Style.FontSize = 11f; string bookmarkText = FormatResult(result.ResultMsg, resultsBlock); render.Render(resultsBlock);
PdfBookmark bookmark = new PdfBookmark(bookmarkText); bookmark.Destination = resultsBlock.CreateDestination(); pdfDoc.Bookmarks.Add(bookmark); }
AcmBlock oigDisclaimerBlock = new AcmBlock(new AcmParagraph(new AcmText("some text."))); oigDisclaimerBlock.Style.FontSize = 11f; oigDisclaimerBlock.Style.ForegroundColor = Color.Gray; oigDisclaimerBlock.Style.Margin = new AcmPadding(.3f); oigDisclaimerBlock.Style.Padding = new AcmPadding(.1f); oigDisclaimerBlock.Style.Border = new AcmBorder(Color.Gray, .01f); render.Render(oigDisclaimerBlock);
AcmBlock disclaimerBlock = new AcmBlock(new AcmParagraph(new AcmText(Disclaimer))); disclaimerBlock.Style.FontSize = 11f; disclaimerBlock.Style.ForegroundColor = Color.Gray; disclaimerBlock.Style.Margin = new AcmPadding(.3f); disclaimerBlock.Style.Padding = new AcmPadding(.1f); disclaimerBlock.Style.Border = new AcmBorder(Color.Gray, .01f); render.Render(disclaimerBlock);
}
public byte[] GetBinaryPDF() { using (MemoryStream ms = new MemoryStream()) { try { pdfDoc.Save(ms); return ms.ToArray(); } catch (Exception ex) { throw ex; } } }
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,195
|
Hi,
There is no silver bullet for out of memory error ---- it all depends on the scenario. We will always try to optimize as much as possible, but we can not guarantee that it will always work for you.
Having that said, 133 pages does not seem to be a lot. Also the code you posted looks perfectly fine and I can not see any reason it would result in such a big file. So I would recommend you to try to reduce the content of your page, to see if you can find a triggering factor ---- once you find that out, try to isolate the problem into a test project and send it to us. Once we have that, we will debug it here and see if we can find anything.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
Thank you for the reply! Sounds like a reasonable plan. I appreciate the help! Sara
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,195
|
You are very welcome. Please feel free to let us know if you need any more help.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
I have pinpointed the issue to be in the BeforeRenderPage event handler. The more I render in this event handler, the more memory is used. Also, I notice when using this event handler, my memory is not being released after the document saves. I am wondering if the AcmRender object created here is not being disposed of and causing a memory leak?
So, when I first setup my document, I subscribe to the BeforeRenderPage event. And I do this once. Code looks like this: pdfDoc = new PdfDocument(); AcmPageLayout pageLayoutOne = new AcmPageLayout(new AcmPadding(0.3f, 1.4f, 0.3f, 0.9f)); AcmPageLayout pageLayoutTwo = new AcmPageLayout(new AcmPadding(0.3f, 0.7f, 0.3f, 0.9f)); render = new AcmRender(pdfDoc, pageLayoutOne, pageLayoutTwo); render.BeforeRenderPage += render_BeforeRenderPage;
When my object that encapsulates all of this disposes, it unsubscribes from the beforeRenderPage event.
The render_BeforeRenderPage code looks like this:
void render_BeforeRenderPage(object sender, AcmPageEventArgs e) { AcmRender render = new AcmRender(page, 0, new AcmPageLayout(new AcmPadding(.3f, 0, .3f, 0)));
AcmTable footer = new AcmTable(1.3f, 4.5f, 2.2f); footer.Style.Border.Top = new AcmLineInfo(AcmLineStyle.Solid, Color.FromArgb(83, 141, 213), 0.01f); footer.Style.Top = 10.3f; footer.Style.ForegroundColor = Color.LightGray; footer.Style.FontSize = 10f; AcmTableRow footerRow = new AcmTableRow(); AcmTableCell cellPageNo = new AcmTableCell(new AcmText(string.Format("Page {0}", page.Index + 1))); AcmTableCell cellTitleFooter = new AcmTableCell(new AcmText(ReportTitle)); cellTitleFooter.Style.HorizontalAlign = AcmHorizontalAlign.Center; AcmText createdDt = new AcmText(string.Format("created {0}", CreateDt)); createdDt.Style.FontSize = 9f; AcmTableCell cellDate = new AcmTableCell(createdDt); cellDate.Style.VerticalAlign = AcmVerticalAlign.Top; cellDate.Style.HorizontalAlign = AcmHorizontalAlign.Right; footer.Rows.Add(footerRow); footerRow.Cells.Add(cellPageNo); footerRow.Cells.Add(cellTitleFooter); footerRow.Cells.Add(cellDate); AcmTableRow copyRightRow = new AcmTableRow(); AcmText crText = new AcmText(string.Format("Copyright © {0} rest of text", copyright)); crText.Style.LineHeight = .18f; AcmTableCell cellCopyRight = new AcmTableCell(crText); cellCopyRight.ColSpan = 3; cellCopyRight.Style.VerticalAlign = AcmVerticalAlign.Bottom; footer.Rows.Add(copyRightRow); copyRightRow.Cells.Add(cellCopyRight);
if (page.Index == 0) { AcmTable header1 = new AcmTable(1.5f, 6.6f); header1.Style.Border.Bottom = new AcmLineInfo(AcmLineStyle.Solid, Color.FromArgb(83, 141, 213), 0.01f); header1.Style.Top = 0.2f; header1.Style.BackgroundColor = Color.FromArgb(240, 245, 249); header1.Style.Height = 1.5f; AcmText title1 = new AcmText(ReportTitle); title1.Style.FontSize = 20f; using (Image imgLogo = Image.FromFile(string.Format("{0}\\PDF\\logo_open.gif", baseUrl))) { AcmTableCell cellImg = new AcmTableCell(new AcmImage(imgLogo)); cellImg.Style.Padding = new AcmPadding(0.2f); AcmTableRow row1 = new AcmTableRow(); AcmTableCell cellTitle = new AcmTableCell(title1); cellTitle.Style.VerticalAlign = AcmVerticalAlign.Middle; header1.Rows.Add(row1); row1.Cells.Add(cellImg); row1.Cells.Add(cellTitle); render.Render(header1, footer); } } else { AcmTable header = new AcmTable(8f); header.Style.Border.Bottom = new AcmLineInfo(AcmLineStyle.Solid, Color.FromArgb(83, 141, 213), 0.01f); header.Style.Top = 0.2f; AcmText title = new AcmText(ReportTitle); title.Style.FontSize = 12f; AcmTableCell cellTitle = new AcmTableCell(title); AcmTableRow row = new AcmTableRow(); header.Rows.Add(row); row.Cells.Add(cellTitle); render.Render(header, footer); }
}
Note that the first thing I did was to remove the image. This did not fix the issue at all. In fact, I removed the header portion and just rendered the footer and still had the same memory issue.
Any thoughts on what might be occurring here and how to fix will be appreciated. It's rather important to our business to provide header and footer information on each page generated in the pdf. Thank you!!!
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
So, I think the memory not releasing was my issue. However, still have the issue of the out of memory error when rendering the header and footer.
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,195
|
Hi,
We do not hold the AcmRender object. The only thing we hold is the contents the render adds to the page (the image and the table you added). The only thing that is suspicious in your code is the image, but as you said the problem still occurs even after you removed the image, so we can not think of anything else that may cause the issue for you. If you want us to investigate further, please send us a test app.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
Thank you! I have created a test app that causes the issue. It may or may not get an out of memory error depending on your amount of available memory, but it will show the memory usage spiking. How to I post this application to you?
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,195
|
Hi,
You an just email it to us in zip format (I believe you have our support email box address, we don't want to post it explicitly in order to avoid spams). Do not send us EO.Pdf.dll as that file is huge so it won't go through. We will run that on the latest version of EO.Pdf and if we see the same memory spike behavior, we should be able to dig into it and find out exactly what's causing it.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
Thank you. Just resent it without EO.pdf.dll
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,195
|
Hi,
We have looked into the code and it appears that the root of the problem is AcmRender will call BeforeRenderPage and AfterRenderPage EVERY TIME you call AcmRender.Render, even if multiple "Render" calls actually generates output to the same page.
For example, if you call Render 10 times on the same page, then BeforeRenderPage and AfterRenderPage will be called 10 times. In your case, this results in 10 page headers and footers to be generated on that page (you will only see one because they are all rendered at exactly the same place). AcmRender calls BeforeRenderPage and AfterRenderPage for every Render call because it does not know whether you are done with that page or not --- so it has to call AfterRenderPage event. In order to keep BeforeRenderPage/AfterRenderPage calls in pairs, it has too call BeforeRenderPage as well. As a result, you got numerous copy of headers and footers rendered on each of your 100+ pages.
There are two ways to avoid this problem:
1. Call Render only once. Instead of passing a single AcmContent to Render, you can have all AcmContent objects ready in an array and pass that array to Render. That way Render will only be called once and your BeforeRenderPage event handler will only be called once for each page; 2. Do not use BeforeRenderPage. Render all contents without page header/footer first, then loop through document.Pages to add page header/footer on each page;
Either way would eliminate the multiple copies of header/footer on each page thus resolves the performance and memory issues for you.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
You guys are awesome! Thanks for looking at this.
I will evaluate the options as see what works out for me. I appreciate the help!
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,195
|
Hi,
You are very welcome. Please feel free to let us know if there is anything else.
Thanks
|
|
Rank: Member Groups: Member
Joined: 6/22/2011 Posts: 22
|
Nope, your suggestion does the trick. I'm doing option one and holding the content in a list and then rendering at the end. Thanks, again!
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,195
|
OK. Let us know if you still have any questions.
Thanks!
|
|