@@ -0,0 +1,22 @@ | |||
# PDF::FDF License | |||
Copyright (C) 2012 pmade inc. (Peter Jones pjones@pmade.com) | |||
Permission is hereby granted, free of charge, to any person obtaining | |||
a copy of this software and associated documentation files (the | |||
"Software"), to deal in the Software without restriction, including | |||
without limitation the rights to use, copy, modify, merge, publish, | |||
distribute, sublicense, and/or sell copies of the Software, and to | |||
permit persons to whom the Software is furnished to do so, subject to | |||
the following conditions: | |||
The above copyright notice and this permission notice shall be | |||
included in all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@@ -0,0 +1,81 @@ | |||
#!/usr/bin/env/ruby | |||
################################################################################ | |||
require('ostruct') | |||
require('optparse') | |||
require('pdf/fdf') | |||
################################################################################ | |||
class Command | |||
############################################################################## | |||
DEFAULT_OPTIONS = { | |||
:generate_fdf => false, | |||
:load_fields => false, | |||
:number_fields => false, | |||
} | |||
############################################################################## | |||
attr_reader(:options) | |||
############################################################################## | |||
def initialize | |||
@options = OpenStruct.new(DEFAULT_OPTIONS) | |||
OptionParser.new do |p| | |||
p.on('-h', '--help', 'Show this option summary') do | |||
$stdout.puts(p); exit | |||
end | |||
p.on('-g', '--generate-fdf', 'Generate an FDF file from YAML files') do | |||
options.generate_fdf = true | |||
end | |||
p.on('-l', '--load-fields', 'Load pdftk dump, generate YAML file') do | |||
options.load_fields = true | |||
end | |||
p.on('-n', '--number-fields', 'Give each field a unique value') do | |||
options.number_fields = true | |||
end | |||
end.parse!(ARGV) | |||
end | |||
############################################################################## | |||
def run | |||
if options.generate_fdf | |||
generate_fdf | |||
elsif options.load_fields | |||
load_fields | |||
else | |||
raise("no operation given, see --help") | |||
end | |||
end | |||
############################################################################## | |||
private | |||
############################################################################## | |||
def generate_fdf | |||
loader = PDF::FDF::FieldLoader.new | |||
ARGV.each {|file| loader.merge(file)} | |||
gen = PDF::FDF::Gen.new(loader.fields) | |||
$stdout.write(gen.to_fdf) | |||
end | |||
############################################################################## | |||
def load_fields | |||
parser = PDF::FDF::DumpFieldsParser.new | |||
parser.parse(ARGV.size == 1 ? File.open(ARGV.first) : $stdin) | |||
$stdout.puts(parser.generate_yaml(options.number_fields)) | |||
end | |||
end | |||
################################################################################ | |||
begin | |||
Command.new.run | |||
rescue RuntimeError => e | |||
$stderr.puts(File.basename($0) + ": ERROR: #{e}") | |||
exit(1) | |||
end |
@@ -0,0 +1,13 @@ | |||
################################################################################ | |||
require('yaml') | |||
################################################################################ | |||
module PDF | |||
module FDF | |||
autoload('Attributes', 'pdf/fdf/attributes') | |||
autoload('Field', 'pdf/fdf/field') | |||
autoload('FieldLoader', 'pdf/fdf/field_loader') | |||
autoload('DumpFieldsParser', 'pdf/fdf/dump_fields_parser') | |||
autoload('Gen', 'pdf/fdf/gen') | |||
end | |||
end |
@@ -0,0 +1,9 @@ | |||
module PDF::FDF::Attributes | |||
############################################################################## | |||
def attributes= (attrs={}) | |||
attrs.each do |key, value| | |||
send("#{key}=", value) | |||
end | |||
end | |||
end |
@@ -0,0 +1,54 @@ | |||
class PDF::FDF::DumpFieldsParser | |||
############################################################################## | |||
attr_reader(:fields) | |||
############################################################################## | |||
def initialize | |||
@fields = [] | |||
end | |||
############################################################################## | |||
def parse (io) | |||
current_field = nil | |||
io.each_line do |line| | |||
if line.match(/^---\s*$/) | |||
@fields << current_field if current_field | |||
current_field = PDF::FDF::Field.new | |||
elsif m = line.match(/^([^:]+):\s*(.*)$/) | |||
current_field ||= PDF::FDF::Field.new | |||
set_field_value(current_field, m[1], m[2]) | |||
else | |||
raise("don't know how to parse: #{line}") | |||
end | |||
end | |||
@fields | |||
end | |||
############################################################################## | |||
def generate_yaml (number_fields=false) | |||
@fields.each_with_index {|f, i| f.value = i.to_s} if number_fields | |||
@fields.map {|f| f.to_hash}.to_yaml | |||
end | |||
############################################################################## | |||
private | |||
############################################################################## | |||
def set_field_value (field, key, value) | |||
case key | |||
when 'FieldType' | |||
field.type = value | |||
when 'FieldName' | |||
field.name = value | |||
when 'FieldFlags' | |||
field.flags = value.to_i | |||
when 'FieldMaxLength' | |||
field.max_length = value.to_i | |||
when 'FieldStateOption' | |||
field.options << value | |||
end | |||
end | |||
end |
@@ -0,0 +1,35 @@ | |||
class PDF::FDF::Field | |||
############################################################################## | |||
include(PDF::FDF::Attributes) | |||
############################################################################## | |||
attr_accessor(:name, :alias, :value, :type, :flags, :max_length, :options) | |||
############################################################################## | |||
def initialize (attributes={}) | |||
attrs = { | |||
'name' => '', | |||
'alias' => '', | |||
'value' => '', | |||
'type' => 'Text', | |||
'flags' => 0, | |||
'max_length' => 0, | |||
'options' => [], | |||
}.merge(attributes) | |||
self.attributes = attrs | |||
end | |||
############################################################################## | |||
def to_hash | |||
h = instance_variables.inject({}) do |hash, name| | |||
hash[name.sub('@', '')] = instance_variable_get(name) | |||
hash | |||
end | |||
h.delete('max_length') if max_length.zero? | |||
h.delete('options') if options.empty? | |||
h | |||
end | |||
end |
@@ -0,0 +1,35 @@ | |||
class PDF::FDF::FieldLoader | |||
############################################################################## | |||
attr_reader(:fields) | |||
############################################################################## | |||
def initialize (file=nil) | |||
@fields = [] | |||
merge(file) if file | |||
end | |||
############################################################################## | |||
def merge (file) | |||
Array(YAML.load_file(file)).each do |new_field| | |||
if old_field = find(new_field) | |||
old_field.attributes = new_field | |||
else | |||
@fields << PDF::FDF::Field.new(new_field) | |||
end | |||
end | |||
@fields | |||
end | |||
############################################################################## | |||
private | |||
############################################################################## | |||
def find (new_field) | |||
@fields.detect do |old_field| | |||
(!new_field['name'].empty? && new_field['name'] == old_field.name) || | |||
(!new_field['alias'].empty? && new_field['alias'] == old_field.alias) | |||
end | |||
end | |||
end |
@@ -0,0 +1,73 @@ | |||
################################################################################ | |||
# | |||
# Portions of this file are: Copyright (c) 2009-2011 Jens Kraemer | |||
# | |||
# Permission is hereby granted, free of charge, to any person obtaining | |||
# a copy of this software and associated documentation files (the | |||
# 'Software'), to deal in the Software without restriction, including | |||
# without limitation the rights to use, copy, modify, merge, publish, | |||
# distribute, sublicense, and/or sell copies of the Software, and to | |||
# permit persons to whom the Software is furnished to do so, subject to | |||
# the following conditions: | |||
# | |||
# The above copyright notice and this permission notice shall be | |||
# included in all copies or substantial portions of the Software. | |||
# | |||
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | |||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
# | |||
################################################################################ | |||
class PDF::FDF::Gen | |||
############################################################################## | |||
def initialize (fields) | |||
@fields = fields | |||
end | |||
############################################################################## | |||
def to_fdf | |||
header + @fields.map {|f| encode_field(f)}.join + footer | |||
end | |||
############################################################################## | |||
private | |||
############################################################################## | |||
def header | |||
"%FDF-1.2\n\n1 0 obj\n<<\n/FDF << /Fields 2 0 R >>\n>>\nendobj\n2 0 obj\n[" | |||
end | |||
############################################################################## | |||
def footer | |||
"]\nendobj\ntrailer\n<<\n/Root 1 0 R\n\n>>\n%%EOF\n" | |||
end | |||
############################################################################## | |||
def encode_field (field) | |||
results = "<</T(#{field.name})/V" | |||
results << | |||
if field.value.is_a?(Array) | |||
'[' + field.value.map {|v| "(#{quote(v)})"}.join + ']' | |||
else | |||
"(#{quote(field.value)})" | |||
end | |||
results << ">>\n" | |||
results | |||
end | |||
############################################################################## | |||
def quote (value) | |||
value.to_s.strip. | |||
gsub(/\\/, '\\'). | |||
gsub(/\(/, '\('). | |||
gsub(/\)/, '\)'). | |||
gsub(/\n/, '\r') | |||
end | |||
end |
@@ -0,0 +1,5 @@ | |||
module PDF | |||
module FDF | |||
VERSION = '0.1.0' | |||
end | |||
end |