Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Re^2: PDF Document Level Script

by Arik123 (Beadle)
on Dec 20, 2017 at 07:49 UTC ( #1205889=note: print w/replies, xml ) Need Help??

in reply to Re: PDF Document Level Script
in thread PDF Document Level Script

Thanks.... PDF::Reuse works to some degree, but it can handle only functions in the documents level script. I want some of the code there to be executed once the document is opened, and PDF::Reuse doesn't provide that capability. My template PDF did have such code, but PDF::Reuse simply deletes it (it doesn't recognize any JS code that isn't a function).

So I'm still looking for a better solution. Any ideas? Maybe I can somehow locate that stream object, using CAM::PDF, and manually edit its content?

Replies are listed 'Best First'.
Re^3: PDF Document Level Script
by marto (Cardinal) on Dec 20, 2017 at 10:22 UTC

    As far as better solutions go, I've no further ideas I'm afraid, in order to provide any I'd have to do the same research as you. Feel free to report back with your findings.

      Okay, there is a way to modify document-level JS with CAM::PDF. I created a PDF with document-level JS, and below is how I replace it with a new JS code. Note that your template may work differently. For example, the JS code is stored in a pdf object, which may contain its length, or alternatively contain a reference to another pdf object that contains the length.

      my $pdf = CAM::PDF->new($file); my $p3 = $pdf->getObjValue($pdf->getObjValue($pdf->getObjValue($pdf->g +etRootDict()->{Names}{value}{JavaScript}{value})->{Names}{value}[1]{v +alue})->{JS}{value}); $p3->{StreamData}{value} = <<EO_JS; this.getField("AfterSave1").display = display.hidden; this.getField("AfterSave2").display = display.hidden; this.dirty=false; EO_JS $n = $pdf->dereference(CAM::PDF::Node->new('reference', $p3->{Length}{ +value})->{value}); $n->{value}{value} = length $p3->{StreamData}{value};;

      For some reason, I'm not sure that's the way CAM::PDF was meant to be used...

        For some reason, I'm not sure that's the way CAM::PDF was meant to be used...

        :-) Maybe not. Here I formatted your code a little, to be able to read it:

        my $pdf = CAM::PDF-> new( $file ); my $p3 = $pdf-> getObjValue( $pdf-> getObjValue( $pdf-> getObjValue( $pdf-> getRootDict-> { Names }{ value }{ JavaScript }{ val +ue } )->{ Names }{ value }[ 1 ]{ value } )-> { JS }{ value } ); $p3-> { StreamData }{ value } = << EO_JS; this.getField("AfterSave1").display = display.hidden; this.getField("AfterSave2").display = display.hidden; this.dirty=false; EO_JS $n = $pdf-> dereference( CAM::PDF::Node-> new( 'reference', $p3-> { Length }{ value })-> { +value } ); $n->{ value }{ value } = length $p3-> { StreamData }{ value };

        It's good you found solution that works for your template, and I understand (you said it yourself) this code wasn't meant to be universal. And yet, it wasn't necessary to make so many assumptions.

        $pdf-> getObjValue( $node-> { value }) $pdf-> getObjValue( $node-> { value }{ a_key }{ value })

        In 1st line, node's type is assumed to be 'reference' (i.e., it's indirect object in PDF structure). In 2nd line it's assumed to be direct object, in this case node's type is 'dictionary', which dictionary has an entry keyed by 'a_key' which (entry), in turn, is assumed to be indirect object, i.e. that node's type assumed as 'reference'! Don't do that. Consider:

        $pdf-> getObjValue( $node-> { value }) $pdf-> getValue( $node )

        If node's type is 'reference', then these 2 lines return same result. But, not only 2nd line is shorter, it will also work for direct objects, i.e. if node's type is dictionary, array, number, etc. In fact, I don't remember to ever use the getObjValue.

        CAM::PDF::Node-> new( 'reference', $node-> { value })-> { value } $node-> { value }

        These 2 lines produce the same result :-) (i.e., if node's type is 'reference'). But of course it's better to add new stream using designated method, then it won't be necessary to hack the 'Length' property (it will be added automatically).

        use strict; use warnings; use feature 'say'; use CAM::PDF; sub _n { CAM::PDF::Node-> new( @_ )} my $js_code = << 'EO_JS'; this.getField("AfterSave1").display = display.hidden; this.getField("AfterSave2").display = display.hidden; this.dirty=false; EO_JS my $file = $ARGV[ 0 ] or die; my $pdf = CAM::PDF-> new( $file ) or die; my $root = $pdf-> getRootDict; my $names_dict = $pdf-> getValue( $root-> { Names }); my $js_tree = $pdf-> getValue( $names_dict-> { JavaScript }); my $js_tree_root_ary = $pdf-> getValue( $js_tree-> { Names }); my $js_code_stream = $pdf-> createStreamObject( $js_code, 'FlateDeco +de' ); my $js_code_obj = $pdf-> appendObject( undef, $js_code_stream, 0 +); my $js_code_ref = _n( reference => $js_code_obj ); my $js_action_dict = _n( dictionary => { JS => $js_code_ref, S => _n( label => 'JavaScript' ), }); my $js_action_obj = $pdf-> appendObject( undef, _n( object => $js_a +ction_dict ), 0 ); my $js_action_ref = _n( reference => $js_action_obj ); my $js_code_name = _n( label => 'my_js_code' ); push @{ $js_tree_root_ary }, $js_code_name, $js_action_ref; $file =~ s/\.pdf$/++$&/i; $pdf-> cleanoutput( $file );

        The only assumption (i.e. no checks) in code above is that input file already has some 'document-level javascript'. Then we just push 2 new entries into already existing array. However, I suspect that it is some dummy JS in your template, since you replace it (not append) with new code. Then, it wasn't necessary to stuff this dummy JS into template to begin with, you could add all necessary top level structures programmatically (similar to what is shown above). Try it for exercise, if so desired :-).

        Nice! Also thanks for the update, hopefully this will be useful to someone in future. I haven't had cause to work with PDFs for some time now.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1205889]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (4)
As of 2023-01-30 17:55 GMT
Find Nodes?
    Voting Booth?

    No recent polls found