iTextSharp 7:调整图章大小的正确方法?

iTextSharp 7: Proper Way to Resize Stamps?

我最近总体上接触了 iText -- 看过第 5 版和第 7 版后,我仍然对图章的实现方式感到困惑。我按照使用 iTextSharp 7 的示例代码之一来添加注释:

 PdfReader reader = new PdfReader(source);
        PdfWriter writer = new PdfWriter(dest);
        PdfDocument pdfDoc = new PdfDocument(reader, writer);

        Rectangle crop = pdfDoc.GetPage(1).GetCropBox();
        Debug.WriteLine("CropBox Rectangle Dim "+crop);


        ImageData img = ImageDataFactory.Create(imgsrc);

        float iWidth = img.GetWidth();
        float iHeight = img.GetHeight();

        //Ignore the below statement
        if (crop.GetWidth() > crop.GetHeight())
        {
            w = crop.GetWidth();
            h = crop.GetHeight();
        }
        else
        {
            w = crop.GetHeight();
            h = crop.GetWidth();
        }


        Debug.WriteLine("Width = "+w+" and Height = "+h);


        Rectangle location = new Rectangle(crop.GetLeft(),crop.GetBottom(),iWidth,iHeight);

        //Creates a Stamp Bounding Box on "Location"
        PdfStampAnnotation stamp = new PdfStampAnnotation(location).SetStampName(new PdfName("Logo"));

        PdfFormXObject xObj = new PdfFormXObject(new Rectangle(iWidth, iHeight));
        PdfCanvas canvas = new PdfCanvas(xObj, pdfDoc);

        canvas.AddImage(img, 0, 0, false);
        stamp.SetNormalAppearance(xObj.GetPdfObject());



        stamp.SetFlags(PdfAnnotation.PRINT);

        pdfDoc.GetFirstPage().AddAnnotation(stamp);
        pdfDoc.Close();

首先,我注意到我正在使用一个 ImageData 对象来输入我的图像。但是,我找不到任何方法来 "scale" 图像 - 类似于 Image.scaleAbsolute。输出的 PDF 最终正确标记,但完全过大。我想我明白 FormXObject 代表什么,但是 Canvas 有什么用?它对应于PDF中的什么。任何澄清都可能对协助未来的实施大有帮助。

谢谢

缩减规模

I can't find any method to "scale" the image down

正如评论中已经解释的那样,AddImage 方法的重载确实允许缩放图像,特别是:

/// <summary>Creates Image XObject from image and adds it to the specified position with specified width preserving aspect ratio.
///     </summary>
/// <param name="asInline">true if to add image as in-line.</param>
/// <returns>created XObject or null in case of in-line image (asInline = true).</returns>
public virtual PdfXObject AddImage(ImageData image, float x, float y, float width, bool asInline)

/// <summary>Creates Image XObject from image and adds it to canvas.</summary>
/// <param name="asInline">true if to add image as in-line.</param>
/// <returns>created XObject or null in case of in-line image (asInline = true).</returns>
public virtual PdfXObject AddImage(ImageData image, iText.Kernel.Geom.Rectangle rect, bool asInline)

/// <summary>Creates Image XObject from image and adds it to canvas.</summary>
/// <param name="image">
/// the
/// <c>PdfImageXObject</c>
/// object
/// </param>
/// <param name="a">an element of the transformation matrix</param>
/// <param name="b">an element of the transformation matrix</param>
/// <param name="c">an element of the transformation matrix</param>
/// <param name="d">an element of the transformation matrix</param>
/// <param name="e">an element of the transformation matrix</param>
/// <param name="f">an element of the transformation matrix</param>
/// <param name="asInline">true if to add image as in-line.</param>
/// <returns>created Image XObject or null in case of in-line image (asInline = true).</returns>
public virtual PdfXObject AddImage(ImageData image, float a, float b, float c, float d, float e, float f, 
    bool asInline)

这些重载中的第一个已经帮助了 OP。

背景

I think I understand what the FormXObject represents, but what is the Canvas for? What does it correspond to in the PDF.

[...]

What is the difference between the "Location" rectangle and the PdfFormXObject rectangle

location 矩形

正如 PDF 规范 ISO 32000-1(第 2 部分将于今年发布)所说

An annotation associates an object such as a note, sound, or movie with a location on a page of a PDF document

(第 12.5.1 节注释 - 常规)

因此,要为注释修复的第一件事就是 location,它是一个矩形,

The annotation rectangle, defining the location of the annotation on the page in default user space units.

(第 12.5.2 节注释词典)

此处使用的坐标系与为页面定义的 MediaBox 一致,CropBox 是显示部分。

如果是 OP 代码,请在此处选择此注释矩形:

Rectangle location = new Rectangle(crop.GetLeft(),crop.GetBottom(),iWidth,iHeight);
PdfStampAnnotation stamp = new PdfStampAnnotation(location)...

即注释矩形 location 位于可见页面区域的左下角,宽度和高度 iWidth,iHeight.

PdfFormXObject 矩形

但是注释看起来怎么样?实际上注释可以有不同的外观,例如取决于光标是否悬停在它们上面。因此,每个注释对象可能有一个或多个 appearances 在其外观字典中定义为单独的 appearance streams:

Appearance streams enable the annotation to be presented visually in different ways to reflect its interactions with the user. Each appearance stream is a form XObject: a self-contained content stream that shall be rendered inside the annotation rectangle.

(第 12.5.5 节外观流)

所以这里 XObject 形式成为故事的一部分:它们是自包含的,也可以从其他内容流中引用,而不仅仅是从注释中引用。

它们实际上非常独立于注释,以至于它们有自己的坐标系(由其边界框 BBox 给出)并且仅在显示时适合注释矩形,可能仿射变换后(由其矩阵 Matrix 给出):

Algorithm: Appearance streams

a) The appearance’s bounding box (specified by its BBox entry) shall be transformed, using Matrix, to produce a quadrilateral with arbitrary orientation. The transformed appearance box is the smallest upright rectangle that encompasses this quadrilateral.

b) A matrix A shall be computed that scales and translates the transformed appearance box to align with the edges of the annotation’s rectangle (specified by the Rect entry). A maps the lower-left corner (the corner with the smallest x and y coordinates) and the upper-right corner (the corner with the greatest x and y coordinates) of the transformed appearance box to the corresponding corners of the annotation’s rectangle.

c) Matrix shall be concatenated with A to form a matrix AA that maps from the appearance’s coordinate system to the annotation’s rectangle in default user space:

AA = Matrix * A

(第 12.5.5 节外观流)

如果是 OP 代码,请在此处选择此边界框:

PdfFormXObject xObj = new PdfFormXObject(new Rectangle(iWidth, iHeight));

矩阵未明确给出,因此默认为单位矩阵。

即外观的边界框(PdfFormXObject 矩形)与注释矩形一样具有宽度和高度 iWidth, iHeight,但其左下角是其坐标系的原点 (0, 0)。

通过上面的算法,它将平滑地适合注释矩形,没有任何扭曲。

有人可能想知道为什么有这么多独立的坐标系需要这样的转换。原因很简单:这使得表单 XObjects 变得容易 re-used,并且通过选择坐标系使其中的绘图指令尽可能简单。

Canvas

PdfCanvas 只是一个助手 class 在 iText 中提供了一种统一的方式来为任何内容流创建内容指令,这里是 XObject 内容流的外观。