如何使pdf签名可见(以及有效性图标)

时间:2013-03-28 17:16:50

标签: ruby pdf digital-signature visible

最初的问题是如何使用Ruby显示pdf签名。我现在可以使用下面的代码。

我现在唯一的问题是有效性图标。它显示在FoxIt Reader上,但不显示在Acrobat Reader上(我现在正在使用XI)

这就是我现在所拥有的

require 'openssl'

begin
  require 'origami'
rescue LoadError
  ORIGAMIDIR = "C:\RailsInstaller\Ruby1.9.3\lib\ruby\gems\1.9.1\gems\origami-1.2.4\lib"
  $: << ORIGAMIDIR
  require 'origami'
end
include Origami

def draw_signature(name, attr = {})
  load! if @instructions.nil?

  x, y = attr[:x], attr[:y]

  @instructions << PDF::Instruction.new('q')
  @instructions << PDF::Instruction.new('cm', 1, 0, 0, 1, x, y)
  @instructions << PDF::Instruction.new('Do', name)
  @instructions << PDF::Instruction.new('Q')
end

def SignPdf(inputF)

    @inputfile = String.new(inputF)
    outputF = @inputfile.insert(inputF.rindex("."),"_signed8")
    certFile = "cert.pem"
    rsakeyFile = "pk.pem"
    passphrase = "mypass"

    key4pem=File.read rsakeyFile
    key = OpenSSL::PKey::RSA.new key4pem, passphrase
    cert = OpenSSL::X509::Certificate.new(File.read certFile)

    pdf = PDF.read(inputF)
    page = pdf.get_page(1)


#signature image
imageobject = Origami::Graphics::ImageXObject.from_image_file('c:\rails_projects\RecibosOnline\app\assets\images\logo.jpg', 'jpg')
imageobject.Width = 200
imageobject.Height = 141

 #formobject = Origami::Graphics::FormXObject.new
 #formobject.write("Teste")

width = 300
height=141
x=201
y=186

signedby = "My Company"
location = "Portugal"#pdf.signature[pdf.signature.keys[5]]
contact = "mail@mail.com" #pdf.signature[pdf.signature.keys[6]]
reason = "Testing" #pdf.signature[pdf.signature.keys[7]]
date = Time.now

caption="Digitally Signed By: #{signedby}\nContact: #{contact}\nLocation: #{location}\nReason: #{reason}\nDate: #{date} "

n0 = Annotation::AppearanceStream.new
n0.Type=Origami::Name.new("XObject")
n0.BBox = [ 0, 0, 100, 100 ]
n0.Matrix = [ 1, 0, 0, 1, 0, 0 ]
n0.set_indirect(true)
n0.Resources = Resources.new
n0.Resources.ProcSet = [Origami::Name.new("PDF"),Origami::Name.new("Text"),Origami::Name.new("Text"),Origami::Name.new("ImageB"),Origami::Name.new("ImageC"),Origami::Name.new("ImageI")]
n0.draw_stream('% DSBlank')
#n0.write(Origami::Name.new("% DSBlank"))

n2 = Annotation::AppearanceStream.new
n2.Type = Origami::Name.new("XObject")
#n2.Subtype = Origami::Name.new("Form")
n2.Resources = Resources.new
n2.Resources.ProcSet = [Origami::Name.new("Text")]
n2.set_indirect(true)
n2.Matrix = [ 1, 0, 0, 1, 0, 0 ]
n2.BBox = [ 0, 0, width, height ]
n2.write(caption,:x => 40, :y => height-35, :size => 15)
#n2.draw_stream("q 0 0 198 48 re W n BT /Helv 7.645 Tf 0 g 0 40.135 Td (Digitally ) Tj 28.889 0 Td (signed ) Tj 24.652 0 Td (by ) Tj10.198 0 Td (James ) Tj 24.641 0 Td (Pravetz ) Tj-88.38 -9.174 Td (DN: ) Tj 15.29 0 Td (cn=James ) Tj37.18 0 Td (Pravetz, ) Tj 30.165 0 Td (c=CA, ) Tj 23.156 0 Td0 -9.174 Td (Reason: ) Tj 30.599 0 Td (I ) Tj 4.248 0 Td(have ) Tj 18.702 0 Td (reviewed ) Tj 32.719 0 Td(this ) Tj 14.02 0 Td (document ) Tj -100.288 -9.174 Td(Date: ) Tj 20.397 0 Td (2006.06.08 ) Tj 40.386 0 Td(13:39:35 ) Tj 31.883 0 Td (-07'00') Tj ET Q")

#Sets the root dictionary element

frm = Annotation::AppearanceStream.new
frm.set_indirect(true)
#frm.Type = Origami::Name.new("XObject")
#frm.Subtype = Origami::Name.new("Form")
frm.Resources = Resources.new
frm.Resources.ProcSet = [Origami::Name.new("PDF")]
frm.Resources.add_xobject(Origami::Name.new("n0"), n0)
frm.Resources.add_xobject(Origami::Name.new("n2"), n2)
frm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
frm.BBox = [ 0, 0, width, height ]
frm.draw_stream('q 1 0 0 1 0 0 cm /n0 Do Q')
frm.draw_stream('q 1 0 0 1 0 0 cm /n2 Do Q')




xo17 = Annotation::Widget::Signature.new
#xo17 = Annotation::Widget::Signature.new
xo17.Rect = Rectangle[ llx: x, lly: y, urx: x+width, ury: y+height ]
#xo17.Resources = Resources.new
#xo17.Resources.add_xobject(Origami::Name.new("FRM"), frm)
xo17.F = Annotation::Flags::PRINT #sets the print mode on
xo17.H = Annotation::Widget::Highlight::INVERT

streamN = Annotation::AppearanceStream.new #.setFilter(:FlateDecode)
streamN.set_indirect(true)
streamN.BBox = [ 0, 0, width, height ]
streamN.Resources = Resources.new
streamN.Resources.ProcSet = [Origami::Name.new("PDF")]
streamN.Resources.add_xobject(Origami::Name.new("FRM"), frm)
streamN.Subtype = nil
#cs = ContentStream.new('q 1 0 0 1 0 0 cm /FRM Do Q',streamN)
streamN.draw_stream('q 1 0 0 1 0 0 cm /FRM Do Q')
#streamN.draw_image(Origami::Name.new("FRM"),{x:x,y:y})
#streamN.write(caption,:x => 40, :y => height-35, :size => 15)
xo17.set_normal_appearance(streamN)

# page.add_xobject(Origami::Name.new("FRM"),frm)
# page.add_xobject(Origami::Name.new("n0"),n0)
# page.add_xobject(Origami::Name.new("n2"),n2)

page.add_annot(xo17)    

# Sign the PDF with the specified keys
pdf.sign(cert, key, 
  :method => 'adbe.pkcs7.sha1',
  :annotation => xo17, 
  :location => location, 
  :contact => contact, 
  :reason => reason
)

# Save the resulting file
pdf.save(outputF)


end
SignPdf("Sample.pdf")

编辑2

继mkl介入之后,我进一步深入了解源代码......

我注意到了xobject.rb文件,其中包含以下用于绘制图像的代码

def draw_image(name, attr = {})
      load! if @instructions.nil?

      x, y = attr[:x], attr[:y]

      @instructions << PDF::Instruction.new('q')
      @instructions << PDF::Instruction.new('cm', 300, 0, 0, 300, x, y)
      @instructions << PDF::Instruction.new('Do', name)
      @instructions << PDF::Instruction.new('Q')
end

然后我在同一个文件

上创建了下面的方法
def draw_stream(name)
      load! if @instructions.nil?

      @instructions << PDF::Instruction.new(name)
end

允许我从输入中控制流。

我也去了ISO32000:2008,并且在我的代码中重复了一遍,但是无处可去。

这是我当前生成的pdf file,以及与签名相关的部分:

16 0 obj
<<
    /Rect [ 201 186 501 327 ]
    /F 4
    /H /I
    /AP <<
        /N 18 0 R
    >>
    /P 1 0 R
    /V 17 0 R
    /T (undef28504)
    /Subtype /Widget
    /FT /Sig
    /DA (/F1 10 Tf 0 g)
>>
endobj
17 0 obj
<<
    /Type /Sig
    /SubFilter /adbe.pkcs7.sha1
    /Reason <54657374696E67>
    /Prop_Build <<
        /Filter <<
            /Name /Adobe.PPKMS
            /R 131101
            /Date (2013-04-03 14:18:41 +0100)
        >>
        /SigQ <<
            /Preview false
            /R 131101
        >>
        /PubSec <<
            /NonEFontNoWarn false
            /Date (2013-04-03 14:18:41 +0100)
            /R 131101
        >>
        /App <<
            /TrustedMode false
            /OS [ /Win ]
            /R 458752
            /Name /Exchange-Pro
        >>
    >>
    /M (D:20130403131841Z00'00)
    /Location <506F72747567616C>
    /Filter /Adobe.PPKMS
    /Contents <308207AE06092A864886F70D010702A082079F3082079B020101310B300906052B0E03021A0500302306092A864886F70D010701A0160414BBA1B7480DDB1B75B136B4DFA2A4C120EC7972DFA0820597308205933082047BA0030201020204426F938C300D06092A864886F70D0101050500303E310B300906035504061302707431153013060355040A130C4D554C5449434552542D4341311830160603550403130F4D554C5449434552542D4341203032301E170D3133303332303137303134375A170D3134303332303136343733365A3081A5310B300906035504061302505431153013060355040A130C4D554C5449434552542D434131163014060355040B130D4345525449504F52202D20524131123010060355040B1309436F72706F726174653120301E060355040B13174553435249544120494E54454C4947454E5445204C444131183016060355040B130F576562204170706C69636174696F6E311730150603550403130E52454349424F53204F4E4C494E4530819F300D06092A864886F70D010101050003818D0030818902818100ACCEA4069031B578896C450D77C83C13DD75C668BF2762368DB0A0B1591B1800E58C1ACDBB25DA15501AF931F9F6BAE0F8F64C481686E959DE005AEC82B33D0D32665037D18BF36BF77C4D56620C42FBBA92D3FF397F9D8377FEABBA93478FCEF009216483D346ACCC76CA10C9B8653B80B8F6EB49BD93897A43DF062D0D9ED30203010001A38202B3308202AF300B0603551D0F0404030203F8303806082B06010505070101042C302A302806082B06010505073001861C687474703A2F2F6F6373702E6D756C7469636572742E636F6D2F63613081E00603551D200481D83081D5304D06092B06010401B03C0A023040303E06082B060105050702011632687474703A2F2F7777772E6D756C7469636572742E636F6D2F6370732F6D756C7469636572742D63612D6370732E68746D6C308183060B2B06010401B03C0A0288063074307206082B0601050507020230661E640068007400740070003A002F002F007700770077002E006D0075006C007400690063006500720074002E0063006F006D002F00630070002F006D0075006C007400690063006500720074002D00630061002D0031003000330030002E00680074006D006C301106096086480186F84201010404030204B030200603551D11041930178115696E666F4072656369626F736F6E6C696E652E7074308201010603551D1F0481F93081F630819AA08197A08194862F687474703A2F2F7777772E6D756C7469636572742E636F6D2F63612F6D756C7469636572742D63612D30322E63726C86616C6461703A2F2F6C6461702E6D756C7469636572742E636F6D2F636E3D4D554C5449434552542D434125323030322C6F3D4D554C5449434552542D43412C633D50543F63657274696669636174655265766F636174696F6E4C6973743F626173653057A055A053A451304F310B300906035504061302707431153013060355040A130C4D554C5449434552542D4341311830160603550403130F4D554C5449434552542D4341203032310F300D0603550403130643524C323935301F0603551D230418301680141DC3B988A518BE60A72CA663CA662AFC0C27C1BD301D0603551D0E0416041406D81F7236619EEB17369C299E2D74FFD038301930090603551D1304023000300D06092A864886F70D0101050500038201010041511FCD5C7561981B0D54326B57F7B87C435AACB7A2962808768313892AB1407237E957577B875414DE810A41323F9E0A768E9AC45C0F66AE093C32C11453C6463F856FEF62E27821134DD09F75208000040E89A814E6309623C5D04163C03CFDE33153908AC3DFCA5B1C66C3DCB89644A3030FE794D50BD255D396535A7AF267C3A538147B9371D05F23D8CA481AEBC7D7A7D97C2E7FB5AB49C4E4554E4800648BC76B1AF5612A1D9361D172084E70690928A911FC209833C506219CF186B6507B599E4C0F42F3BF23C2B8F0A078D01D9BF5FD4746D97253EE4FE8F472489B3DC26F7072C6587218825BB3061074B9C240F8928D36FEFC0F708897752CD946318201C7308201C30201013046303E310B300906035504061302707431153013060355040A130C4D554C5449434552542D4341311830160603550403130F4D554C5449434552542D43412030320204426F938C300906052B0E03021A0500A081D8301806092A864886F70D010903310B06092A864886F70D010701301C06092A864886F70D010905310F170D3133303430333133313834315A302306092A864886F70D010904311604142948A25663C43F9571225B0F12B9097DFE7149DB307906092A864886F70D01090F316C306A300B060960864801650304012A300B0609608648016503040116300B0609608648016503040102300A06082A864886F70D0307300E06082A864886F70D030202020080300D06082A864886F70D0302020140300706052B0E030207300D06082A864886F70D0302020128300D06092A864886F70D010101050004818021F8A3A104E8B6EC1D9026CA9A822C4357EEBAABD5D87422BE6199802F45E83E9D2417711CE26DCB28A8246ACE119B128956394A8FA641346E5F3CBBDD84ED7E36B2F947DD5D92EC1C2C078A1630A729773F17027FC3B64C18844C26F7FA18A7B4FC9DE334741235E178280ED25CD8840A83A91FCE240646EB84117E348BF82E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000>
    /ContactInfo <6D61696C406D61696C2E636F6D>
    /ByteRange [ 0 12880 17078 2366 ]
>>
endobj
18 0 obj
<<
    /BBox [ 0 0 300 141 ]
    /Resources <<
        /ProcSet [ /PDF ]
        /XObject <<
            /FRM 19 0 R
        >>
        /Font <<
            /F1 <<
                /Type /Font
                /Subtype /Type1
                /BaseFont /Helvetica
                /Name /F1
            >>
        >>
    >>
    /Length 27
    /Subtype /Form
>>stream
q 1 0 0 1 0 0 cm /FRM Do Q

endstream
endobj
19 0 obj
<<
    /Resources <<
        /ProcSet [ /PDF ]
        /XObject <<
            /n0 20 0 R
            /n2 21 0 R
        >>
        /Font <<
            /F1 <<
                /Type /Font
                /Subtype /Type1
                /BaseFont /Helvetica
                /Name /F1
            >>
        >>
    >>
    /Matrix [ 1 0 0 1 0 0 ]
    /BBox [ 0 0 300 141 ]
    /Length 52
    /Subtype /Form
>>stream
q 1 0 0 1 0 0 cm /n0 Do Q
q 1 0 0 1 0 0 cm /n2 Do Q

endstream
endobj
20 0 obj
<<
    /Type /XObject
    /BBox [ 0 0 100 100 ]
    /Matrix [ 1 0 0 1 0 0 ]
    /Resources <<
        /ProcSet [ /PDF /Text /Text /ImageB /ImageC /ImageI ]
        /Font <<
            /F1 <<
                /Type /Font
                /Subtype /Type1
                /BaseFont /Helvetica
                /Name /F1
            >>
        >>
    >>
    /Length 10
    /Subtype /Form
>>stream
% DSBlank

endstream
endobj
21 0 obj
<<
    /Type /XObject
    /Resources <<
        /ProcSet [ /Text ]
        /Font <<
            /F1 <<
                /Type /Font
                /Subtype /Type1
                /BaseFont /Helvetica
                /Name /F1
            >>
        >>
    >>
    /Matrix [ 1 0 0 1 0 0 ]
    /BBox [ 0 0 300 141 ]
    /Length 176
    /Subtype /Form
>>stream
BT
/F1 15 Tf
40 106 Td
20 TL
(Digitally Signed By: My Company) Tj
(Contact: mail@mail.com) '
(Location: Portugal) '
(Reason: Testing) '
(Date: 2013-04-03 14:18:41 +0100 ) '
ET

endstream
endobj
22 0 obj
<<
    /Fields [ 16 0 R ]
    /SigFlags 3
>>
endobj

我错过了什么?

1 个答案:

答案 0 :(得分:1)

有时候,一个男人只需要知道如何真正理解他一遍又一遍地读到的东西......

ppkappearances文件全部都有。我甚至没有按照我的预期去遵守他们推荐的词典结构。 我只需要添加n1和n3层,流内容甚至可以只是“%DS Blank”。您找到的问号字符串流是我从iText解决方案PdfSignatureAppearance.cs文件中获取的那个。如果我使用那个福昕阅读器提出问号(即使签名有效)。对于Adobe,无论我使用它还是DSBlank

require 'openssl'

begin
  require 'origami'
rescue LoadError
  ORIGAMIDIR = "C:\RailsInstaller\Ruby1.9.3\lib\ruby\gems\1.9.1\gems\origami-1.2.4\lib"
  $: << ORIGAMIDIR
  require 'origami'
end
include Origami

def draw_signature(name, attr = {})
  load! if @instructions.nil?

  x, y = attr[:x], attr[:y]

  @instructions << PDF::Instruction.new('q')
  @instructions << PDF::Instruction.new('cm', 1, 0, 0, 1, x, y)
  @instructions << PDF::Instruction.new('Do', name)
  @instructions << PDF::Instruction.new('Q')
end

def SignPdf(inputF)

    @inputfile = String.new(inputF)
    outputF = @inputfile.insert(inputF.rindex("."),"_signed8")
    certFile = "cert.pem"
    rsakeyFile = "pk.pem"
    passphrase = "mypass"

    key4pem=File.read rsakeyFile
    key = OpenSSL::PKey::RSA.new key4pem, passphrase
    cert = OpenSSL::X509::Certificate.new(File.read certFile)

    pdf = PDF.read(inputF)
    page = pdf.get_page(1)


#signature image
imageobject = Origami::Graphics::ImageXObject.from_image_file('c:\rails_projects\RecibosOnline\app\assets\images\logo.jpg', 'jpg')
imageobject.Width = 200
imageobject.Height = 141

 #formobject = Origami::Graphics::FormXObject.new
 #formobject.write("Teste")

width = 300
height=141
x=201
y=186

signedby = "My Company"
location = "Portugal"#pdf.signature[pdf.signature.keys[5]]
contact = "mail@mail.com" #pdf.signature[pdf.signature.keys[6]]
reason = "Testing" #pdf.signature[pdf.signature.keys[7]]
date = Time.now

caption="Digitally Signed By: #{signedby}\nContact: #{contact}\nLocation: #{location}\nReason: #{reason}\nDate: #{date} "

    #DSBlankstream= "% DSBLank"

    questionMark = "% DSUnknown\n" +
            "q\n" +
            "1 G\n" +
            "1 g\n" +
            "0.1 0 0 0.1 9 0 cm\n" +
            "0 J 0 j 4 M []0 d\n" +
            "1 i \n" +
            "0 g\n" +
            "313 292 m\n" +
            "313 404 325 453 432 529 c\n" +
            "478 561 504 597 504 645 c\n" +
            "504 736 440 760 391 760 c\n" +
            "286 760 271 681 265 626 c\n" +
            "265 625 l\n" +
            "100 625 l\n" +
            "100 828 253 898 381 898 c\n" +
            "451 898 679 878 679 650 c\n" +
            "679 555 628 499 538 435 c\n" +
            "488 399 467 376 467 292 c\n" +
            "313 292 l\n" +
            "h\n" +
            "308 214 170 -164 re\n" +
            "f\n" +
            "0.44 G\n" +
            "1.2 w\n" +
            "1 1 0.4 rg\n" +
            "287 318 m\n" +
            "287 430 299 479 406 555 c\n" +
            "451 587 478 623 478 671 c\n" +
            "478 762 414 786 365 786 c\n" +
            "260 786 245 707 239 652 c\n" +
            "239 651 l\n" +
            "74 651 l\n" +
            "74 854 227 924 355 924 c\n" +
            "425 924 653 904 653 676 c\n" +
            "653 581 602 525 512 461 c\n" +
            "462 425 441 402 441 318 c\n" +
            "287 318 l\n" +
            "h\n" +
            "282 240 170 -164 re\n" +
            "B\n" +
            "Q\n";

    n0 = Annotation::AppearanceStream.new
    n0.Type=Origami::Name.new("XObject")
    n0.BBox = [ 0, 0, 100, 100 ]
    n0.Matrix = [ 1, 0, 0, 1, 0, 0 ]
    n0.set_indirect(true)
    n0.Resources = Resources.new
    n0.Resources.ProcSet = [Origami::Name.new("PDF"),Origami::Name.new("Text"),Origami::Name.new("Text"),Origami::Name.new("ImageB"),Origami::Name.new("ImageC"),Origami::Name.new("ImageI")]
    n0.draw_stream('% DSBlank')
    #n0.write(Origami::Name.new("% DSBlank"))


    n1 = Annotation::AppearanceStream.new
    n1.Type=Origami::Name.new("XObject")
    n1.BBox = [ 0, 0, 100, 100 ]
    n1.Matrix = [ 1, 0, 0, 1, 0, 0 ]
    n1.set_indirect(true)
    n1.Resources = Resources.new
    n1.Resources.ProcSet = [Origami::Name.new("PDF"),Origami::Name.new("Text"),Origami::Name.new("Text"),Origami::Name.new("ImageB"),Origami::Name.new("ImageC"),Origami::Name.new("ImageI")]
    n1.draw_stream('% DSBlank')

    n3 = Annotation::AppearanceStream.new
    n3.Type=Origami::Name.new("XObject")
    n3.BBox = [ 0, 0, 100, 100 ]
    n3.Matrix = [ 1, 0, 0, 1, 0, 0 ]
    n3.set_indirect(true)
    n3.Resources = Resources.new
    n3.Resources.ProcSet = [Origami::Name.new("PDF"),Origami::Name.new("Text"),Origami::Name.new("Text"),Origami::Name.new("ImageB"),Origami::Name.new("ImageC"),Origami::Name.new("ImageI")]
    n3.draw_stream('% DSBlank')

    n2 = Annotation::AppearanceStream.new
    n2.Type = Origami::Name.new("XObject")
    #n2.Subtype = Origami::Name.new("Form")
    n2.Resources = Resources.new
    n2.Resources.ProcSet = [Origami::Name.new("Text")]
    n2.set_indirect(true)
    n2.Matrix = [ 1, 0, 0, 1, 0, 0 ]
    n2.BBox = [ 0, 0, width, height ]
    n2.write(caption,:x => 40, :y => height-35, :size => 15)
    #n2.draw_stream("q 0 0 198 48 re W n BT /Helv 7.645 Tf 0 g 0 40.135 Td (Digitally ) Tj 28.889 0 Td (signed ) Tj 24.652 0 Td (by ) Tj10.198 0 Td (James ) Tj 24.641 0 Td (Pravetz ) Tj-88.38 -9.174 Td (DN: ) Tj 15.29 0 Td (cn=James ) Tj37.18 0 Td (Pravetz, ) Tj 30.165 0 Td (c=CA, ) Tj 23.156 0 Td0 -9.174 Td (Reason: ) Tj 30.599 0 Td (I ) Tj 4.248 0 Td(have ) Tj 18.702 0 Td (reviewed ) Tj 32.719 0 Td(this ) Tj 14.02 0 Td (document ) Tj -100.288 -9.174 Td(Date: ) Tj 20.397 0 Td (2006.06.08 ) Tj 40.386 0 Td(13:39:35 ) Tj 31.883 0 Td (-07'00') Tj ET Q")

##Sets the root dictionary element

    frm = Annotation::AppearanceStream.new
    frm.set_indirect(true)
    #frm.Type = Origami::Name.new("XObject")
    #frm.Subtype = Origami::Name.new("Form")
    frm.Resources = Resources.new
    frm.Resources.ProcSet = [Origami::Name.new("PDF")]
    frm.Resources.add_xobject(Origami::Name.new("n0"), n0)
    frm.Resources.add_xobject(Origami::Name.new("n1"), n1)
    frm.Resources.add_xobject(Origami::Name.new("n2"), n2)
    frm.Resources.add_xobject(Origami::Name.new("n3"), n3)
    frm.Matrix = [ 1, 0, 0, 1, 0, 0 ]
    frm.BBox = [ 0, 0, width, height ]
    frm.draw_stream('q 1 0 0 1 0 0 cm /n0 Do Q')
    frm.draw_stream('q 1 0 0 1 0 0 cm /n1 Do Q')
    frm.draw_stream('q 1 0 0 1 0 0 cm /n2 Do Q')
    frm.draw_stream('q 1 0 0 1 0 0 cm /n3 Do Q')


    xo17 = Annotation::Widget::Signature.new
    #xo17 = Annotation::Widget::Signature.new
    xo17.Rect = Rectangle[ llx: x, lly: y, urx: x+width, ury: y+height ]
    #xo17.Resources = Resources.new
    #xo17.Resources.add_xobject(Origami::Name.new("FRM"), frm)
    xo17.F = Annotation::Flags::PRINT #sets the print mode on
    xo17.H = Annotation::Widget::Highlight::INVERT

    streamN = Annotation::AppearanceStream.new #.setFilter(:FlateDecode)
    streamN.set_indirect(true)
    streamN.BBox = [ 0, 0, width, height ]
    streamN.Resources = Resources.new
    streamN.Resources.ProcSet = [Origami::Name.new("PDF")]
    streamN.Resources.add_xobject(Origami::Name.new("FRM"), frm)
    streamN.Subtype = nil
    #cs = ContentStream.new('q 1 0 0 1 0 0 cm /FRM Do Q',streamN)
    streamN.draw_stream('q 1 0 0 1 0 0 cm /FRM Do Q')
    #streamN.draw_image(Origami::Name.new("FRM"),{x:x,y:y})
    #streamN.write(caption,:x => 40, :y => height-35, :size => 15)
    xo17.set_normal_appearance(streamN)

    # page.add_xobject(Origami::Name.new("FRM"),frm)
    # page.add_xobject(Origami::Name.new("n0"),n0)
    # page.add_xobject(Origami::Name.new("n2"),n2)

    page.add_annot(xo17)    

    # Sign the PDF with the specified keys
    pdf.sign(cert, key, 
      :method => 'adbe.pkcs7.sha1',
      :annotation => xo17, 
      :location => location, 
      :contact => contact, 
      :reason => reason
    )

    # Save the resulting file
    pdf.save(outputF)


end

SignPdf("Sample.pdf")

真是个斗争!

我想说:

最终,获胜者将是你站在战场中间的人。可悲地看着他们进入战场,并想知道为什么它必须像那样。只有他们知道如何阅读......那就不会发生任何事情。但与此同时,如果它不会发生,他们就不会知道他们不知道如何以正确的焦点阅读。