将iTextSharp用于HTML到PDF,页眉,页脚和正文上的图像,"无法访问封闭的流"

时间:2018-01-29 20:00:18

标签: c# html pdf itext

我使用iTextSharp和XMLWorker 5.5.12将HTML转换为PDF,我需要生成页眉,页脚和正文内容。 我能够使用页面事件生成所有文本,当我尝试在正文和页面事件中插入图像时,只要我在parser.Parse(reader);行抛出错误{{ 1}}。

这是我到目前为止的代码:

Cannot access a closed Stream.

这是Page Event的代码:

protected ActionResult ViewPdf(string viewName, object model, ViewDataDictionary viewData = null,
            iTextSharp.text.Rectangle size = null,
            float marginLeft = 10f, float marginRight = 10f,
            float marginTop = 10f, float marginBottom = 10f,
            IEnumerable<PDFFragmentBaseViewModel> fragments = null)
        {
            if (size == null) size = iTextSharp.text.PageSize.LETTER;
            using (iTextSharp.text.Document document = new iTextSharp.text.Document(size, marginLeft, marginRight, marginTop, marginBottom))
            {
                using (var outStream = new MemoryStream())
                {
                    PdfWriter writer = PdfWriter.GetInstance(document, outStream);
                    writer.CloseStream = false;
                    // page event
                    //if (pageEvent != null)
                    //    writer.PageEvent = pageEvent;

                    // css
                    ICSSResolver cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
                    //if (cssPath != null)
                    //    cssResolver.AddCssFile(cssPath, true);

                    //Register Image Procesor
                    var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory();
                    tagProcessorFactory.AddProcessor(new ResourceImageHtmlTagProcessor(), new[] { "resimg" });

                    // html 
                    HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
                    htmlContext.SetTagFactory(tagProcessorFactory);

                    // pipelines
                    PdfWriterPipeline pdfPipeline = new PdfWriterPipeline(document, writer);
                    HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, pdfPipeline);
                    CssResolverPipeline cssPipeline = new CssResolverPipeline(cssResolver, htmlPipeline);

                    // parse
                    XMLWorker worker = new XMLWorker(cssPipeline, true);
                    XMLParser parser = new XMLParser(worker);

                    writer.CloseStream = false;
                    List<PDFFragmentViewModel> _frags = null;
                    if (fragments != null)
                    {
                        _frags = new List<PDFFragmentViewModel>();
                        foreach (var frag in fragments)
                        {
                            _frags.Add(new PDFFragmentViewModel {
                                Content = REC0Utils.RenderRazorViewToString(frag.ViewName, ControllerContext, new object { }, viewData ?? ViewData),
                                Alignment = frag.Alignment,
                                Leading = frag.Leading,
                                LLX = frag.LLX,
                                LLY = frag.LLY,
                                URX = frag.URX,
                                URY = frag.URY
                            });
                        }
                        //string htmlHeader = "<!DOCTYPE html><html><body><table style=\"width: 100%; border: 1px solid black;\"><tr><td>A</td><td>B</td></tr></table></body></html>";
                        //string htmlHeader = REC0Utils.RenderRazorViewToString(headerViewName, ControllerContext, headerModel, viewData ?? ViewData);
                        writer.PageEvent = new HtmlPageEventHelper(_frags);
                    }
                    document.Open();

                    // Render the view xml to a string, then parse that string into an XML dom.
                    string xmltext = REC0Utils.RenderRazorViewToString(viewName, this.ControllerContext, model, viewData == null ? ViewData : viewData);

                    using (var reader = new StringReader(xmltext))
                    {
                        parser.Parse(reader);
                    }

                    // Close and get the resulted binary data.
                    document.Close();

                    // Send the binary data to the browser.
                    return new BinaryContentResult(outStream.ToArray(), "application/pdf");
                }
            }

            // Parse the XML into the iTextSharp document.
            //TextReader reader = new StringReader(xmltext);
            //XMLWorkerHelper.GetInstance().ParseXHtml(writer, document, reader);

            //XMLParser textHandler = new XMLParser(doc);
            //textHandler.Parse(xmldoc);
        }

我一直在调查,我在某处找到了删除public class HtmlPageEventHelper : PdfPageEventHelper { List<PDFFragmentViewModel> _fragments; public HtmlPageEventHelper(List<PDFFragmentViewModel> fragments) { this._fragments = fragments; } public override void OnEndPage(PdfWriter writer, iTextSharp.text.Document document) { base.OnEndPage(writer, document); var _instance = XMLWorkerHelper.GetInstance(); // css ICSSResolver cssResolver = _instance.GetDefaultCssResolver(false); //Register Image Procesor var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory(); tagProcessorFactory.AddProcessor(new ResourceImageHtmlTagProcessor(), new[] { "resimg" }); // html HtmlPipelineContext htmlContext = new HtmlPipelineContext(null); htmlContext.SetTagFactory(tagProcessorFactory); // pipelines PdfWriterPipeline pdfPipeline = new PdfWriterPipeline(document, writer); HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, pdfPipeline); CssResolverPipeline cssPipeline = new CssResolverPipeline(cssResolver, htmlPipeline); // parse XMLWorker worker = new XMLWorker(cssPipeline, true); XMLParser parser = new XMLParser(worker); foreach (var _frag in _fragments) { ColumnText ct = new ColumnText(writer.DirectContent); //using (var reader = new StringReader(_frag.Content)) //{ // parser.Parse(reader); //} _instance.ParseXHtml(new ColumnTextElementHandler(ct), new StringReader(_frag.Content)); //ct.SetSimpleColumn(document.Left, document.Top, document.Right, document.GetTop(-20), 10, Element.ALIGN_MIDDLE); ct.SetSimpleColumn( _frag.LLX.HasValue ? document.GetLeft(_frag.LLX.Value) : document.Left, _frag.LLY.HasValue ? document.GetTop(_frag.LLY.Value) : document.Top, _frag.URX.HasValue ? document.GetRight(_frag.URX.Value) : document.Right, _frag.URY.HasValue ? document.GetBottom(_frag.URY.Value) : document.Bottom, _frag.Leading, _frag.Alignment); ct.Go(); } } } 语句的建议,当我删除它们时,我得到了一个不同的错误:

using

任何人都可以对此有所了解吗?

更新 根据评论的要求,这是Can not find own context 错误的堆栈跟踪。

Can not find own context

这是内部异常的堆栈跟踪:

   at iTextSharp.tool.xml.XMLWorker.EndElement(String tag, String ns)
   at iTextSharp.tool.xml.parser.XMLParser.EndElement()
   at iTextSharp.tool.xml.parser.state.ClosingTagState.Process(Char character)
   at iTextSharp.tool.xml.parser.XMLParser.ParseWithReader(TextReader reader)
   at iTextSharp.tool.xml.parser.XMLParser.Parse(TextReader reader)
   at MvcREC0.WebUI.Controllers.BaseController.ViewPdf(String viewName, Object model, ViewDataDictionary viewData, Rectangle size, Single marginLeft, Single marginRight, Single marginTop, Single marginBottom, IEnumerable`1 fragments) in C:\Pr0g\MVCRec0\WebUI\Controllers\BaseController.cs:line 225
   at MvcREC0.WebUI.Controllers.Crudere`6.<Index>d__8.MoveNext() in C:\Pr0g\MVCRec0\WebUI\Controllers\Cruder.cs:line 688
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Mvc.Async.TaskAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<BeginInvokeAsynchronousActionMethod>b__36(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<>c__DisplayClass2b.<BeginInvokeAction>b__1c()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)
   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
   at System.Web.HttpApplication.CallHandlerExecutionStep.InvokeEndHandler(IAsyncResult ar)
   at System.Web.HttpApplication.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar)

1 个答案:

答案 0 :(得分:2)

确实,XMLWorker架构不支持您尝试执行的操作。 (我实际上对此感到有些惊讶;我想我甚至在此处或之前的一些评论声称它应该有用。)

简而言之

XMLWorker体系结构不允许来自同一线程的重叠使用。但它确实允许在任何线程情况下来自不同线程和非重叠用法的重叠使用。

由于您的页眉和页脚似乎不依赖于实际的页面内容,我建议您切换到两遍过程:在第一个过程中使用Document和a创建文档正文PdfWriter如上所述,仅仅没有事件监听器;在第二次传递中使用PdfReaderPdfStamper将页眉和页脚标记在其上。或者,您可以尝试在本答复结尾处发布的解决方法。

详细

XMLWorkercontext数据槽(在Thread中的Java中)保留当前解析操作的ThreadLocal

当前线程中context的内容在XMLParser.ParseWithReader开始时初始化,当前线程的整个context在其末尾被删除。

方法XMLParser.ParseWithReader最终会被每个XMLParser.Parse重载使用,也会被每个XMLWorkerHelper.parseXHtml重载使用。

只要两次解析尝试使用XMLWorker架构 - 例如解析页面事件监听器中的尝试和填充正文的解析尝试(除非后者明显限于单个页面) - 重叠,因此,它们相互进入并且尝试完成首先删除上下文另一方使用。

解决方法

这个问题有一个解决方法,至少对于像手头那样的情况,如果可以使用反射,并且运行时环境允许:

每当切换到另一次尝试时,可以存储当前的context值并将其替换为适合解析的值。当context被声明为private时,这需要反思。

在手头的情况下将是:

public class HtmlPageEventHelper : PdfPageEventHelper
{
    List<PDFFragmentViewModel> _fragments;
    FieldInfo context;

    public HtmlPageEventHelper(List<PDFFragmentViewModel> fragments)
    {
        this._fragments = fragments;
        context = typeof(XMLWorker).GetField("context", BindingFlags.NonPublic | BindingFlags.Static);
    }

    public override void OnEndPage(PdfWriter writer, iTextSharp.text.Document document)
    {
        [...]
        // parse
        XMLWorker worker = new XMLWorker(cssPipeline, true);
        XMLParser parser = new XMLParser(worker);

        LocalDataStoreSlot contextSlot = (LocalDataStoreSlot) context.GetValue(worker);
        object contextData = Thread.GetData(contextSlot);
        Thread.SetData(contextSlot, null);

        try
        {
            foreach (var _frag in _fragments)
            {
                ColumnText ct = new ColumnText(writer.DirectContent);

                //using (var reader = new StringReader(_frag.Content))
                //{
                //    parser.Parse(reader);
                //}

                _instance.ParseXHtml(new ColumnTextElementHandler(ct), new StringReader(_frag.Content));
                //ct.SetSimpleColumn(document.Left, document.Top, document.Right, document.GetTop(-20), 10, Element.ALIGN_MIDDLE);
                ct.SetSimpleColumn(
                    _frag.LLX.HasValue ? document.GetLeft(_frag.LLX.Value) : document.Left,
                    _frag.LLY.HasValue ? document.GetTop(_frag.LLY.Value) : document.Top,
                    _frag.URX.HasValue ? document.GetRight(_frag.URX.Value) : document.Right,
                    _frag.URY.HasValue ? document.GetBottom(_frag.URY.Value) : document.Bottom,
                    _frag.Leading, _frag.Alignment);
                ct.Go();
            }
        }
        finally
        {
            Thread.SetData(contextSlot, contextData);
        }
    }
}