pdf_ravager 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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