将FreeText注释添加到PDF

时间:2014-06-13 12:19:24

标签: c++ ios pdf annotations podofo

我正在使用podofo进行PDF操作,例如根据我在iOS应用程序中的要求添加注释,签名等。我首先尝试了podofo library available唯一可行的样本。但是样本的问题是添加的注释在GoogleAdobe Reader等任何预览中都没有显示。这就是问题。

根据Adobe的一些指南,我发现需要Appearance Key才能显示FreeText annotation。我尝试在文本编辑器中分析原始pdf文件,以查看具有正确注释的PDF的差异,并使用podofo创建PDF注释。我发现AP N个密钥带有一个stream对象,该对象采用编码形式进行注释,而podofo示例中缺少该对象。

然后在搜索之后我找到了podofo自己的样本并试图使用代码,这似乎做得正确,但也没有用,我知道我错过了什么,但不知道是什么,在哪里,请看下面的代码

+(void)createFreeTextAnnotationOnPage:(NSInteger)pageIndex doc:(PdfMemDocument*)aDoc rect:(CGRect)aRect borderWidth:(double)bWidth title:(NSString*)title content:(NSString*)content bOpen:(Boolean)bOpen color:(UIColor*)color {
    PoDoFo::PdfMemDocument *doc = (PoDoFo::PdfMemDocument *) aDoc;
    PoDoFo::PdfPage* pPage = doc->GetPage(pageIndex);
    if (! pPage) {
        // couldn't get that page
        return;
    }

    PoDoFo::PdfRect rect;
    rect.SetBottom(aRect.origin.y);
    rect.SetLeft(aRect.origin.x);
    rect.SetHeight(aRect.size.height);
    rect.SetWidth(aRect.size.width);


    PoDoFo::PdfString sTitle(reinterpret_cast<const PoDoFo::pdf_utf8*>([title UTF8String]));
    PoDoFo::PdfString sContent(reinterpret_cast<const PoDoFo::pdf_utf8*>([content UTF8String]));

    PoDoFo::PdfFont* pFont = doc->CreateFont( "Helvetica", new PoDoFo::PdfIdentityEncoding( 0, 0xffff, true ) );


    std::ostringstream  oss;
    oss << "BT" << std::endl << "/" <<   pFont->GetIdentifier().GetName()
    << " "  <<   pFont->GetFontSize()
    << " Tf " << std::endl;

    [APDFManager WriteStringToStream:sContent :oss :pFont];
    oss << "Tj ET" << std::endl;

    PoDoFo::PdfDictionary fonts;
    fonts.AddKey(pFont->GetIdentifier().GetName(), pFont->GetObject()->Reference());
    PoDoFo::PdfDictionary resources;
    resources.AddKey( PoDoFo::PdfName("Fonts"), fonts );

    PoDoFo::PdfAnnotation* pAnnotation =
    pPage->CreateAnnotation( PoDoFo::ePdfAnnotation_FreeText, rect );



    pAnnotation->SetTitle( sTitle );
    pAnnotation->SetContents( sContent );
    //pAnnotation->SetAppearanceStream( &xObj );
    pAnnotation->GetObject()->GetDictionary().AddKey( PoDoFo::PdfName("DA"), PoDoFo::PdfString(oss.str()) );
    pAnnotation->GetObject()->GetDictionary().AddKey( PoDoFo::PdfName("DR"), resources );
}

+(void) WriteStringToStream:(const PoDoFo::PdfString & )rsString :(std::ostringstream &)  oss :(PoDoFo::PdfFont*) pFont
{
    PoDoFo::PdfEncoding* pEncoding = new PoDoFo::PdfIdentityEncoding( 0, 0xffff, true );
    PoDoFo::PdfRefCountedBuffer buffer = pEncoding->ConvertToEncoding( rsString, pFont );
    PoDoFo::pdf_long  lLen    = 0;
    char* pBuffer = NULL;

    std::auto_ptr<PoDoFo::PdfFilter> pFilter = PoDoFo::PdfFilterFactory::Create( PoDoFo::ePdfFilter_ASCIIHexDecode );
    pFilter->Encode( buffer.GetBuffer(), buffer.GetSize(), &pBuffer, &lLen );

    oss << "<";
    oss << std::string( pBuffer, lLen );
    oss << ">";
    free( pBuffer );
    delete pEncoding;
}

SO世界中的任何人都可以告诉我上面的代码有什么问题,以及如何添加正确的FreeText Annotation以便它在任何地方都能正确显示。

非常感谢。

2 个答案:

答案 0 :(得分:3)

有问题的注释如下所示:

19 0 obj
<<
  /Type/Annot
  /Contents(þÿ M Y   A N N O T A T I O N)
  /DA(BT\n/Ft18 12 Tf \n 1 0 0 rg \n<002D003900000021002E002E002F0034002100340029002F002E>Tj ET\n)
  /DR<</Fonts<</Ft18 18 0 R>>>>
  /M(D:20140616141406+05'00')
  /P 4 0 R
  /Rect[ 188.814117 748.970520 467.849731 795.476456]
  /Subtype/FreeText
  /T(þÿ A n n o t a t e P D F)
>>
endobj

三个观察结果:

  1. 它有默认外观但不包含 APpearance流
  2. 默认外观的内容无效。
  3. 默认资源位于错误的对象中。
  4. 第1项可能导致外观不在许多简单的查看器中呈现,这些查看器仅显示已完成的内容(页面内容,注释外观,......),但不会从默认外观创建外观。因此,您应该提供外观流。

    第2项和第3项可能会导致外观无法在更复杂的查看器中呈现,这些查看器尝试从默认外观默认资源创建外观,但期望 DA 是正确的, DR 正确定位。因此,您应该更正 DA 并移动 DR

    详细......

    1 - 默认外观但不是 APpearance流

    虽然根据规范ISO 32000-1,自由文字注释需要 DA ,而 AP 则不需要,更简单的PDF查看器可能没有内置代码从默认外观创建外观流。

    这并不奇怪:虽然如果您的PDF没有太多事情要做,将默认值应用于某些内容可能意味着计算文本的最佳大小以适应某些区域和类似任务。因此,简单,不完整的观众倾向于不实现这一点。

    2 - 默认外观内容无效

    您的 DA 字符串包含 BT ET 运算符。但是,如果你看一下ISO 32000-1的第12.7.3.3节变量文本,你会发现在外观创建过程中, DA 的内容被嵌入到< strong> BT .. ET 信封:

      

    外观流包括标记内容的以下部分,它表示绘制文本的流部分:

    /Tx BMC          % Begin marked content with tag Tx 
      q              % Save graphics state 
          … Any required graphics state changes, such as clipping … 
        BT           % Begin text object 
          … Default appearance string ( DA ) … 
          … Text-positioning and text-showing operators to show the variable text … 
        ET           % End text object 
      Q              % Restore graphics state 
    EMC              % End marked content 
    
      

    默认外观字符串( DA )包含建立图形状态参数(如文本大小和颜色)所需的任何图形状态或文本状态运算符,用于显示字段的变量文本。只有文本对象中允许的运算符才会出现在此字符串

    BT 文本对象中不允许 BT ET

    此外,您可以在 DA 中添加文字内容。如上所示,文本绘制操作在 DA 内容之后立即添加。因此,您最终会有重复文本的危险。

    3 - 默认资源错位

    您在注释词典中有默认资源。但是上面提到的ISO 32000-1部分12.7.3.3 变量文本表示:

      

    指定的 font 值应与默认资源字典的 Font 条目中的资源名称匹配(从 DR 条目引用交互式表格字典)。

    因此,您的 DR 将被忽略,并且会在其他地方出现。因此,您最好忽略您选择的字体

答案 1 :(得分:0)

我正在研究类似的事情。我尝试手动生成外观流,但发现它很难。实际上你上面发布的podofo示例代码有效,但是添加外观流的方式是错误的。你也不能使用错误的SetAppearanceStream。

podofo的PdfPainter可以绘制文字。它生成文本流。它看起来只适用于PdfPage,但实际上它也适用于XObject。这真是一个隐藏的功能!

我的代码示例:

PdfFont *pFont = ...;

// Add XObject
PdfXObject xObj(borderPdfRect, pPdfMemDocument);

PdfPainter painter;
painter.SetPage(&xObj);
painter.Save(); // Save graphics settings

// Draw text
painter.SetFont(pFont);
painter.GetFont()->SetFontSize(fontSize);
painter.SetColor(self.textColor.color.red, self.textColor.color.green, self.textColor.color.blue);
PdfString pdfStr(reinterpret_cast<const pdf_utf8*>([self.text UTF8String]));
painter.DrawMultiLineText(textPdfRect, pdfStr);
painter.Restore();
painter.FinishPage();

// Add xObj as appearance stream. Don't use SetAppearanceStream
PdfDictionary dict;
dict.AddKey("N", xObj.GetObject()->Reference());
pTextAnno->GetObject()->GetDictionary().AddKey("AP", dict);