2

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

根据 Adob​​e 的一些指导方针,我发现它需要Appearance Key出现FreeText annotation。我尝试在文本编辑器中分析原始 pdf 文件,以查看具有正确注释的 PDF 中的差异,以及 podofo 创建的 PDF 注释。我发现有一个带有注释编码形式的对象的AP N键,该注释从 podofo 样本中丢失。stream

然后在搜索后我找到了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以便它在任何地方都正确显示。

非常感谢。

4

2 回答 2

5

有问题的注释如下所示:

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. 它有一个Default Appearance但没有Appearance 流
  2. Default Appearance的内容无效。
  3. 默认资源位于错误的对象中。

第 1 项可能会导致外观无法在许多仅显示最终内容(页面内容、注释外观等)但不从Default Appearance创建外观的简单查看器中呈现。因此,您还应该提供外观流。

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

详细地...

1 -默认外观但不是外观流

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

这并不完全令人惊讶:虽然您的 PDF 没有太多工作要做,但将默认值应用于某些内容可能意味着计算文本的最佳大小以适合某些区域和类似任务。因此,简单的、不完整的查看器往往不会实现这一点。

2 -默认外观内容无效

您的DA字符串包含BTET运算符。但是,如果您查看ISO 32000-1的第 12.7.3.3 节可变文本,您会看到在外观创建期间DA的内容被嵌入到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 ) 包含建立图形状态参数所需的任何图形状态或文本状态运算符,例如文本大小和颜色,以显示字段的可变文本。此字符串中只能出现文本对象中允许的运算符

但是BTET不允许在另一个BT .. ET文本对象中!

此外,您在DA中添加文本内容。正如您在上面看到的,文本绘制操作是在您的DA内容之后添加的。因此,您最终会面临重复文本的危险。

3 -默认资源错位

您在注释字典中有默认资源。但是上面提到的ISO 32000-1的 12.7.3.3可变文本部分指出:

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

因此,您的DR将被忽略并在其他地方出现。所以你选择的字体充其量可能会被忽略

于 2014-06-16T13:54:18.027 回答
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);
于 2016-03-16T03:29:06.693 回答