最初的问题是关于使用 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 文件,以及与签名相关的部分:
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
我错过了什么?