我想编写一些代码,以便在/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文件中所有类型链接的缩放。通过反复试验,我发现包含缩放因子信息的数组可能大小为15
和30
。请查看我的代码,了解我是如何得出这些数字的。另外,Bruno Lowagie曾经写过,PDF中链接的缩放因子可以是3和5个元素。
...由5个值组成的目的地的缩放系数 属于/ XYZ
...
[5 0 R, /FitH, 795]
有3个元素(换句话说d.size() == 5
是 假)
我的代码:
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);
}
}
}
}
}
答案 0 :(得分:0)
您的代码中的主要问题是您尝试将目标数组元素d[1]
设置为字符串 "XYZ 0 0 2"
,而您应该将d[1]
设置为名称 XYZ ,d[2]
和d[3]
为0
,d[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 可以从页面树中的祖先节点继承,该框数组可能不是以通常的方式排序,或者该数组的条目可能是间接对象。