Perl:CAM::P DF 进行了更改,但这些更改不会在最终文档中实现



我一直在使用CAM::PDF模块尝试在工作中编辑PDF文档-本质上只是尝试自动更改文档上的日期,以显示它们最近已经过审查

不幸的是,尽管我的代码告诉我我正在对PDF对象进行更改($PDF->{changes}(给pdf文档试图改变最大的可访问性(任何人都可以访问、读取、写入(pdf的输出似乎从未随着这些变化而实现。我也一直在批量输出对象节点tmp文件,发现在运行代码后,所有这些文件都没有旧日期的迹象;然而,当我在运行后查看pdf时,旧日期仍然在pdf上。以前有人遇到过这种情况吗?或者有什么建议吗?

手动操作不是一种选择;我想编写这个脚本,这样我就可以同时对多个文件运行一个脚本(我有很多文件要处理(,但除了更改文档上写的日期外,文档还必须保持原样(我的意思是,如果它们的大小稍微改变一点就可以了,但如果它们的外观完全改变了就不可以了(

我严格遵循了changepdfstring.pl的示例(https://metacpan.org/pod/distribution/CAM-PDF/bin/changepdfstring.pl)来自模块CAM::PDF的作者,关于如何为我的代码做到这一点,然后尝试了它的不同变体,试图让事情正常工作-所以我很困惑,最终什么都没起作用

#!/usr/bin/perl
use strict;
use warnings;
use CAM::PDF;
use Data::Dumper;
my $pdf = CAM::PDF->new('Order fulfilment process flowchart.pdf');
if (!$pdf->canModify())
{
die "This PDF forbids modificationn";
}
my $olddate = "15.02.2019";
my $newdate = "22.02.2022";
foreach my $objectnumber (keys %{$pdf->{xref}}){
my $objectnode = $pdf->dereference($objectnumber);
$pdf->changeString($objectnode, {$olddate=>$newdate});
}

my $change = $pdf->{changes};
print Dumper($change);
my $count = 0;
foreach my $objectnumber (keys %{$pdf->{xref}}){
my $objectnode = $pdf->dereference($objectnumber);
$count++;
open (ONO, ">tmp.objectnode.$count");
print ONO Dumper($objectnode);
close (ONO);}
if (!scalar %{$pdf->{changes}})
{
die "no changes were made :(";
}
$pdf->preserveOrder();
$pdf->cleanoutput('pleasework.pdf');

如有任何帮助或建议,将不胜感激

PDF规范[1]第145页中的快速搜索显示,有2个元数据字段应该允许进行简单的更改,以实现您想要做的事情。

  • 创作日期
  • ModDate

下面你可以找到一个使用CAM::PDF的快速脚本,用当前日期设置/修改ModDate,从而产生"修改"PDF的错觉。

如果需要,可以修改脚本以使用特定日期而不是当前时间来设置修改日期。

请注意,我不确定CAM::PDF是否是完成此任务的最佳选项。

该脚本只是在CAM::PDF的限制和简单性范围内可以完成的内容的一个示例。

[1]https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf

#!/usr/bin/env perl
use strict;
use warnings;
use Time::Local;
use CAM::PDF;
use CAM::PDF::Node;
my $infile = shift || die 'syntax...';
my $outfile = shift || die 'syntax...';
my $pdf = CAM::PDF->new($infile) || die;
my $info = $pdf->getValue($pdf->{trailer}->{Info});
if ($info) {
my @time = localtime(time);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = @time;
$year += 1900;
$mon++;
my $gmt_offset_in_seconds = timegm(@time) - timelocal(@time);
my $gmt_offset_min = ($gmt_offset_in_seconds / 60) % 60;
my $gmt_offset_hour = abs(int($gmt_offset_in_seconds / (60*60)));
my $offset_char = "";
if ($gmt_offset_in_seconds < 0) {
$offset_char = "-";
} else {
$offset_char = "+";
}
my $date = sprintf("D:%04d%02d%02d%02d%02d%02d%s%02d'%02d'", $year, $mon, $mday, $hour, $min, $sec, $offset_char, $gmt_offset_hour, $gmt_offset_min);
my $objnum = undef;
for my $obj ('Creator', 'Producer', 'CreationDate') {
if (exists $info->{$obj} and exists $info->{$obj}->{objnum}) {
$objnum = $info->{$obj}->{objnum};
last;
}
}
die "Cannot find objnum, halting..." if not defined $objnum;
my $mod_date = $info->{ModDate};
if ($mod_date) {
$mod_date->{value} = $date;
} else {
my $mod_date = new CAM::PDF::Node('string',$date);
$mod_date->{gennum} = 0;
$mod_date->{objnum} = $objnum;
$info->{ModDate} = $mod_date;
}
$pdf->preserveOrder();
$pdf->cleanoutput($outfile);
} else {
print "Cannot find PDF info section, doing nothing!n";
}

我是CAM::PDF的作者。在没有看到PDF的情况下,我只能猜测,但我敢打赌,问题是$olddate根本与文档中的任何文本都不匹配。例如,紧排可以将字符串分解为多个部分。此外,有几种不同的方法可以对字符串进行编码,在生成的文档中显示为。因此,对你来说,诀窍是弄清楚你特定文档中的日期模式是什么。

话虽如此,我也喜欢@Bruce Ramos在另一个答案中提出的聪明想法。这种方法不会改变在渲染的PDF中可见的日期(比如,如果你打印它(,但它应该在几乎任何PDF查看器中显示为元数据。

我发现我试图编辑的行实际上不是pdf中的一组连续字符,而是在pdf中BT行的TJ运算符中。我看不到任何关于处理所需文本位于CAM::PDF库中TJ行的情况的规定(尽管可能有@ChrisDolan?(,因此CAM::PDF无法对其进行操作或"交换"。在解压缩所有流(如果适用(后,我发现这行"TJ"中有我希望操作的文本:

[(D)-20(a)24(t)62(e)-46(:)86( )-46(1)52(5)-37(.)70(0)-37(2)52(.)-20(2)52(0)-37(1)52(9)] TJ

我认为CAM::PDF不可能作用于TJ行,也许它只能作用于TJ行

对于任何想快速解决这个问题的人来说,这个"肮脏"的脚本在这种情况下对我有效:

#!/usr/bin/perl
use strict;
use Compress::Raw::Zlib;
use bytes;
open(OUT,'>', "newfromoldscript.pdf");
my $fname = 'Order fulfilment process flowchart.pdf';
open(FILE, '<:raw', $fname) || die("can't open($fname): $!");
$/ = undef;
my $file = <FILE>;
my $file_len = length($file);
my $i = 0;
my $offset;
my $offset;
my $o;
do {
$o = doX(substr($file, $offset, $file_len), $i);
$offset+=$o;
$i++;
} while($o  && $i< 100);    
sub doX {
my $file = shift;
my $i = shift;
my $stream = index($file, "nstream");
if ($stream < 0) {
print OUT $file;
return 0;
}
$stream++;
my $deflate = 1;
my $line_before = rindex(substr($file,0,$stream), "<<");
print OUT substr($file,0,$line_before);
my $x = substr($file, $line_before,$stream-$line_before);
if ($i == 22) {
print "";
}
my $stream_len;
if ($x =~ /FlateDecode/Length (d+)>>/) {
$stream_len = $1;
}
if ($x =~ /FlateDecode/Length (d+)//) {
print "Warn Object $i has len/len what the even is this?n";
$stream_len = $1;
}
if ($x =~ /XML/Length (d+)>>/) {
$deflate = 0;
$stream_len = $1;
}
if (!$stream_len) { 
die("I fail with no stream len : $x");
}

print "-->$line_before,$i,$stream=$stream_len=$x<--n";
my $bytes = substr($file, $stream+8,$stream_len);
my $orig_bytes = $bytes;    # inflate seems to mangle bytes, so take a copy
my $o;
my $d=new Compress::Raw::Zlib::Inflate();
if ($deflate) {
$d->inflate($bytes,$o);
} else {
$o = $bytes;
}
my $orig_x = $x;
my $changes;
my %change = (
'-20(2)52(0)-37(.)52(.)' => '-20(2)52(0)-37(2)52(0)', #trialling different reg ex's here 
'-37(1)52(9)'=>'-37(2)52(0)', #reg ex's
'Date: 15.02.2019'=>'Date: 12.02.2020', 
'[(A)[d-]+(p)[d-]+(p)[d-]+(r)[d-]+(o)[d-]+(ve)[d-]+(d)[d-]+( )[d-]+(B[^]]+] TJ'=>'(Approved By: George W) Tj??G-TAG??' #scrap the whole TJ, replace for Tj
);
foreach my $re (keys %change) {
my $to = $change{$re};
$re =~ s/([()])/\1/g;     # escape round brackets
print $re;
open (GW, ">tmp.gw");
print GW $re;
close (GW);
if ($o=~/$re/m) {
$o =~ s/$re/$to/mg;
print $o;
$changes++;
}
}
if ($changes) {
print "n MADE CHANGESn";  
#split, get rid of the ? mark tag
my @remains = split('??G-TAG??', $o); 
my $firsthalf = $remains[0];
my $secondhalf = $remains[1];
#reverse the string
$firsthalf = scalar reverse ($firsthalf);
if ($firsthalf =~ m/fT 52.8 2F/){print "FOUND THE REVERSE"}
$firsthalf =~ s/fT 52.8 2F/fT 52.8 0F/;
#reg ex to back track to the nearest and thus relevant Font/F and set it to F0 

#put it back in correct orientation
$firsthalf = scalar reverse ($firsthalf);
$o = join("", $firsthalf, $secondhalf);
open (WEIRD, ">tmp.weird");
print WEIRD $firsthalf;
close (WEIRD);
$changes++;
my $d = new Compress::Raw::Zlib::Deflate();
my $obytes;
my $obytes2;
my $status = $d->deflate($o, $obytes);
$d->flush($obytes2);
$bytes = $obytes . $obytes2;
if (length($bytes) != $stream_len) {
my $l = length($bytes);
print "-->$x<--n";
warn("what do we do here $l != $stream_len");
$orig_x =~ s/$stream_len/$l/;
}
print OUT $orig_x . "streamrn";
print OUT $bytes . "r";
} else {
print OUT $orig_x . "streamrn";
print OUT $orig_bytes . "r";
}


open(TMP,">out/tmp.$i.bytes");
print TMP $o;
close(TMP);
return $stream + 8 + $stream_len + 1;
}

从本质上讲,我把TJ换成了TJ,用于将文档上其他人的名字更改为我的名字,这使得插入我的更改更简单(但可能会很混乱(。为了使其能够用大写字母显示,我必须反转字符串,并将其下的字体(F((F2(换成F0

对于与日期有关的TJ行,我将TJ字符换成了我希望更改的日期,这意味着我必须遵守"不友好"的语法TJ运算符行遵守

最新更新