即使 FormFlattening 属性为 false,Itextsharp 表单也会被展平

Mar*_*skI 0 c# pdf itext

我有以下方法,该方法应该在给定坐标处的 pdf 文件上标记图像,并在图层仍然分离(即未展平)的情况下返回它,我设置了 FormFlattening 属性,但它不起作用。

经过一番实验,我发现当我调用 getPdfLayers 方法时,文件不会被展平,为什么会这样呢?

public static byte[] StampLayer(System.Drawing.Image image, int x, int y, string layername)
{
    var iImage = iTextSharp.text.Image.GetInstance(image, ImageFormat.Tiff);
    var reader = new PdfReader(_pdfFile);

    using (var ms = new MemoryStream())
    {
        using (var stamper = new PdfStamper(reader, ms))
        {
            //Don't delete otherwise the stamper flattens the layers
            var layers = stamper.GetPdfLayers();

            stamper.FormFlattening = false;

            var logoLayer = new PdfLayer(layername, stamper.Writer);
            PdfContentByte cb = stamper.GetUnderContent(1);
            cb.BeginLayer(logoLayer);

            //300dpi
            iImage.ScalePercent(24f);
            iImage.SetAbsolutePosition(x, y);
            cb.AddImage(iImage);

            cb.EndLayer();
            stamper.Close();

            return (ms.GetBuffer());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

iTextSharp版本是:5.5.6

我尝试过 png 和 jpg 图像,结果是一样的。

我正在使用这个文件来测试。

mkl*_*mkl 5

您似乎在 iText(Sharp) 中发现了一个错误。但您的代码中还存在一个额外的弱点。

错误

PdfStamperImp( 的类stamper.Writer) 派生自PdfWriterPdfWriter保存它所写入的 PDF 图层的集合:

protected Dictionary<IPdfOCG, object> documentOCG = new Dictionary<IPdfOCG,object>();
Run Code Online (Sandbox Code Playgroud)

PdfStamperImp仅使用文档的现有层延迟初始化该成员,例如在其方法中GetPdfLayers

virtual public Dictionary<string,PdfLayer> GetPdfLayers()
{
    if (documentOCG.Count == 0)
    {
        ReadOCProperties();
    }
    ...
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它使用字典的计数documentOCG作为初始化是否已经发生的指示器。

但不幸的是,

var logoLayer = new PdfLayer(layername, stamper.Writer);
Run Code Online (Sandbox Code Playgroud)

打破了这个惰性初始化方案:它执行

writer.RegisterLayer(this);
Run Code Online (Sandbox Code Playgroud)

并被RegisterLayer定义PdfWriter

documentOCG[layer] = null;
Run Code Online (Sandbox Code Playgroud)

在给定的情况下。

因此,在大于0后,这会阻止惰性层初始化,因此在标记期间有效地删除了重要的层信息,除非之前已经发生过初始化。new PdfLayer(layername, stamper.Writer)documentOCG.Count

您的解决方法

//Don't delete otherwise the stamper flattens the layers
var layers = stamper.GetPdfLayers();
Run Code Online (Sandbox Code Playgroud)

本质上强制在调用构造函数之前进行初始化。PdfLayer

这个错误可以通过覆盖来修复RegisterLayerPdfStamperImp当然它必须是虚拟的);覆盖必须首先触发延迟初始化本身。实际上,延迟初始化应该使用独立标志并检查字典计数作为健全性检查。

iText 中存在模拟错误,可以使用StampInLayer.java重现。

您的代码中的问题

你归来

return (ms.GetBuffer());
Run Code Online (Sandbox Code Playgroud)

这是完全错误的:缓冲区通常比实际文件大,即您返回的 PDF 带有长尾垃圾字节。使用

return (ms.ToArray());
Run Code Online (Sandbox Code Playgroud)

反而。

一个误会

在您的问题和代码中,您认为您的问题是形式扁平化并试图进行干预。不过,表单扁平化与您的问题无关。表单扁平化是指扁平化(合并到内容中)AcroForm 表单字段值,例如文本字段或复选框。