【问题标题】:Annotation(stamp) missed after flattened展平后缺少注释(戳记)
【发布时间】:2020-05-28 04:09:13
【问题描述】:

Annotation(PdfName.STAMP) 在被 itextsharp5.5.13.1 展平后丢失。 我有两个pdf。一个有效,另一个无效。 任何想法将不胜感激。

代码如下

string outFile = inputFile + "_f.pdf";
using (PdfReader pdfReader = new PdfReader(inputFileName))
{
    PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None))

    //pdfStamper.FormFlattening = true;
    //pdfStamper.FreeTextFlattening = true;
    pdfStamper.AnnotationFlattening = true;
    //pdfStamper.AcroFields.GenerateAppearances = true;

}

【问题讨论】:

标签: pdf itext flatten


【解决方案1】:

原因是iTextSharp中的一个bug,如果其边界框不使用原点作为左下角,则注释展平无法正确计算展平注释的位置.

如果您查看PdfStamperImp.FlattenAnnotations(bool) 的代码,您会很快意识到if (app != null) 块中的计算仅在边界框位于原点或不需要缩放来拟合外观边界时才有意义框到注释矩形中。

(由于边界框的左下角通常是原点,因此不会经常弹出。)

因此,为了展平此类注释,您必须使用不同的展平方法,例如像这样:

using (PdfReader pdfReader = new PdfReader(inputFileName))
{
    PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None));
    ImprovedAnnotationFlattening(pdfStamper);
    pdfStamper.Close();
}

使用这些辅助方法:

void ImprovedAnnotationFlattening(PdfStamper pdfStamper)
{
    double[] DEFAULT_MATRIX = { 1, 0, 0, 1, 0, 0 };

    PdfReader reader = pdfStamper.Reader;

    for (int page = 1; page <= reader.NumberOfPages; ++page)
    {
        PdfDictionary pageDic = reader.GetPageN(page);
        PdfArray annots = pageDic.GetAsArray(PdfName.ANNOTS);

        if (annots == null)
            continue;

        for (int idx = 0; idx < annots.Size; ++idx)
        {
            PdfObject annoto = annots.GetDirectObject(idx);
            if (!(annoto is PdfDictionary))
                continue;

            PdfDictionary annDic = (PdfDictionary)annoto;

            PdfNumber ff = annDic.GetAsNumber(PdfName.F);
            int flags = ff != null ? ff.IntValue : 0;
            if ((flags & PdfFormField.FLAGS_PRINT) == 0 || (flags & PdfFormField.FLAGS_HIDDEN) != 0)
                continue;

            PdfObject obj1 = annDic.Get(PdfName.AP);
            if (obj1 == null)
                continue;
            PdfDictionary appDic = obj1 is PdfIndirectReference
                ? (PdfDictionary)PdfReader.GetPdfObject(obj1)
                : (PdfDictionary)obj1;
            PdfObject obj = appDic.Get(PdfName.N);
            PdfStream objDict = appDic.GetAsStream(PdfName.N);

            if (objDict != null)
            {
                Rectangle rect = PdfReader.GetNormalizedRectangle(annDic.GetAsArray(PdfName.RECT));
                Rectangle bbox = PdfReader.GetNormalizedRectangle(objDict.GetAsArray(PdfName.BBOX));

                PdfContentByte cb = pdfStamper.GetOverContent(page);
                cb.SetLiteral("Q ");

                PdfArray matrixArray = objDict.GetAsArray(PdfName.MATRIX);
                double[] matrix = matrixArray != null ? matrixArray.AsDoubleArray() : DEFAULT_MATRIX;
                AffineTransform transform = new AffineTransform(matrix);

                double[] bboxCorners = { bbox.Left, bbox.Bottom, bbox.Right, bbox.Bottom, bbox.Right, bbox.Top, bbox.Left, bbox.Top };
                transform.Transform(bboxCorners, 0, bboxCorners, 0, 4);
                double minX = Min(bboxCorners, 0, 2);
                double maxX = Max(bboxCorners, 0, 2);
                double minY = Min(bboxCorners, 1, 2);
                double maxY = Max(bboxCorners, 1, 2);

                transform.preConcatenate(AffineTransform.GetTranslateInstance(-minX, -minY));
                transform.preConcatenate(AffineTransform.GetScaleInstance(rect.Width/(maxX-minX), rect.Height/(maxY-minY)));
                transform.preConcatenate(AffineTransform.GetTranslateInstance(rect.Left, rect.Bottom));

                transform.GetMatrix(matrix);

                cb.AddFormXObj(objDict, GenerateName(), matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);

                cb.SetLiteral("q ");

                annots.Remove(idx);
                --idx;
            }
        }
    }
}

double Min(double[] array, int start, int step)
{
    double result = array[start];

    for (int i = start + step; i < array.Length; i+=step)
    {
        result = Math.Min(result, array[i]);
    }

    return result;
}

double Max(double[] array, int start, int step)
{
    double result = array[start];

    for (int i = start + step; i < array.Length; i += step)
    {
        result = Math.Max(result, array[i]);
    }

    return result;
}

PdfName GenerateName()
{
    PdfName name = new PdfName("XXX" + formXObjectsCounter);
    ++formXObjectsCounter;
    return name;
}

int formXObjectsCounter = 4711;

注意:我刚刚编写了这些方法(尽可能多地从原始扁平化代码中复制),并且仅使用您的示例文件进行了测试。一些边界条件可能仍需考虑用于一般用途。特别是我没有做所有相关的null0 测试。我也没有尝试支持正确的标记。

【讨论】:

  • 感谢您的快速答复! “ImprovedAnnotationFlattening”方法效果很好。不好意思,其实我的产品环境是itextsharp5.1.3。考虑到直接升级到5.5.13.1的风险,5.1.3版本有对应的解决方案吗? @mkl
  • 您在 5.1.3 中尝试过该代码吗?我没有尝试过,但我没有使用任何概念上新的东西,所以可能一些小的更改足以使代码在 5.1.3 下运行。
  • 另外,我对扁平化 Form 和 FreeText 等有同样的问题。我想系统地学习itext,一些建议将不胜感激。 @mkl
  • 我已经在 itextsharp5.1.3(早于 5.5.13.1)中尝试过该代码。但是没有找到 PdfContentByte#AddFormXObj ;-)
  • 您可能想尝试用代码 that method currently executes 替换该调用。 ISO 一致性检查和if (IsTagged()) 块中的内容是可选的...
猜你喜欢
  • 2013-12-29
  • 2013-09-17
  • 1970-01-01
  • 1970-01-01
  • 2019-09-07
  • 1970-01-01
  • 1970-01-01
  • 2019-05-06
  • 2017-09-06
相关资源
最近更新 更多