更改PDF文件中的放大链接 [英] Changing zoom in links in PDF files

查看:80
本文介绍了更改PDF文件中的放大链接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to write some code that changes zoom in links in case of /Fit. I've come up with some code that shows value /Fit and changes it to XYZ 0 0 1 for 100% zoom, but the result PDF has still zoom of type /Fit. Here you can find my sample 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个元素.

Upon a request of @mkl, I paste my code that aims to change zoom in all kind of links in PDF files. By trial-and-error, I've found that arrays that hold information on zoom factor could be of size 15 and 30. Please see my code to find out how I arrived at that numbers. Additionally, Bruno Lowagie has once written, zoom factor of links in PDF could be of 3 and 5 elements.

...由5个值组成的目标的缩放系数 的类型是/XYZ

... zoom factor of destinations that consist of 5 values and that are of type /XYZ

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

... [5 0 R, /FitH, 795] has 3 elements (in other words d.size() == 5 is false)

样本pdf

我的代码:

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);
                }
            }
        }
    }
}    

推荐答案

简而言之

代码中的主要问题是,您应将目标数组元素d[1]设置为 string "XYZ 0 0 2",而应将d[1]设置为 name XYZ d[2]d[3]表示数字0,而d[4]表示数字zoom.

In a nutshell

The main issue in your code is that you try to set the destination array element d[1] to the string "XYZ 0 0 2" while you should have set d[1] to the name XYZ, d[2] and d[3] to the number 0, and d[4] to the number zoom.

另一个问题是,您尝试通过其Length确定目标数组的类型.该长度实际上是用于表示PDF文件中该数组的字符数.这个数字显然可以相差很大,里面可能有额外的空白,可能有任意精度的数字,等等,所以这不是一个很好的标准.您应该使用的是数组中的Size元素数量,并结合第二个元素的值.

A side issue is that you try to determine the type of the destination array by its Length. This length actually is the number of characters used to represent that array inside the PDF file. This number obviously can vary much, there may be additional white space inside, there may be numbers of arbitrary precision, etc., so this is not a good criterion. What you should use instead is the Size, the number of elements in the array, in combination with the value of the second element.

因此,您的方法可以像这样固定

Thus, your method can be fixed like this

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);
            }
        }
    }
}

如您所见,我为无效的目标添加了另外的if分支(有效的目标数组至少包含2个元素),并且尚未实现的目标类型,因为您尚未考虑很多目标类型.

As you see I have add additional if branches for invalid destinations (a valid destination array has at least 2 elements) and not yet implemented destination types as there are numerous destination types you do not yet consider.

PDF规范了解以下目标类型:

The PDF specification knows these destination types:

语法含义

[页面/XYZ 左上方缩放] 显示由 page 指定的页面,其坐标( left top )位于窗口的左上角,其中的内容页面被 zoom 因子放大.参数 left top zoom 中任何一个的空值表示该参数的当前值应保持不变. zoom 值为0与空值具有相同的含义.

[page /XYZ left top zoom] Display the page designated by page, with the coordinates (left, top) positioned at the upper-left corner of the window and the contents of the page magnified by the factor zoom. A null value for any of the parameters left, top, or zoom specifies that the current value of that parameter shall be retained unchanged. A zoom value of 0 has the same meaning as a null value.

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

[page /Fit] Display the page designated by page, with its contents magnified just enough to fit the entire page within the window both horizontally and vertically. If the required horizontal and vertical magnification factors are different, use the smaller of the two, centering the page within the window in the other dimension.

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

[page /FitH top] Display the page designated by page, with the vertical coordinate top positioned at the top edge of the window and the contents of the page magnified just enough to fit the entire width of the page within the window. A null value for top specifies that the current value of that parameter shall be retained unchanged.

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

[page /FitV left] Display the page designated by page, with the horizontal coordinate left positioned at the left edge of the window and the contents of the page magnified just enough to fit the entire height of the page within the window. A null value for left specifies that the current value of that parameter shall be retained unchanged.

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

[page /FitR left bottom right top] Display the page designated by page, with its contents magnified just enough to fit the rectangle specified by the coordinates left, bottom, right, and top entirely within the window both horizontally and vertically. If the required horizontal and vertical magnification factors are different, use the smaller of the two, centering the rectangle within the window in the other dimension.

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

[page /FitB] (PDF 1.1) Display the page designated by page, with its contents magnified just enough to fit its bounding box entirely within the window both horizontally and vertically. If the required horizontal and vertical magnification factors are different, use the smaller of the two, centering the bounding box within the window in the other dimension.

[页面/FitBH 顶部] (PDF 1.1)显示由 page 指定的页面,其垂直坐标 top 位于窗口的顶部边缘,并且内容页面放大到恰好适合窗口内边框的整个宽度. top 的空值表示该参数的当前值应保持不变.

[page /FitBH top] (PDF 1.1) Display the page designated by page, with the vertical coordinate top positioned at the top edge of the window and the contents of the page magnified just enough to fit the entire width of its bounding box within the window. A null value for top specifies that the current value of that parameter shall be retained unchanged.

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

[page /FitBV left] (PDF 1.1) Display the page designated by page, with the horizontal coordinate left positioned at the left edge of the window and the contents of the page magnified just enough to fit the entire height of its bounding box within the window. A null value for left specifies that the current value of that parameter shall be retained unchanged.

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

因此,仍有许多情况需要实施...

So there still are a number of cases to implement...

您观察到,如果 PageLayout OneColumn ,转换后的 Fit 目标现在跳到原始目标之后的页面强>.不过, PageLayout SinglePage 似乎一切正常.

As you observed the transformed Fit targets now jumped to the page after the original target if the PageLayout was OneColumn. Everything seemed ok for the PageLayout value SinglePage, though.

原因是我们为 Fit XYZ 替换插入了坐标(0,0).查看上面引用的规范摘录,我们看到 XYZ 表示要以给定坐标位于窗口左上角的方式显示页面!不过,通常,PDF页面的坐标系的坐标原点(0,0)位于页面的左下角甚至更低.

The cause is that we insert the coordinates (0,0) for the XYZ replacement of Fit. Looking into the quoted specification excerpt above we see that XYZ means that the page is to be displayed with the given coordinates positioned at the upper-left corner of the window! Usually, though, PDF pages have coordinate systems with the coordinate origin (0,0) in the lower-left corner of the page or even beneath.

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

Thus, what we interpreted as a jump to the following page actually is a jump where the bottom left of the target page is in the top left of the window... ;)

如果我们想为左上角使用更合适的坐标,则可以扩展固定方法,如下所示:

If we want to use more appropriate coordinates for the top left corner, we can extend the fixed method like this:

public static void ChangeZoomOfLinksFixedMore(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;
            if (d.Size < 2)
            {
                Console.WriteLine("Page {0} {1} {2}. Invalid.", i, actionType, d);
                continue;
            }

            float left = 0;
            float top = 0;
            PdfObject pageObject = PdfReader.GetPdfObjectRelease(d[0]);
            if (pageObject is PdfDictionary)
            {
                PdfArray cropBox = ((PdfDictionary)pageObject).GetAsArray(PdfName.CROPBOX);
                if (cropBox == null)
                    cropBox = ((PdfDictionary)pageObject).GetAsArray(PdfName.MEDIABOX);
                if (cropBox != null && cropBox.Size == 4)
                {
                    if (cropBox[0] is PdfNumber)
                        left = ((PdfNumber)cropBox[0]).FloatValue;
                    if (cropBox[3] is PdfNumber)
                        top = ((PdfNumber)cropBox[3]).FloatValue;
                }
            }

            // 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(left));
                d.Add(new PdfNumber(top));
                d.Add(new PdfNumber(zoom));
            }
            else
            {
                Console.WriteLine("Page {0} {1} {2}. To be implemented.", i, actionType, d.GetAsName(1));
            }
        }
    }
}

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

Beware, this does not yet consider all corner cases: The CropBox or MediaBox may be inherited from an ancestor node in the page tree, that box array might not be ordered in the usual manner, or the entries of that array might be indirect objects.

这篇关于更改PDF文件中的放大链接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆