pdf_ravager 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1,14 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.2"
4
- - "1.9.3"
3
+ - jruby-18mode
5
4
  - jruby-19mode
6
- - rbx-19mode
7
- matrix:
8
- allow_failures:
9
- - rvm: jruby-19mode
5
+ - jruby-head
10
6
  before_install:
11
7
  - sudo add-apt-repository "deb http://archive.canonical.com/ $(lsb_release -sc) partner" -y
12
8
  - sudo apt-get update -y
13
9
  - sudo apt-get install acroread ghostscript -y
14
-
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2012 Abe Voelker
1
+ Copyright 2012-2013 Abe Voelker
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -11,33 +11,47 @@ XFA documents.
11
11
  ## Synopsis
12
12
 
13
13
  ```ruby
14
- require 'pdf_ravager'
14
+ require 'pdf_ravager/kernel'
15
15
 
16
- data = {:name => 'Bob', :gender => 'm', :relation => 'Uncle' }
16
+ data = {name: 'Bob', gender: 'm', relation: 'Uncle' }
17
17
 
18
- info = pdf do
19
- text 'name', data[:name]
20
- text 'name_stylized', "<b>#{data[:name]}</b>", :rich => true
21
- radio_group 'sex' do
22
- fill 'male' if data[:gender] == 'm'
23
- fill 'female' if data[:gender] == 'f'
18
+ template = pdf do |p|
19
+ p.text 'name', data[:name]
20
+ p.rich_text 'name_stylized', "<b>#{data[:name]}</b>"
21
+ p.radio_group 'sex' do |rg|
22
+ rg.fill 'male' if data[:gender] == 'm'
23
+ rg.fill 'female' if data[:gender] == 'f'
24
24
  end
25
- check 'related' if data[:relation]
26
- checkbox_group 'relation' do
25
+ p.check 'related' if data[:relation]
26
+ p.checkbox_group 'relation' do |cg|
27
27
  case data[:relation]
28
28
  when 'Mom', 'Dad'
29
- check 'parent'
29
+ cg.check 'parent'
30
30
  when 'Brother', 'Sister'
31
- check 'sibling'
31
+ cg.check 'sibling'
32
32
  else
33
- check 'other'
33
+ cg.check 'other'
34
34
  end
35
35
  end
36
36
  end
37
37
 
38
- info.ravage '/tmp/info.pdf', :out_file => '/tmp/info_filled.pdf'
38
+ template.ravage '/tmp/info.pdf', out_file: '/tmp/info_filled.pdf'
39
39
  # if you'd like the populated form to be read-only:
40
- info.ravage '/tmp/info.pdf', :out_file => '/tmp/info_filled.pdf', :read_only => true
40
+ template.ravage '/tmp/info.pdf', out_file: '/tmp/info_filled.pdf', read_only: true
41
+ ```
42
+
43
+ If you don't want the global `pdf` method, the default `require 'pdf_ravager'`
44
+ actually doesn't add it. You just need to be more wordy in this case:
45
+
46
+ ```ruby
47
+ require 'pdf_ravager'
48
+
49
+ data = {name: 'Bob', gender: 'm', relation: 'Uncle' }
50
+
51
+ template = PDFRavager::Template.new do |p|
52
+ p.text 'name', data[:name]
53
+ # ...
54
+ end
41
55
  ```
42
56
 
43
57
  ## Usage
@@ -66,20 +80,24 @@ Because there is no such thing as a "checkbox group," the
66
80
  example,
67
81
 
68
82
  ```ruby
69
- checkbox_group 'relation' do
70
- check 'parent'
83
+ pdf do |p|
84
+ p.checkbox_group 'relation' do |cg|
85
+ cg.check 'parent'
86
+ end
71
87
  end
72
88
  ```
73
89
 
74
90
  is equivalent to
75
91
 
76
92
  ```ruby
77
- check 'relation.parent'
93
+ pdf do |p|
94
+ p.check 'relation.parent'
95
+ end
78
96
  ```
79
97
 
80
98
  ## Copyright
81
99
 
82
- Copyright (c) 2012 Abe Voelker. Released under the terms of the
100
+ Copyright (c) 2012-2013 Abe Voelker. Released under the terms of the
83
101
  MIT license. See LICENSE for details.
84
102
 
85
103
  The [version of iText][2] vendored is licensed under the LGPL.
data/Rakefile CHANGED
@@ -20,6 +20,6 @@ else
20
20
  task :spec => ['spec:unit']
21
21
  end
22
22
 
23
- task :default => :spec
23
+ task :default => 'spec:unit'
24
24
 
25
25
  Bundler::GemHelper.install_tasks
data/lib/pdf_ravager.rb CHANGED
@@ -1,3 +1,2 @@
1
1
  require 'pdf_ravager/version'
2
- require 'pdf_ravager/pdf'
3
- require 'pdf_ravager/ravager' if RUBY_PLATFORM =~ /java/
2
+ require 'pdf_ravager/template'
@@ -0,0 +1,36 @@
1
+ require 'java'
2
+ require File.dirname(__FILE__) + '/../../../vendor/iText-4.2.0'
3
+
4
+ module PDFRavager
5
+ module FieldTypes
6
+ module AcroForm
7
+
8
+ module SOM
9
+ def self.short_name(str)
10
+ com.lowagie.text.pdf.XfaForm::Xml2Som.getShortName(self.escape(str))
11
+ end
12
+
13
+ def self.escape(str)
14
+ com.lowagie.text.pdf.XfaForm::Xml2Som.escapeSom(str) # just does: str.gsub(/\./) { '\\.' }
15
+ end
16
+ end
17
+
18
+ def acro_form_name
19
+ @name
20
+ end
21
+
22
+ def acro_form_value
23
+ @value.to_s
24
+ end
25
+
26
+ def set_acro_form_value(acro_fields)
27
+ begin
28
+ acro_fields.setField(SOM.short_name(acro_form_name), acro_form_value)
29
+ rescue java.lang.NullPointerException
30
+ false
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,65 @@
1
+ require 'nokogiri'
2
+
3
+ module PDFRavager
4
+ module FieldTypes
5
+ module XFA
6
+
7
+ def xfa_node_type
8
+ 'text'
9
+ end
10
+
11
+ def xfa_name
12
+ @name
13
+ end
14
+
15
+ def xfa_value
16
+ @value.to_s
17
+ end
18
+
19
+ def set_xfa_value(xfa)
20
+ # the double-load is to work around a Nokogiri bug I found:
21
+ # https://github.com/sparklemotion/nokogiri/issues/781
22
+ doc = Nokogiri::XML(Nokogiri::XML::Document.wrap(xfa.getDomDocument).to_xml)
23
+ # first, assume the user-provided field name is an xpath and use it directly:
24
+ strict_match =
25
+ begin
26
+ doc.xpath(xfa_name)
27
+ rescue Nokogiri::XML::XPath::SyntaxError
28
+ []
29
+ end
30
+ # otherwise, we'll loosely match the field name anywhere in the document:
31
+ loose_match = doc.xpath("//*[local-name()='field'][@name='#{xfa_name}']")
32
+ matched_nodes = strict_match.any? ? strict_match : loose_match
33
+ matched_nodes.each do |node|
34
+ value_node = node.at_xpath("*[local-name()='value']")
35
+ if value_node
36
+ text_node = value_node.at_xpath("*[local-name()='#{xfa_node_type}']")
37
+ if text_node
38
+ # Complete node structure already exists - just set the value
39
+ text_node.content = xfa_value
40
+ else
41
+ # <value> node exists, but without child <text> node
42
+ Nokogiri::XML::Builder.with(value_node) do |xml|
43
+ xml.text_ {
44
+ xml.send("#{xfa_node_type}_", xfa_value)
45
+ }
46
+ end
47
+ end
48
+ else
49
+ # No <value> node exists - create whole structure
50
+ Nokogiri::XML::Builder.with(node) do |xml|
51
+ xml.value_ {
52
+ xml.send("#{xfa_node_type}_") {
53
+ xml.text xfa_value
54
+ }
55
+ }
56
+ end
57
+ end
58
+ end
59
+ xfa.setDomDocument(doc.to_java)
60
+ xfa.setChanged(true)
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,36 @@
1
+ require 'pdf_ravager/field_types/acro_form'
2
+ require 'pdf_ravager/field_types/xfa'
3
+
4
+ module PDFRavager
5
+ module Fields
6
+ class Checkbox
7
+ include FieldTypes::AcroForm
8
+ include FieldTypes::XFA
9
+
10
+ attr_reader :name, :value
11
+
12
+ def initialize(name, value, opts={})
13
+ @name, @value = name, value
14
+ @true_value = opts[:true_value] ? opts[:true_value] : '1'
15
+ @false_value = opts[:false_value] ? opts[:false_value] : '0'
16
+ end
17
+
18
+ def ==(other)
19
+ self.name == other.name && self.value == other.value
20
+ end
21
+
22
+ def xfa_node_type
23
+ 'integer'
24
+ end
25
+
26
+ def xfa_value
27
+ @value ? @true_value : @false_value
28
+ end
29
+
30
+ def acro_form_value
31
+ @value ? @true_value : @false_value
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ require 'pdf_ravager/field_types/acro_form'
2
+ require 'pdf_ravager/field_types/xfa'
3
+
4
+ module PDFRavager
5
+ module Fields
6
+ class Radio
7
+ include FieldTypes::AcroForm
8
+ include FieldTypes::XFA
9
+
10
+ attr_reader :group_name, :name
11
+
12
+ def initialize(group_name, name)
13
+ @group_name, @name = group_name, name
14
+ end
15
+
16
+ def ==(other)
17
+ self.group_name == other.group_name && self.name == other.name
18
+ end
19
+
20
+ def acro_form_name
21
+ @group_name
22
+ end
23
+
24
+ def xfa_name
25
+ @group_name
26
+ end
27
+
28
+ def acro_form_value
29
+ @name
30
+ end
31
+
32
+ def xfa_value
33
+ @name
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,62 @@
1
+ require 'pdf_ravager/field_types/xfa'
2
+
3
+ module PDFRavager
4
+ module Fields
5
+ class RichText
6
+ include FieldTypes::XFA
7
+
8
+ attr_reader :name, :value
9
+
10
+ def initialize(name, value)
11
+ @name, @value = name, value
12
+ end
13
+
14
+ def ==(other)
15
+ self.name == other.name && self.value == other.value
16
+ end
17
+
18
+ def set_xfa_value(xfa)
19
+ # the double-load is to work around a Nokogiri bug I found:
20
+ # https://github.com/sparklemotion/nokogiri/issues/781
21
+ doc = Nokogiri::XML(Nokogiri::XML::Document.wrap(xfa.getDomDocument).to_xml)
22
+ # first, assume the user-provided field name is an xpath and use it directly:
23
+ strict_match =
24
+ begin
25
+ doc.xpath(xfa_name)
26
+ rescue Nokogiri::XML::XPath::SyntaxError
27
+ []
28
+ end
29
+ # otherwise, we'll loosely match the field name anywhere in the document:
30
+ loose_match = doc.xpath("//*[local-name()='field'][@name='#{xfa_name}']")
31
+ matched_nodes = strict_match.any? ? strict_match : loose_match
32
+ matched_nodes.each do |node|
33
+ value_node = node.at_xpath("*[local-name()='value']")
34
+ if value_node
35
+ # <value> node exists - just create <exData>
36
+ Nokogiri::XML::Builder.with(value_node) do |xml|
37
+ xml.exData('contentType' => 'text/html') do
38
+ xml.body_('xmlns' => "http://www.w3.org/1999/xhtml", 'xmlns:xfa' => "http://www.xfa.org/schema/xfa-data/1.0/") do
39
+ xml << xfa_value # Note: this value is not sanitized/escaped!
40
+ end
41
+ end
42
+ end
43
+ else
44
+ # No <value> node exists - create whole structure
45
+ Nokogiri::XML::Builder.with(node) do |xml|
46
+ xml.value_ do
47
+ xml.exData('contentType' => 'text/html') do
48
+ xml.body_('xmlns' => "http://www.w3.org/1999/xhtml", 'xmlns:xfa' => "http://www.xfa.org/schema/xfa-data/1.0/") do
49
+ xml << xfa_value # Note: this value is not sanitized/escaped!
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ xfa.setDomDocument(doc.to_java)
57
+ xfa.setChanged(true)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,22 @@
1
+ require 'pdf_ravager/field_types/acro_form'
2
+ require 'pdf_ravager/field_types/xfa'
3
+
4
+ module PDFRavager
5
+ module Fields
6
+ class Text
7
+ include FieldTypes::AcroForm
8
+ include FieldTypes::XFA
9
+
10
+ attr_reader :name, :value
11
+
12
+ def initialize(name, value)
13
+ @name, @value = name, value
14
+ end
15
+
16
+ def ==(other)
17
+ self.name == other.name && self.value == other.value
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module PDFRavager
2
+ module Fieldsets
3
+ class CheckboxGroup
4
+
5
+ def initialize(template, name)
6
+ @template, @name = template, name
7
+ yield self if block_given?
8
+ end
9
+
10
+ def check(name, opts={})
11
+ @template.check("#{@name}.#{name}", opts)
12
+ end
13
+
14
+ def uncheck(name, opts={})
15
+ @template.uncheck("#{@name}.#{name}", opts)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module PDFRavager
2
+ module Fieldsets
3
+ class RadioGroup
4
+
5
+ def initialize(template, name)
6
+ @template, @name = template, name
7
+ yield self if block_given?
8
+ end
9
+
10
+ def fill(name)
11
+ @template.fill(@name, name)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ require 'pdf_ravager/template'
2
+
3
+ module Kernel
4
+ def pdf(*args, &blk)
5
+ PDFRavager::Template.new(*args, &blk)
6
+ end
7
+ end
@@ -1,90 +1,38 @@
1
+ unless RUBY_PLATFORM =~ /java/
2
+ raise "You can only ravage PDFs using JRuby, not #{RUBY_PLATFORM}!"
3
+ end
4
+
1
5
  require 'java'
2
6
  require 'nokogiri'
3
7
  require File.dirname(__FILE__) + '/../../vendor/iText-4.2.0'
4
8
 
5
- java_import "com.lowagie.text.pdf.AcroFields"
6
- java_import "com.lowagie.text.pdf.PdfArray"
7
- java_import "com.lowagie.text.pdf.PdfDictionary"
8
- java_import "com.lowagie.text.pdf.PdfName"
9
- java_import "com.lowagie.text.pdf.PdfObject"
10
- java_import "com.lowagie.text.pdf.PdfReader"
11
- java_import "com.lowagie.text.pdf.PdfStamper"
12
- java_import "com.lowagie.text.pdf.PdfStream"
13
- java_import "com.lowagie.text.pdf.PdfWriter"
14
- java_import "com.lowagie.text.pdf.XfaForm"
15
- java_import "com.lowagie.text.pdf.XfdfReader"
16
-
17
9
  module PDFRavager
18
10
  class Ravager
19
11
  private_class_method :new
20
12
 
21
- def self.open(opts={}, &block)
13
+ def self.ravage(template, opts={})
22
14
  opts = {:in_file => opts} if opts.is_a? String
23
15
  out = if opts[:out_file]
24
16
  java.io.FileOutputStream.new(opts[:out_file])
25
17
  else
26
18
  java.io.ByteArrayOutputStream.new
27
19
  end
28
- raise "You must pass a block" unless block_given?
29
- ravager = new(opts.merge({:out => out}))
30
- yield ravager
31
- ravager.destroy
20
+ ravager = new(template, opts.merge({:out => out}))
21
+ ravager.send(:set_field_values)
22
+ ravager.send(:set_read_only) if opts[:read_only]
23
+ ravager.send(:destroy)
32
24
  out
33
25
  end
34
26
 
35
- def set_field_value(name, value, type=nil, options={})
36
- return set_rich_text_field(name, value) if options && options[:rich]
37
- begin
38
- # First try AcroForms method of setting value
39
- @afields.setField(SOM.short_name(name), value)
40
- rescue java.lang.NullPointerException
41
- end
42
- # Also look for the XDP node and set that value
43
- # Note: the double-load is to work around a Nokogiri bug I found:
44
- # https://github.com/sparklemotion/nokogiri/issues/781
45
- doc = Nokogiri::XML(Nokogiri::XML::Document.wrap(@xfa.getDomDocument).to_xml)
46
- node_type = type == :checkbox ? 'integer' : 'text'
47
- doc.xpath("//*[local-name()='field'][@name='#{name}']").each do |node|
48
- value_node = node.at_xpath("*[local-name()='value']")
49
- if value_node
50
- text_node = value_node.at_xpath("*[local-name()='#{node_type}']")
51
- if text_node
52
- # Complete node structure already exists - just set the value
53
- text_node.content = value
54
- else
55
- # <value> node exists, but without child <text> node
56
- Nokogiri::XML::Builder.with(value_node) do |xml|
57
- xml.text_ {
58
- xml.send("#{node_type}_", value)
59
- }
60
- end
61
- end
62
- else
63
- # No <value> node exists - create whole structure
64
- Nokogiri::XML::Builder.with(node) do |xml|
65
- xml.value_ {
66
- xml.send("#{node_type}_") {
67
- xml.text value
68
- }
69
- }
70
- end
71
- end
72
- end
73
- @xfa.setDomDocument(doc.to_java)
74
- @xfa.setChanged(true)
75
- end
76
-
77
- def destroy
78
- read_only! if @opts[:read_only]
79
- @stamper.close
80
- end
81
-
82
27
  private
83
28
 
84
- def initialize(opts={})
85
- @opts = opts
86
- @reader = PdfReader.new(opts[:in_file])
87
- @stamper = PdfStamper.new(@reader, opts[:out])
29
+ # instantiation is private because there is a lot of state mutation
30
+ # and some invariants that need to be cleaned up. therefore, Ravager's
31
+ # lifecycle is managed by the public class method `ravage`.
32
+ def initialize(template, opts={})
33
+ @template, @opts = template, opts
34
+ @reader = com.lowagie.text.pdf.PdfReader.new(opts[:in_file])
35
+ @stamper = com.lowagie.text.pdf.PdfStamper.new(@reader, opts[:out])
88
36
  @afields = @stamper.getAcroFields
89
37
  @xfa = @afields.getXfa
90
38
  @som = @xfa.getDatasetsSom
@@ -95,62 +43,36 @@ module PDFRavager
95
43
  end
96
44
  end
97
45
 
98
- def set_rich_text_field(name, value)
99
- doc = Nokogiri::XML(Nokogiri::XML::Document.wrap(@xfa.getDomDocument).to_xml)
100
- doc.xpath("//*[local-name()='field'][@name='#{name}']").each do |node|
101
- value_node = node.at_xpath("*[local-name()='value']")
102
- if value_node
103
- # <value> node exists - just create <exData>
104
- Nokogiri::XML::Builder.with(value_node) do |xml|
105
- xml.exData('contentType' => 'text/html') do
106
- xml.body_('xmlns' => "http://www.w3.org/1999/xhtml", 'xmlns:xfa' => "http://www.xfa.org/schema/xfa-data/1.0/") do
107
- xml << value # Note: this value is not sanitized/escaped!
108
- end
109
- end
110
- end
111
- else
112
- # No <value> node exists - create whole structure
113
- Nokogiri::XML::Builder.with(node) do |xml|
114
- xml.value_ do
115
- xml.exData('contentType' => 'text/html') do
116
- xml.body_('xmlns' => "http://www.w3.org/1999/xhtml", 'xmlns:xfa' => "http://www.xfa.org/schema/xfa-data/1.0/") do
117
- xml << value # Note: this value is not sanitized/escaped!
118
- end
119
- end
120
- end
121
- end
46
+ def destroy
47
+ @stamper.close
48
+ end
49
+
50
+ def set_field_values
51
+ case @type
52
+ when :acro_forms
53
+ @template.fields.each{|f| f.set_acro_form_value(@afields) }
54
+ when :xfa
55
+ @template.fields.each do |f|
56
+ f.set_acro_form_value(@afields) if f.respond_to? :set_acro_form_value
57
+ f.set_xfa_value(@xfa) if f.respond_to? :set_xfa_value
122
58
  end
123
59
  end
124
- @xfa.setDomDocument(doc.to_java)
125
- @xfa.setChanged(true)
126
60
  end
127
61
 
128
- def read_only!
62
+ def set_read_only
129
63
  case @type
130
64
  when :acro_forms
131
65
  @stamper.setFormFlattening(true)
132
66
  when :xfa
133
- if @xfa_type == :static
134
- @stamper.setFormFlattening(true)
135
- else
136
- doc = Nokogiri::XML(Nokogiri::XML::Document.wrap(@xfa.getDomDocument).to_xml)
137
- doc.xpath("//*[local-name()='field']").each do |node|
138
- node["access"] = "readOnly"
139
- end
140
- @xfa.setDomDocument(doc.to_java)
141
- @xfa.setChanged(true)
67
+ doc = Nokogiri::XML(Nokogiri::XML::Document.wrap(@xfa.getDomDocument).to_xml)
68
+ doc.xpath("//*[local-name()='field']").each do |node|
69
+ node["access"] = "readOnly"
142
70
  end
71
+ @xfa.setDomDocument(doc.to_java)
72
+ @xfa.setChanged(true)
143
73
  end
144
74
  end
145
- end
146
-
147
- class SOM
148
- def self.short_name(str)
149
- XfaForm::Xml2Som.getShortName(self.escape(str))
150
- end
151
75
 
152
- def self.escape(str)
153
- XfaForm::Xml2Som.escapeSom(str) # just does: str.gsub(/\./) { '\\.' }
154
- end
155
76
  end
77
+
156
78
  end
@@ -0,0 +1,61 @@
1
+ require 'pdf_ravager/fields/text'
2
+ require 'pdf_ravager/fields/rich_text'
3
+ require 'pdf_ravager/fields/checkbox'
4
+ require 'pdf_ravager/fields/radio'
5
+ require 'pdf_ravager/fieldsets/checkbox_group'
6
+ require 'pdf_ravager/fieldsets/radio_group'
7
+ require 'pdf_ravager/ravager' if RUBY_PLATFORM =~ /java/
8
+
9
+ module PDFRavager
10
+ class Template
11
+ attr_reader :name, :fields
12
+
13
+ def initialize(name=nil)
14
+ @name, @fields = name, []
15
+ yield self if block_given?
16
+ end
17
+
18
+ def text(name, value)
19
+ @fields << PDFRavager::Fields::Text.new(name, value)
20
+ end
21
+
22
+ def rich_text(name, value)
23
+ @fields << PDFRavager::Fields::RichText.new(name, value)
24
+ end
25
+
26
+ def check(name, opts={})
27
+ @fields << PDFRavager::Fields::Checkbox.new(name, true, opts)
28
+ end
29
+
30
+ def uncheck(name, opts={})
31
+ @fields << PDFRavager::Fields::Checkbox.new(name, false, opts)
32
+ end
33
+
34
+ def fill(group_name, name)
35
+ @fields << PDFRavager::Fields::Radio.new(group_name, name)
36
+ end
37
+
38
+ def checkbox_group(group_name, &blk)
39
+ PDFRavager::Fieldsets::CheckboxGroup.new(self, group_name, &blk)
40
+ end
41
+
42
+ def radio_group(group_name, &blk)
43
+ PDFRavager::Fieldsets::RadioGroup.new(self, group_name, &blk)
44
+ end
45
+
46
+ if RUBY_PLATFORM =~ /java/
47
+ def ravage(file, opts={})
48
+ PDFRavager::Ravager.ravage(self, opts.merge({:in_file => file}))
49
+ end
50
+ else
51
+ def ravage(file, opts={})
52
+ raise "You can only ravage PDFs using JRuby, not #{RUBY_PLATFORM}!"
53
+ end
54
+ end
55
+
56
+ def ==(other)
57
+ self.fields == other.fields
58
+ end
59
+
60
+ end
61
+ end
@@ -1,3 +1,3 @@
1
1
  module PDFRavager
2
- VERSION = "0.0.8"
3
- end
2
+ VERSION = "0.1.0"
3
+ end
data/pdf_ravager.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
9
9
  s.homepage = 'https://github.com/abevoelker/pdf_ravager'
10
10
  s.summary = %q{DSL to aid filling out AcroForms PDF and XFA documents}
11
11
  s.description = %q{DSL to aid filling out AcroForms PDF and XFA documents}
12
+ s.license = 'MIT'
12
13
 
13
- s.add_dependency "json"
14
14
  s.add_dependency "nokogiri"
15
15
  s.add_development_dependency "bundler", "~> 1.0"
16
16
  s.add_development_dependency "minitest", "~> 4.1"
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency "chunky_png", "~> 1.2"
20
20
 
21
21
  s.files = `git ls-files`.split("\n")
22
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.test_files = ['test','spec','features'].map{|d| `git ls-files -- #{d}/*`.split("\n")}.flatten
23
23
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
24
  s.require_paths = ["lib"]
25
25
  end
@@ -3,20 +3,20 @@ require 'rspec'
3
3
  require 'pdf_ravager'
4
4
  require 'securerandom'
5
5
  require 'chunky_png'
6
+ require 'tempfile'
6
7
 
7
- def mktemp
8
- # the tests are already dependent on Linux, so /tmp/ usage should be OK
9
- "/tmp/#{SecureRandom.uuid}"
8
+ def mktemp(ext)
9
+ Tempfile.new(['', ext]).path
10
10
  end
11
11
 
12
12
  def pdf_to_ps(pdf_file, out_file=nil)
13
- out_file ||= "#{mktemp}.ps"
13
+ out_file ||= mktemp('.ps')
14
14
  system("acroread -toPostScript -markupsOn -pairs #{pdf_file} #{out_file} >/dev/null 2>&1")
15
15
  out_file
16
16
  end
17
17
 
18
18
  def ps_to_png(ps_file, out_file=nil)
19
- out_file ||= "#{mktemp}.png"
19
+ out_file ||= mktemp('.png')
20
20
  system("gs -dSAFER -dBATCH -dNOPAUSE -r150 -sDEVICE=png16m -dTextAlphaBits=4 -sOutputFile=#{out_file} #{ps_file} >/dev/null 2>&1")
21
21
  out_file
22
22
  end
@@ -3,11 +3,11 @@ require File.dirname(__FILE__) + '/../integration_helper'
3
3
  describe 'a PDF with a multiline text field' do
4
4
  describe 'filled with FOO' do
5
5
  before(:each) do
6
- p = pdf do
7
- text 'multilinetext', 'FOO' * 10000
6
+ p = PDFRavager::Template.new do |t|
7
+ t.text 'multilinetext', 'FOO' * 10000
8
8
  end
9
9
  pdf_file = File.join(File.dirname(__FILE__), "pdf")
10
- @pdf_file = "#{mktemp}.pdf"
10
+ @pdf_file = mktemp('.pdf')
11
11
  @pdf = p.ravage pdf_file, :out_file => @pdf_file
12
12
  end
13
13
 
@@ -3,11 +3,11 @@ require 'integration/integration_helper'
3
3
  describe 'a PDF with a text field' do
4
4
  describe 'filled with foo' do
5
5
  before(:each) do
6
- p = pdf do
7
- text 'text_field', 'foo'
6
+ p = PDFRavager::Template.new do |t|
7
+ t.text 'text_field', 'foo'
8
8
  end
9
9
  pdf_file = File.join(File.dirname(__FILE__), "pdf")
10
- @pdf_file = "#{mktemp}.pdf"
10
+ @pdf_file = mktemp('.pdf')
11
11
  @pdf = p.ravage pdf_file, :out_file => @pdf_file
12
12
  end
13
13
 
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/fields_helper'
2
+ require 'pdf_ravager/template'
3
+
4
+ class TestCheckbox < MiniTest::Unit::TestCase
5
+
6
+ def setup
7
+ @checked = PDFRavager::Fields::Checkbox.new('checkbox1', true, :true_value => 'foo', :false_value => 'bar')
8
+ @unchecked = PDFRavager::Fields::Checkbox.new('checkbox2', false, :true_value => 'foo', :false_value => 'bar')
9
+ end
10
+
11
+ def test_that_custom_checked_acro_form_value_is_set
12
+ assert_equal @checked.acro_form_value, 'foo'
13
+ end
14
+
15
+ def test_that_custom_checked_xfa_value_is_set
16
+ assert_equal @checked.xfa_value, 'foo'
17
+ end
18
+
19
+ def test_that_custom_unchecked_acro_form_value_is_set
20
+ assert_equal @unchecked.acro_form_value, 'bar'
21
+ end
22
+
23
+ def test_that_custom_unchecked_xfa_value_is_set
24
+ assert_equal @unchecked.xfa_value, 'bar'
25
+ end
26
+
27
+ end
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/../unit_helper'
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/unit_helper'
2
+ require 'pdf_ravager/kernel'
3
+
4
+ class TestKernelIntegration < MiniTest::Unit::TestCase
5
+
6
+ def setup
7
+ @template = pdf do |p|
8
+ p.text 'text', 'foo'
9
+ p.rich_text 'rich_text', '<b>foo</b>'
10
+ p.check 'checkbox1'
11
+ p.uncheck 'checkbox2'
12
+ p.fill 'radio_group', 'button'
13
+ end
14
+ end
15
+
16
+ def test_that_dsl_template_equals_longform_template
17
+ template = PDFRavager::Template.new do |t|
18
+ t.text 'text', 'foo'
19
+ t.rich_text 'rich_text', '<b>foo</b>'
20
+ t.check 'checkbox1'
21
+ t.uncheck 'checkbox2'
22
+ t.fill 'radio_group', 'button'
23
+ end
24
+ assert_equal @template, template
25
+ end
26
+
27
+ end
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/unit_helper'
2
+ require 'pdf_ravager/template'
3
+
4
+ class TestTemplate < MiniTest::Unit::TestCase
5
+
6
+ def setup
7
+ @template = PDFRavager::Template.new do |t|
8
+ t.text 'text', 'foo'
9
+ t.rich_text 'rich_text', '<b>foo</b>'
10
+ t.check 'checkbox1'
11
+ t.uncheck 'checkbox2'
12
+ t.checkbox_group 'cbox_group' do |cb|
13
+ cb.check 'checked'
14
+ cb.uncheck 'unchecked'
15
+ end
16
+ t.fill 'radio_group', 'button'
17
+ t.radio_group 'better_radio_group' do |rg|
18
+ rg.fill 'button'
19
+ end
20
+ end
21
+
22
+ @template_with_name = PDFRavager::Template.new('template'){}
23
+ end
24
+
25
+ def test_that_text_is_set
26
+ assert_includes @template.fields, PDFRavager::Fields::Text.new('text', 'foo')
27
+ end
28
+
29
+ def test_that_rich_text_is_set
30
+ assert_includes @template.fields, PDFRavager::Fields::RichText.new('rich_text', '<b>foo</b>')
31
+ end
32
+
33
+ def test_that_checkbox_is_set
34
+ assert_includes @template.fields, PDFRavager::Fields::Checkbox.new('checkbox1', true)
35
+ end
36
+
37
+ def test_that_checkbox_is_unset
38
+ assert_includes @template.fields, PDFRavager::Fields::Checkbox.new('checkbox2', false)
39
+ end
40
+
41
+ def test_that_radio_button_is_filled
42
+ assert_includes @template.fields, PDFRavager::Fields::Radio.new('radio_group', 'button')
43
+ end
44
+
45
+ def test_that_name_is_set
46
+ assert_equal @template_with_name.name, 'template'
47
+ end
48
+
49
+ def test_that_checkbox_group_box_is_checked
50
+ assert_includes @template.fields, PDFRavager::Fields::Checkbox.new('cbox_group.checked', true)
51
+ end
52
+
53
+ def test_that_checkbox_group_box_is_unchecked
54
+ assert_includes @template.fields, PDFRavager::Fields::Checkbox.new('cbox_group.unchecked', false)
55
+ end
56
+
57
+ def test_that_radio_group_button_is_filled
58
+ assert_includes @template.fields, PDFRavager::Fields::Radio.new('better_radio_group', 'button')
59
+ end
60
+
61
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdf_ravager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,38 +9,20 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-07 00:00:00.000000000 Z
12
+ date: 2013-05-10 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: json
16
- version_requirements: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ! '>='
19
- - !ruby/object:Gem::Version
20
- version: !binary |-
21
- MA==
22
- none: false
23
- requirement: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ! '>='
26
- - !ruby/object:Gem::Version
27
- version: !binary |-
28
- MA==
29
- none: false
30
- prerelease: false
31
- type: :runtime
32
14
  - !ruby/object:Gem::Dependency
33
15
  name: nokogiri
34
16
  version_requirements: !ruby/object:Gem::Requirement
35
17
  requirements:
36
- - - ! '>='
18
+ - - ">="
37
19
  - !ruby/object:Gem::Version
38
20
  version: !binary |-
39
21
  MA==
40
22
  none: false
41
23
  requirement: !ruby/object:Gem::Requirement
42
24
  requirements:
43
- - - ! '>='
25
+ - - ">="
44
26
  - !ruby/object:Gem::Version
45
27
  version: !binary |-
46
28
  MA==
@@ -51,13 +33,13 @@ dependencies:
51
33
  name: bundler
52
34
  version_requirements: !ruby/object:Gem::Requirement
53
35
  requirements:
54
- - - ~>
36
+ - - "~>"
55
37
  - !ruby/object:Gem::Version
56
38
  version: '1.0'
57
39
  none: false
58
40
  requirement: !ruby/object:Gem::Requirement
59
41
  requirements:
60
- - - ~>
42
+ - - "~>"
61
43
  - !ruby/object:Gem::Version
62
44
  version: '1.0'
63
45
  none: false
@@ -67,13 +49,13 @@ dependencies:
67
49
  name: minitest
68
50
  version_requirements: !ruby/object:Gem::Requirement
69
51
  requirements:
70
- - - ~>
52
+ - - "~>"
71
53
  - !ruby/object:Gem::Version
72
54
  version: '4.1'
73
55
  none: false
74
56
  requirement: !ruby/object:Gem::Requirement
75
57
  requirements:
76
- - - ~>
58
+ - - "~>"
77
59
  - !ruby/object:Gem::Version
78
60
  version: '4.1'
79
61
  none: false
@@ -83,13 +65,13 @@ dependencies:
83
65
  name: rspec
84
66
  version_requirements: !ruby/object:Gem::Requirement
85
67
  requirements:
86
- - - ~>
68
+ - - "~>"
87
69
  - !ruby/object:Gem::Version
88
70
  version: '2.11'
89
71
  none: false
90
72
  requirement: !ruby/object:Gem::Requirement
91
73
  requirements:
92
- - - ~>
74
+ - - "~>"
93
75
  - !ruby/object:Gem::Version
94
76
  version: '2.11'
95
77
  none: false
@@ -99,13 +81,13 @@ dependencies:
99
81
  name: rake
100
82
  version_requirements: !ruby/object:Gem::Requirement
101
83
  requirements:
102
- - - ~>
84
+ - - "~>"
103
85
  - !ruby/object:Gem::Version
104
86
  version: '0.9'
105
87
  none: false
106
88
  requirement: !ruby/object:Gem::Requirement
107
89
  requirements:
108
- - - ~>
90
+ - - "~>"
109
91
  - !ruby/object:Gem::Version
110
92
  version: '0.9'
111
93
  none: false
@@ -115,13 +97,13 @@ dependencies:
115
97
  name: chunky_png
116
98
  version_requirements: !ruby/object:Gem::Requirement
117
99
  requirements:
118
- - - ~>
100
+ - - "~>"
119
101
  - !ruby/object:Gem::Version
120
102
  version: '1.2'
121
103
  none: false
122
104
  requirement: !ruby/object:Gem::Requirement
123
105
  requirements:
124
- - - ~>
106
+ - - "~>"
125
107
  - !ruby/object:Gem::Version
126
108
  version: '1.2'
127
109
  none: false
@@ -133,15 +115,24 @@ executables: []
133
115
  extensions: []
134
116
  extra_rdoc_files: []
135
117
  files:
136
- - .gitignore
137
- - .travis.yml
118
+ - ".gitignore"
119
+ - ".travis.yml"
138
120
  - Gemfile
139
121
  - LICENSE
140
122
  - README.md
141
123
  - Rakefile
142
124
  - lib/pdf_ravager.rb
143
- - lib/pdf_ravager/pdf.rb
125
+ - lib/pdf_ravager/field_types/acro_form.rb
126
+ - lib/pdf_ravager/field_types/xfa.rb
127
+ - lib/pdf_ravager/fields/checkbox.rb
128
+ - lib/pdf_ravager/fields/radio.rb
129
+ - lib/pdf_ravager/fields/rich_text.rb
130
+ - lib/pdf_ravager/fields/text.rb
131
+ - lib/pdf_ravager/fieldsets/checkbox_group.rb
132
+ - lib/pdf_ravager/fieldsets/radio_group.rb
133
+ - lib/pdf_ravager/kernel.rb
144
134
  - lib/pdf_ravager/ravager.rb
135
+ - lib/pdf_ravager/template.rb
145
136
  - lib/pdf_ravager/version.rb
146
137
  - pdf_ravager.gemspec
147
138
  - spec/integration/integration_helper.rb
@@ -152,18 +143,22 @@ files:
152
143
  - spec/integration/text_field/pdf
153
144
  - spec/integration/text_field/spec.rb
154
145
  - spec/spec_helper.rb
155
- - spec/unit/pdf_spec.rb
146
+ - spec/unit/fields/checkbox_spec.rb
147
+ - spec/unit/fields/fields_helper.rb
148
+ - spec/unit/kernel_spec.rb
149
+ - spec/unit/template_spec.rb
156
150
  - spec/unit/unit_helper.rb
157
151
  - vendor/iText-4.2.0.jar
158
152
  homepage: https://github.com/abevoelker/pdf_ravager
159
- licenses: []
153
+ licenses:
154
+ - MIT
160
155
  post_install_message:
161
156
  rdoc_options: []
162
157
  require_paths:
163
158
  - lib
164
159
  required_ruby_version: !ruby/object:Gem::Requirement
165
160
  requirements:
166
- - - ! '>='
161
+ - - ">="
167
162
  - !ruby/object:Gem::Version
168
163
  segments:
169
164
  - 0
@@ -173,7 +168,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
173
168
  none: false
174
169
  required_rubygems_version: !ruby/object:Gem::Requirement
175
170
  requirements:
176
- - - ! '>='
171
+ - - ">="
177
172
  - !ruby/object:Gem::Version
178
173
  segments:
179
174
  - 0
@@ -187,4 +182,17 @@ rubygems_version: 1.8.24
187
182
  signing_key:
188
183
  specification_version: 3
189
184
  summary: DSL to aid filling out AcroForms PDF and XFA documents
190
- test_files: []
185
+ test_files:
186
+ - spec/integration/integration_helper.rb
187
+ - spec/integration/multiline_text_field/expected.png
188
+ - spec/integration/multiline_text_field/pdf
189
+ - spec/integration/multiline_text_field/spec.rb
190
+ - spec/integration/text_field/expected.png
191
+ - spec/integration/text_field/pdf
192
+ - spec/integration/text_field/spec.rb
193
+ - spec/spec_helper.rb
194
+ - spec/unit/fields/checkbox_spec.rb
195
+ - spec/unit/fields/fields_helper.rb
196
+ - spec/unit/kernel_spec.rb
197
+ - spec/unit/template_spec.rb
198
+ - spec/unit/unit_helper.rb
@@ -1,105 +0,0 @@
1
- require 'json'
2
- require 'pdf_ravager/ravager' if RUBY_PLATFORM =~ /java/
3
-
4
- module PDFRavager
5
- class PDF
6
- attr_reader :name, :fields
7
-
8
- def initialize(name=nil, opts={})
9
- @name = name if name
10
- @fields = opts[:fields] || []
11
- end
12
-
13
- def text(name, value, opts={})
14
- if opts.empty?
15
- @fields << {:name => name, :value => value, :type => :text}
16
- else
17
- @fields << {:name => name, :value => value, :type => :text, :options => opts}
18
- end
19
- end
20
-
21
- def check(name, opts={})
22
- @fields << {:name => name, :value => true, :type => :checkbox}
23
- end
24
-
25
- def radio_group(gname, &blk)
26
- fields = []
27
- # TODO: replace w/ singleton method?
28
- PDF.instance_eval do
29
- send(:define_method, :fill) do |name, opts={}|
30
- fields << {:name => gname, :value => name, :type => :radio}
31
- end
32
- blk.call
33
- send(:undef_method, :fill)
34
- end
35
-
36
- @fields += fields
37
- end
38
-
39
- def checkbox_group(gname, &blk)
40
- # TODO: replace w/ singleton method?
41
- PDF.instance_eval do
42
- alias_method :__check_original__, :check
43
- send(:define_method, :check) do |name, opts={}|
44
- __check_original__("#{gname}.#{name}", opts)
45
- end
46
- blk.call
47
- # restore check method back to normal
48
- alias_method :check, :__check_original__
49
- send(:undef_method, :__check_original__)
50
- end
51
- end
52
-
53
- if RUBY_PLATFORM =~ /java/
54
- def ravage(file, opts={})
55
- PDFRavager::Ravager.open(opts.merge(:in_file => file)) do |pdf|
56
- @fields.each do |f|
57
- value = if f[:type] == :checkbox
58
- !!f[:value] ? '1' : '0' # Checkbox default string values
59
- else
60
- f[:value]
61
- end
62
- pdf.set_field_value(f[:name], value, f[:type], f[:options])
63
- end
64
- end
65
- end
66
- else
67
- def ravage(file, opts={})
68
- raise "You can only ravage .pdfs using JRuby, not #{RUBY_PLATFORM}!"
69
- end
70
- end
71
-
72
- def ==(other)
73
- self.name == other.name && self.fields == other.fields
74
- end
75
-
76
- def to_json(*args)
77
- {
78
- "json_class" => self.class.name,
79
- "data" => {"name" => @name, "fields" => @fields }
80
- }.to_json(*args)
81
- end
82
-
83
- def self.json_create(obj)
84
- fields = obj["data"]["fields"].map do |f|
85
- # symbolize the root keys
86
- f = f.inject({}){|h,(k,v)| h[k.to_sym] = v; h}
87
- f[:type] = f[:type].to_sym if f[:type]
88
- # symbolize the :options keys
89
- if f[:options]
90
- f[:options] = f[:options].inject({}){|h,(k,v)| h[k.to_sym] = v; h}
91
- end
92
- f
93
- end
94
- o = new(obj["data"]["name"], :fields => fields)
95
- end
96
- end
97
- end
98
-
99
- module Kernel
100
- def pdf(name=nil, opts={}, &blk)
101
- r = PDFRavager::PDF.new(name, opts)
102
- r.instance_eval(&blk)
103
- r
104
- end
105
- end
@@ -1,48 +0,0 @@
1
- require File.dirname(__FILE__) + '/unit_helper'
2
- require 'pdf_ravager/pdf'
3
-
4
- class TestPDF < MiniTest::Unit::TestCase
5
- def setup
6
- @pdf = pdf 'foo.pdf' do
7
- text 'text', 'foo'
8
- text 'rich_text', '<b>foo</b>', :rich => true
9
- check 'checkbox'
10
- checkbox_group 'checkbox_group' do
11
- check 'foo'
12
- end
13
- radio_group 'radio_group' do
14
- fill 'foo'
15
- end
16
- end
17
-
18
- @pdf_from_json = JSON.parse(@pdf.to_json)
19
- end
20
-
21
- def test_that_name_is_set
22
- assert_equal @pdf.name, 'foo.pdf'
23
- end
24
-
25
- def test_that_text_is_set
26
- assert_includes @pdf.fields, {:name => 'text', :value => 'foo', :type => :text}
27
- end
28
-
29
- def test_that_rich_text_is_set
30
- assert_includes @pdf.fields, {:name => 'rich_text', :value => '<b>foo</b>', :type => :text, :options => {:rich => true}}
31
- end
32
-
33
- def test_that_checkbox_is_set
34
- assert_includes @pdf.fields, {:name => 'checkbox', :value => true, :type => :checkbox}
35
- end
36
-
37
- def test_that_checkbox_group_is_set
38
- assert_includes @pdf.fields, {:name => 'checkbox_group.foo', :value => true, :type => :checkbox}
39
- end
40
-
41
- def test_that_radio_group_is_set
42
- assert_includes @pdf.fields, {:name => 'radio_group', :value => 'foo', :type => :radio}
43
- end
44
-
45
- def test_json_serialization
46
- assert_equal @pdf, @pdf_from_json
47
- end
48
- end