I've been trying for days to get a CheckBox or Radio Button to render using PDF::API2 and haven't been able to.
I've poured over the PDFMark reference, PDF specification, and any examples I've been able to find. I can get simple Widget annotations to render, but haven't been able to get anything that requires an appearance stream or appearance dictionary to work correctly. Below is a selection of test code attempting to set up a checkbox:
#!/usr/bin/perl
use PDF::API2;
use PDF::API2::Basic::PDF::Utils;
# set up pdf
my $pdfOptions = {};
my $pdf = PDF::API2->new( \$pdfOptions );
my $page = $pdf->page();
$page->mediabox( 'Letter' );
my $AcroForm = PDFDict();
$AcroForm->{NeedAppearances} = PDFBool( 'true' );
$AcroForm->realise;
my @Annots;
my @Fields;
my $resourceObj = PDFDict();
$resourceObj->{Type} = PDFName( 'Font' );
$resourceObj->{Subtype} = PDFName( 'Type1' );
$resourceObj->{Name} = PDFName( 'ZaDb' );
$resourceObj->{BaseFont} = PDFName( 'ZapfDingbats' );
$resourceObj->realise();
$AcroForm->{DR} = PDFDict();
$AcroForm->{DR}->{Font} = PDFDict();
$AcroForm->{DR}->{ZaDb} = $resourceObj;
$AcroForm->realise();
my $item = PDFDict();
$item->{P} = $page;
$item->{Type} = PDFName( 'Annot' );
$item->{Subtype} = PDFName( 'Widget' );
$item->{FT} = PDFName( 'Btn' );
my $yes = PDFName( 'Yes' );
my $off = PDFName( 'Off' );
$item->{P} = $page;
$item->{Type} = PDFName( 'Annot' );
$item->{Subtype} = PDFName( 'Widget' );
$item->{Rect} = PDF::API2::Basic::PDF::Literal->new( "[100 300 200 400]" );
$item->{FT} = PDFName( 'Btn' );
$item->{T} = PDFStr( 'Urgent' );
$item->{V} = PDFName( 'Yes' );
$item->{AS} = PDFName( 'Yes' );
$item->{AP} = PDFDict();
$item->{AP}->{N} = PDFDict();
# My understanding is that these would be nulled to be used with NeedAppearances
$item->{AP}->{N}->{$yes} = PDFNull();
$item->{AP}->{N}->{$off} = PDFNull();
$item->realise();
push @Annots, $item;
push @Fields, $item if( $AcroForm );
$page->{Annots} = PDFArray( @Annots );
$AcroForm->{Fields} = PDFArray(@Fields) if( $AcroForm );
$pdf->{Root}->{AcroForm} = $AcroForm if( $AcroForm );
print $pdf->stringify();
exit;
I would expect to see a checkbox rendered towards the middle of this page, instead I get an empty, unusable annotation. I'm trying to get the NeedAppearances flag to work, as I'd given up attempting a proper appearance stream/appearance dictionary, but I would be grateful for solutions that use either method.
This is the code I finally got to render correctly, both in the browser and in Adobe Reader. Posting this because working examples of using this module for widget annotations are sparse.
The trick was: using {pdf}->new_obj to define objects and capture their references, as well as proper placement of the AcroForm, and finally, setting the {' stream'} property to an empty string, which I suppose forced the rendering of the stream / endstream tags, allowing the PDF readers an anchor in which to insert their appearance streams.
I used qpdf to analyze my output, which allowed me to see how the module's various methods were affecting the final PDF output.
#!/usr/bin/perl
# set up pdf
my $pdfOptions = {};
my $pdf = PDF::API2->new( \$pdfOptions );
my $page = $pdf->page();
$page->mediabox( 'Letter' );
my @Annots;
my @Fields;
my $fontObj = PDFDict();
$fontObj->realise();
$fontObj->{Type} = PDFName( 'Font' );
$fontObj->{Subtype} = PDFName( 'Type1' );
$fontObj->{BaseFont} = PDFName( 'Times-Roman' );
$fontObj = $pdf->{pdf}->new_obj( $fontObj );
my $resourceObj = PDFDict();
$resourceObj->realise();
$resourceObj->{Font} = PDFDict();
$resourceObj->{Font}->realise();
$resourceObj->{Font}->{F1} = $fontObj;
$resourceObj = $pdf->{pdf}->new_obj( $resourceObj );
my $AcroForm = PDFDict();
$AcroForm->realise();
$AcroForm->{DR} = $resourceObj;
$AcroForm->{NeedAppearances} = PDFBool( 'true' );
my $yesObj = PDF::API2::Resource::XObject::Form->new( $pdf );
$yesObj->{Resources} = $resourceObj;
$yesObj->{BBox} = PDF::API2::Basic::PDF::Literal->new( "[100 300 200 400]" );
$yesObj->realise();
$yesObj->{' stream'} = '';
$yesObj = $pdf->{pdf}->new_obj( $yesObj );
my $noObj = PDF::API2::Resource::XObject::Form->new( $pdf );
$noObj->{Resources} = $resourceObj;
$noObj->{Subtype} = PDFName( 'Form' );
$noObj->{BBox} = PDF::API2::Basic::PDF::Literal->new( "[100 300 200 400]" );
$noObj->realise();
$noObj->{' stream'} = '';
$noObj = $pdf->{pdf}->new_obj( $noObj );
my $item = PDFDict();
$item->{Type} = PDFName( 'Annot' );
$item->{Subtype} = PDFName( 'Widget' );
$item->{FT} = PDFName( 'Btn' );
$item->{T} = PDFStr( 'checkbox1' );
$item->{V} = PDFName( 'Yes' );
$item->{P} = $page;
$item->{Rect} = PDF::API2::Basic::PDF::Literal->new( "[100 300 200 400]" );
$item->{H} = PDFName( 'N' );
$item->{AS} = PDFName('Yes');
$item->{AP} = PDFDict();
$item->{AP}->realise();
$item->{AP}->{N} = PDFDict();
$item->{AP}->{N}->realise();
$item->{AP}->{N}->{'Yes'} = $yesObj;
$item->{AP}->{N}->{'Off'} = $noObj;
$item = $pdf->{pdf}->new_obj( $item );
$item->realise();
push @Annots, $item;
push @Fields, $item if( $AcroForm );
$page->{Annots} = PDFArray( @Annots );
$AcroForm->{Fields} = PDFArray(@Fields) if( $AcroForm );
$pdf->{catalog}->{'AcroForm'} = $AcroForm;
$pdf->{pdf}->out_obj($pdf->{catalog});
print $pdf->stringify();
exit;
Following code will mark PDF checkbox
#!/usr/bin/perl -w
use strict;
use CAM::PDF;
my $pdf = CAM::PDF->new('tenant.pdf') or die "Could not open PDF ($!)!";
my @fields = $pdf->getFormFieldList();
foreach my $field ( @fields ) {
if ($field =~ /Female/) {
my $ff_obj = $pdf->getFormField($field);
$ff_obj->{value}->{value}->{AS}->{value} = "On";
}
else {
$pdf->fillFormFields($field => $field);
}
}
$pdf->cleanoutput('afilled.pdf');
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.