首页 > 解决方案 > 如何使用 Perl 的 PDF::API2 以 PDF 格式呈现复选框

问题描述

几天来,我一直在尝试让 CheckBox 或 Radio Button 使用 PDF::API2 进行渲染,但一直未能成功。

我翻阅了 PDFMark 参考资料、PDF 规范以及我能找到的任何示例。我可以获得简单的 Widget 注释来渲染,但无法获得任何需要外观流或外观字典才能正常工作的东西。下面是一些尝试设置复选框的测试代码:

#!/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;

我希望看到这个页面中间呈现一个复选框,而不是我得到一个空的、不可用的注释。我正在尝试使 NeedAppearances 标志起作用,因为我已经放弃尝试正确的外观流/外观字典,但我会感谢使用这两种方法的解决方案。

标签: perlpdfpdf-generationacrofields

解决方案


这是我最终在浏览器和 Adob​​e Reader 中正确渲染的代码。发布此内容是因为使用此模块进行小部件注释的工作示例很少。

诀窍是:使用 {pdf}->new_obj 定义对象并捕获它们的引用,以及正确放置 AcroForm,最后,将 {'stream'} 属性设置为空字符串,我想这会强制渲染流/端流标签,允许 PDF 阅读器插入其外观流的锚点。

我使用 qpdf 来分析我的输出,这让我可以看到模块的各种方法如何影响最终的 PDF 输出。

    #!/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;

推荐阅读