Perl: CAM :: PDF вносит изменения, но они не материализуются в окончательном документе - PullRequest
1 голос
/ 19 февраля 2020

Я использовал модуль CAM :: PDF, чтобы попытаться отредактировать pdf документы на работе - по сути, просто пытался автоматически изменять дату на документах, чтобы показать, что они были недавно просмотрены

к сожалению, несмотря на мой код сообщая мне, что я делаю изменения в объектах PDF ($ pdf -> {changes}), и предоставляя файлам PDF do c пытается изменить максимальную доступность (любой может получить доступ, прочитать, написать), вывод PDF-файла никогда не кажется материализоваться с этими изменениями. Я также копировал tmp файлы объектного узла, которые я выводил по массе, и обнаружил, что все они не показывают никаких признаков старой даты после выполнения кода; Тем не менее, когда я просматриваю PDF после запуска, старая дата все еще на PDF. Кто-нибудь сталкивался с этим раньше или может что-нибудь предложить?

просто сделать это вручную не вариант; Я хочу написать сценарий, чтобы у меня был сценарий, который я просто запускаю для нескольких файлов одновременно (у меня есть МНОЖЕСТВО этих файлов, чтобы разобраться на работе), но кроме изменения дат, записанных в do c, do c должен продолжать выглядеть так же sh (я имею в виду, было бы хорошо, если бы они немного изменились в размере, но не хорошо, если бы они полностью изменились по внешнему виду)

Я строго следовал примеру 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 modification\n";
   }
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');

Любая помощь или совет будет принята с благодарностью

Ответы [ 3 ]

3 голосов
/ 24 февраля 2020

Быстрый поиск на странице 145 спецификации PDF [1] показывает, что есть 2 поля метаданных, которые должны позволять простое изменение для достижения того, что вы пытаетесь сделать.

  • CreationDate
  • ModDate

Ниже вы можете найти быстрый скрипт, использующий CAM :: PDF для установки / изменения ModDate с текущей датой, создавая иллюзию «изменения» PDF.

В сценарий можно при необходимости внести изменения, чтобы использовать указанную дату c вместо текущего времени для установки даты изменения.

Обратите внимание, что я не уверен, что 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";
}

2 голосов
/ 02 марта 2020

Я автор CAM :: PDF. Не видя PDF, я могу только догадываться, но держу пари, что проблема в том, что $olddate просто не соответствует ни одному тексту в do c. Кернинг может разбивать строки на несколько частей, например. Кроме того, есть несколько различных способов кодирования строк, которые выглядят одинаковыми в результирующем do c. Так что хитрость для вас будет выяснить, каков шаблон для дат в ваших спецификациях c.

Тем не менее, мне также нравится умная идея, которую @Bruce Ramos предложил в отдельном ответе. Этот подход не изменит дату, которая видна в отрендеренном PDF (например, если вы распечатаете ее), но она должна отображаться как метаданные практически в любом средстве просмотра PDF.

0 голосов
/ 06 марта 2020

Я обнаружил, что строка, которую я пытался отредактировать, на самом деле не была непрерывным набором символов в PDF, а скорее была внутри оператора TJ в строке BT в PDF. Я не вижу каких-либо условий для обработки случаев, когда желаемый текст находится в строках TJ в библиотеке CAM :: PDF (хотя, возможно, есть @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 CHANGES\n";  
        #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 . "stream\r\n";
        print OUT $bytes . "\r";
    } else {
        print OUT $orig_x . "stream\r\n";
        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 соблюдают

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...