更改PDF文件中的链接缩放

时间:2017-05-02 16:30:56

标签: c# pdf itext

我想编写一些代码,以便在/Fit的情况下更改链接的缩放。我想出了一些显示值/Fit的代码,并将其更改为XYZ 0 0 1进行100%缩放,但结果PDF仍然缩放/Fit类型。 Here您可以找到我的样本PDF。

PdfDictionary page = reader.GetPageN(i);
PdfArray annotationsArray = page.GetAsArray(PdfName.ANNOTS);
if (annotationsArray == null)
    continue;


for (int j = 0; j < annotationsArray.Size; j++)
{
    PdfDictionary annotation = annotationsArray.GetAsDict(j);
    PdfDictionary annotationAction = annotation.GetAsDict(PdfName.A);
    if (annotationAction == null)
        continue;

    if (PdfName.GOTO.Equals(annotationAction.Get(PdfName.S)))
    {
        PdfArray d = annotationAction.GetAsArray(PdfName.D);
        if (d.Length == 15)
        {
            d[1] = new PdfString("XYZ 0 0 1");
            //Console.WriteLine(d[1]); // shows /Fit for my sample PDF
        }
    }
}

修改

根据@mkl的请求,我粘贴了我的代码,旨在更改PDF文件中所有类型链接的缩放。通过反复试验,我发现包含缩放因子信息的数组可能大小为1530。请查看我的代码,了解我是如何得出这些数字的。另外,Bruno Lowagie曾经写过,PDF中链接的缩放因子可以是3和5个元素。

  

...由5个值组成的目的地的缩放系数   属于/ XYZ

     

... [5 0 R, /FitH, 795]有3个元素(换句话说d.size() == 5是   假)

Sample pdfs

我的代码:

public static void ChangeZoomOfLinks(PdfReader reader, double zoom = 1)
{
    for (int i = 1; i < reader.NumberOfPages; i++)
    {
        PdfDictionary page = reader.GetPageN(i);
        PdfArray annotationsArray = page.GetAsArray(PdfName.ANNOTS);
        if (annotationsArray == null)
            continue;

        for (int j = 0; j < annotationsArray.Size; j++)
        {
            PdfDictionary annotation = annotationsArray.GetAsDict(j);
            PdfDictionary annotationAction = annotation.GetAsDict(PdfName.A);
            if (annotationAction == null)
                continue;

            if (PdfName.GOTO.Equals(annotationAction.Get(PdfName.S)))
            {
                PdfArray d = annotationAction.GetAsArray(PdfName.D);
                if (d == null)
                    continue;

                // for custom zoom type, e.g. 100 
                if (d.Length == 30)     //  this length is in: lock.pdf
                {
                    Console.WriteLine("Custom zoom of: {0}. Table length: {1}", d[4], d.Length);
                    d[4] = new PdfNumber(zoom);
                }
                // for Fit zoom
                else if (d.Length == 15) // this length is in: tony.pdf
                {
                    Console.WriteLine("Custom zoom of: {0}. Table length: {1}", d[1], d.Length);
                    d[1] = new PdfString("XYZ 0 0 2");
                }
            }
            // below is ported code of Bruno Lowagie
            else if (PdfName.LINK.Equals(annotationAction.Get(PdfName.S)))
            {
                PdfArray d = annotation.GetAsArray(PdfName.DEST);
                if (d != null && d.Length == 5 && PdfName.XYZ.Equals(d.GetAsName(1)))
                {
                    //Console.WriteLine(d[4]);
                    d[4] = new PdfNumber(zoom);
                }
            }
        }
    }
}    

1 个答案:

答案 0 :(得分:0)

简而言之

您的代码中的主要问题是您尝试将目标数组元素d[1]设置为字符串 "XYZ 0 0 2",而您应该将d[1]设置为名称 XYZ d[2]d[3]0d[4]为{{1} }}

副作用是您尝试通过zoom确定目标数组的类型。该长度实际上是用于表示PDF文件中的该数组的字符数。这个数字显然可以变化很大,里面可能有额外的空白区域,可能有任意精度的数字等,所以这不是一个好的标准。您应该使用的是数组中的Length元素数,以及第二个元素的值。

修复方法

因此,您的方法可以像这样修复

Size

如您所见,我为无效目的地添加了额外的public static void ChangeZoomOfLinksFixed(PdfReader reader, double zoom = 1) { for (int i = 1; i < reader.NumberOfPages; i++) { PdfDictionary page = reader.GetPageN(i); PdfArray annotationsArray = page.GetAsArray(PdfName.ANNOTS); if (annotationsArray == null) continue; for (int j = 0; j < annotationsArray.Size; j++) { PdfDictionary annotation = annotationsArray.GetAsDict(j); PdfDictionary annotationAction = annotation.GetAsDict(PdfName.A); if (annotationAction == null) continue; PdfName actionType = annotationAction.GetAsName(PdfName.S); PdfArray d = null; if (PdfName.GOTO.Equals(actionType)) d = annotationAction.GetAsArray(PdfName.D); else if (PdfName.LINK.Equals(actionType)) d = annotation.GetAsArray(PdfName.DEST); if (d == null) continue; // for custom zoom type if (d.Size == 5 && PdfName.XYZ.Equals(d.GetAsName(1))) { Console.WriteLine("Page {0} {1} XYZ; former zoom {2}.", i, actionType, d[4]); d[4] = new PdfNumber(zoom); } // for Fit zoom else if (d.Size == 2 && PdfName.FIT.Equals(d.GetAsName(1))) { Console.WriteLine("Page {0} {1} Fit.", i, actionType); d[1] = PdfName.XYZ; d.Add(new PdfNumber(0)); d.Add(new PdfNumber(0)); d.Add(new PdfNumber(zoom)); } else if (d.Size > 1) { Console.WriteLine("Page {0} {1} {2}. To be implemented.", i, actionType, d.GetAsName(1)); } else { Console.WriteLine("Page {0} {1} {2}. Invalid.", i, actionType, d); } } } } 分支(有效目标数组至少有2个元素)并且尚未实现目标类型,因为您还有许多目标类型尚未考虑。

指定的目的地类型

PDF规范知道这些目的地类型:

  

语法含义

     

[页面 / XYZ 左上角缩放]   显示页面指定的页面,坐标(顶部)位于窗口的左上角,内容为页面放大了因子 zoom 。任何参数 left top zoom 的空值指定该参数的当前值应保持不变。 zoom 值为0,其含义与空值相同。

     

[页面 / Fit]   显示页面指定的页面,其内容放大到足以在窗口内水平和垂直适合整个页面。如果所需的水平和垂直放大系数不同,请使用两者中较小的一个,将窗口中的页面居中放在另一个维度中。

     

[页面 / FitH 顶部]   显示 page 指定的页面,垂直坐标 top 位于窗口的上边缘,页面内容放大到足以适合整个宽度窗口内的页面。 top 的空值指定该参数的当前值应保持不变。

     

[页面 / FitV ]   显示 page 指定的页面,水平坐标 left 位于窗口的左边缘,页面内容放大到足以适合整个高度窗口内的页面。 left 的空值指定该参数的当前值应保持不变。

     

[ / FitR 左下方右上方]   显示 page 指定的页面,其内容放大到足以适合坐标 left bottom 右边指定的矩形 top 完全在窗口内水平和垂直。如果所需的水平和垂直放大系数不同,请使用两者中较小的一个,将窗口中的矩形居中放在另一个维度中。

     

[ page / FitB]   (PDF 1.1)显示 page 指定的页面,其内容放大到足以在窗口内完全适合水平和垂直方向的边界框。如果所需的水平和垂直放大系数不同,请使用两者中较小的一个,将窗口中的边界框居中放在另一个维度中。

     

[页面 / FitBH 顶部]   (PDF 1.1)显示页面指定的页面,垂直坐标 top 位于窗口的上边缘,内容为页面放大到足以适合窗口内边界框的整个宽度。 top 的空值指定该参数的当前值应保持不变。

     

[页面 / FitBV ]   (PDF 1.1)显示页面指定的页面,水平坐标 left 位于窗口的左边缘,并且内容为页面放大到足以适合窗口内边界框的整个高度。 left 的空值指定该参数的当前值应保持不变。

     

(ISO 32000-1,表151 - 目标语法)

所以仍有许多案例需要实施......

进一步修复您的方法

如果 PageLayout OneColumn 您观察到转化的 Fit 目标现在跳转到原始目标后的页面。但是,对于 PageLayout SinglePage ,一切似乎都可以。

原因是我们为 XYZ 替换 Fit 插入坐标(0,0)。查看上面引用的规范摘录,我们看到 XYZ 表示页面将以显示,其中给定的坐标位于窗口的左上角 的!但是,通常情况下,PDF页面的坐标系统在页面的左下角角落中具有坐标原点(0,0),甚至位于下方。

因此,我们解释为跳转到下一页实际上是一个跳转,其中目标页面的左下角位于窗口的左上角...;)

如果我们想在左上角使用更合适的坐标,我们可以像这样扩展固定方法:

if

请注意,这还没有考虑所有极端情况: CropBox MediaBox 可以从页面树中的祖先节点继承,该框数组可能不是以通常的方式排序,或者该数组的条目可能是间接对象。