fn_document 0.9.2

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.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +20 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +19 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/fn_document.gemspec +95 -0
  9. data/lib/fn/block.rb +27 -0
  10. data/lib/fn/document.rb +204 -0
  11. data/lib/fn/migrate.xslt +224 -0
  12. data/lib/fn/node/base.rb +69 -0
  13. data/lib/fn/node/context.rb +70 -0
  14. data/lib/fn/node/root.rb +14 -0
  15. data/lib/fn/pdf/node/begin_document.rb +22 -0
  16. data/lib/fn/pdf/node/begin_page_ext.rb +24 -0
  17. data/lib/fn/pdf/node/create_textflow.rb +137 -0
  18. data/lib/fn/pdf/node/end_page_ext.rb +22 -0
  19. data/lib/fn/pdf/node/fit_image.rb +28 -0
  20. data/lib/fn/pdf/node/fit_pdi_page.rb +26 -0
  21. data/lib/fn/pdf/node/fit_textflow.rb +27 -0
  22. data/lib/fn/pdf/node/invert.rb +36 -0
  23. data/lib/fn/pdf/node/load_image.rb +21 -0
  24. data/lib/fn/pdf/node/open_pdi.rb +22 -0
  25. data/lib/fn/pdf/node/open_pdi_page.rb +31 -0
  26. data/lib/fn/pdf/node/resume_page.rb +21 -0
  27. data/lib/fn/pdf/node/set_parameter.rb +22 -0
  28. data/lib/fn/pdf/node/watermark.rb +24 -0
  29. data/lib/fn/pdf/struct.rb +40 -0
  30. data/lib/fn/pdf/writer.rb +205 -0
  31. data/lib/fn/resource.rb +53 -0
  32. data/lib/fn/swf/node/break.rb +19 -0
  33. data/lib/fn/swf/node/flash.rb +24 -0
  34. data/lib/fn/swf/node/font.rb +24 -0
  35. data/lib/fn/swf/node/frame.rb +19 -0
  36. data/lib/fn/swf/node/hot_spot.rb +43 -0
  37. data/lib/fn/swf/node/image.rb +19 -0
  38. data/lib/fn/swf/node/page.rb +30 -0
  39. data/lib/fn/swf/node/photo_block.rb +53 -0
  40. data/lib/fn/swf/node/text.rb +51 -0
  41. data/lib/fn/swf/struct.rb +43 -0
  42. data/lib/fn/swf/writer.rb +89 -0
  43. data/lib/fn/util.rb +27 -0
  44. data/lib/fn/validation.rng +172 -0
  45. data/lib/fn_document.rb +3 -0
  46. data/test/helper.rb +18 -0
  47. data/test/test_fn_document.rb +7 -0
  48. metadata +150 -0
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+
7
+ def EndPageExt(number)
8
+ FN::Node::Base("end_page_ext", :pagenumber => number).extend(EndPageExt)
9
+ end
10
+
11
+ module EndPageExt
12
+ include FN::Node::Base
13
+
14
+ def visit(struct)
15
+ has_no_children
16
+ struct.resume_page(attributes.to_h)
17
+ struct.end_page_ext("")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+ def FitImage(image, x, y, opts = {})
7
+ if opts.delete(:inverted)
8
+ opts[:orientate] = "south"
9
+ end
10
+ FN::Node::Base("fit_image", opts.merge(:image => image, :x => x, :y => y)).extend(FitImage)
11
+ end
12
+
13
+ module FitImage
14
+ include FN::Node::Base
15
+
16
+ def visit(struct)
17
+ has_no_children
18
+ h = attributes.to_h
19
+ img = struct[h.delete("image")]
20
+ x = h.delete("x").to_i
21
+ y = h.delete("y").to_i
22
+ h["scale"] = 0.99 # if h["scale"] == "1.0"
23
+ struct.fit_image(img, x, y, h)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+ def FitPdiPage(var)
7
+ FN::Node::Base("fit_pdi_page", :page => var)
8
+ end
9
+
10
+ module FitPdiPage
11
+ include FN::Node::Base
12
+
13
+ def visit(struct)
14
+ @logger = Logger.new("#{RAILS_ROOT}/log/pdf_writer_logs/#{Time.now.strftime('pdf_writer_log_%Y_%m_%d')}.log")
15
+ # @logger.info "HELP "*88
16
+ # @logger.info "self[:page] => #{self[:page]}" # yields {page}
17
+ # @logger.info "struct[self[:page]] => #{struct[self[:page]]}" # yields -1
18
+ # @logger.info "struct => #{struct.inspect}"
19
+ has_no_children
20
+ struct.fit_pdi_page(struct[self[:page]], 0, struct[CURRENT_PAGE_HEIGHT], "")
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+ def FitTextflow(flow, block)
7
+ FN::Node::Base("fit_textflow", :flow => "{#{flow && flow.flow_name}}",
8
+ :x => block["x"],
9
+ :y => block["y"],
10
+ :x2 => block["x"].to_f + block["width"].to_f,
11
+ :y2 => block["y"].to_f + block["height"].to_f
12
+ ).extend(FitTextflow)
13
+ end
14
+
15
+ module FitTextflow
16
+ include FN::Node::Base
17
+
18
+ def visit(struct)
19
+ has_no_children
20
+ struct.fit_textflow struct[self["flow"]],
21
+ self["x"].to_f, self["y"].to_f,
22
+ self["x2"].to_f, self["y2"].to_f, ""
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ # if block['flip'] == "yes"
2
+ # @pdf.save
3
+ # @pdf.translate block["x"].to_f + block["x2"].to_f, block["y"].to_f + block["y2"].to_f
4
+ # @pdf.rotate 180
5
+ # @pdf.fit_textflow flow, block["x"].to_f, block["y"].to_f, block["x2"].to_f, block["y2"].to_f, ""
6
+ # @pdf.restore
7
+ # else
8
+
9
+ require File.dirname(__FILE__) + "/../../node/base"
10
+ require "PDFlib"
11
+ module FN
12
+ module PDF
13
+ module Node
14
+ def Invert(block)
15
+ FN::Node::Base("invert",
16
+ :x => block["x"],
17
+ :y => block["y"],
18
+ :x2 => block["x"].to_f + (block["boxWidth"] || block["width"]).to_f,
19
+ :y2 => block["y"].to_f + (block["boxHeight"] || block["height"]).to_f
20
+ ).extend(Invert)
21
+ end
22
+
23
+ module Invert
24
+ include FN::Node::Base
25
+
26
+ def visit(struct)
27
+ struct.save
28
+ struct.translate self["x"].to_f + self["x2"].to_f, self["y"].to_f + self["y2"].to_f
29
+ struct.rotate 180
30
+ visit_children struct
31
+ struct.restore
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+ def LoadImage(file, var)
7
+ FN::Node::Base("load_image", :file => file, :assigns => var).extend(LoadImage)
8
+ end
9
+
10
+ module LoadImage
11
+ include FN::Node::Base
12
+
13
+ def visit(struct)
14
+ has_no_children
15
+ img = struct.load_image("auto", self[:file], "")
16
+ struct.assigns self, img
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+ def OpenPdi(file, var)
7
+ FN::Node::Base("open_pdi", :file => file, :assigns => var).extend(OpenPdi)
8
+ end
9
+
10
+ module OpenPdi
11
+ include FN::Node::Base
12
+
13
+ def visit(struct)
14
+ pdi = struct.open_pdi(self[:file], "", 0)
15
+ struct.assigns(self, pdi)
16
+ visit_children struct
17
+ struct.close_pdi(pdi)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+
7
+ def OpenPdiPage(pdi_var, page_number, page_var)
8
+ FN::Node::Base("open_pdi_page", :pdi => pdi_var, :number => page_number, :assigns => page_var).extend(OpenPdiPage)
9
+ end
10
+
11
+ module OpenPdiPage
12
+ include FN::Node::Base
13
+
14
+ def visit(struct)
15
+ @logger = Logger.new("#{RAILS_ROOT}/log/pdf_writer_logs/#{Time.now.strftime('pdf_writer_log_%Y_%m_%d')}.log")
16
+ # @logger.info "DOGS "*88
17
+ # @logger.info "struct.inspect: #{struct.inspect}"
18
+ # @logger.info "struct[self[:pdi]]: #{struct[self[:pdi]]}"
19
+ # @logger.info "self[:number].to_i: #{self[:number].to_i}"
20
+ pg = struct.open_pdi_page(struct[self[:pdi]], self[:number].to_i, "")
21
+ # @logger.info "self.inspect: #{self.inspect}"
22
+ # @logger.info "pg.inspect: #{pg.inspect}"
23
+ # @logger.info "CATS "*88
24
+ struct.assigns self, pg
25
+ visit_children struct
26
+ struct.close_pdi_page(pg)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+ def ResumePage(number)
7
+ FN::Node::Base("resume_page", :pagenumber => number).extend(ResumePage)
8
+ end
9
+
10
+ module ResumePage
11
+ include FN::Node::Base
12
+
13
+ def visit(struct)
14
+ struct.resume_page(attributes.to_h)
15
+ visit_children(struct)
16
+ struct.suspend_page("")
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+
7
+ def SetParameter(key, value)
8
+ FN::Node::Base("set_parameter", :key => key, :value => value).extend(SetParameter)
9
+ end
10
+
11
+ module SetParameter
12
+ include FN::Node::Base
13
+
14
+ def visit(struct)
15
+ has_no_children
16
+ struct[self[:key]] = self[:value]
17
+ struct.set_parameter(self[:key], self[:value])
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + "/../../node/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module PDF
5
+ module Node
6
+
7
+ def Watermark(text)
8
+ FN::Node::Base("watermark", :text => text).extend(Watermark)
9
+ end
10
+
11
+ module Watermark
12
+ include FN::Node::Base
13
+
14
+ def visit(struct)
15
+ has_no_children
16
+ w = struct[CURRENT_PAGE_WIDTH]
17
+ h = struct[CURRENT_PAGE_HEIGHT]
18
+ font = struct.load_font("Arial,Bold", "unicode", "")
19
+ struct.fit_textline(self["text"], 0, h, "font #{font} fontsize 30 boxsize {#{w} #{h}} fitmethod meet rotate 0 textrendering 1 position {0 50}")
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ require "PDFlib"
2
+ module FN
3
+ module PDF
4
+ class Struct < Hash
5
+ attr_reader :pdf
6
+
7
+ def initialize(debug = false)
8
+ @pdf = PDFlib.new
9
+ @debug = debug
10
+ end
11
+
12
+ def assigns(node, value)
13
+ self["{#{node[:assigns]}}"] = value
14
+ end
15
+
16
+ def method_missing(*args, &block)
17
+ @logger = Logger.new("#{RAILS_ROOT}/log/pdf_writer_logs/#{Time.now.strftime('pdf_writer_log_%Y_%m_%d')}.log")
18
+ # @logger.info("This is what args looks like when we first get it: #{a.inspect}")
19
+ args.map! do |elem|
20
+ case elem
21
+ when Hash:
22
+ elem.inject([]) {|m, (key, value)|
23
+ m << "#{key}={#{value}}"
24
+ }.join(" ")
25
+ else
26
+ elem
27
+ end
28
+ end
29
+ begin
30
+ # @logger.info("Calling #{args.inspect}, this struct: #{self.inspect}")
31
+ @pdf.send(*args, &block)
32
+ rescue Exception => error
33
+ # @logger.info("FAILURE FAILURE FAILURE FAILURE FAILURE FAILURE")
34
+ # @logger.info("Exception: #{error}")
35
+ raise error
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,205 @@
1
+ require "rubygems"
2
+ require "PDFlib"
3
+ require "RMagick"
4
+ require "tempfile"
5
+ Dir[File.dirname(__FILE__) + "/node/*.rb"].each do |f|
6
+ require_dependency f.sub(/\.rb$/, '')
7
+ end
8
+ module FN
9
+ module PDF
10
+ class WriterError < RuntimeError; end
11
+ class Writer
12
+ include Node
13
+ SPACE = /[\s_]+/
14
+
15
+ def self.encoding=(e)
16
+ @encoding = e
17
+ end
18
+
19
+ def self.encoding
20
+ @encoding
21
+ end
22
+
23
+ def write(doc, options = {})
24
+ options[:save_as] ||= (tmp = Tempfile.new("pdf"); tmp.close; tmp.path)
25
+ write_xml translate(doc, options), options[:save_as]
26
+ end
27
+
28
+ def write_xml(xml, file)
29
+ xml.root.extend(FN::Node::Root)
30
+ xml.root.visit(FN::PDF::Struct.new)
31
+ return file
32
+ end
33
+
34
+ def titlecase(str)
35
+ face = str.split(SPACE).map{|s| s.capitalize }.join("")
36
+ end
37
+
38
+ def translate(doc, options = {})
39
+ raise "Not an FN Document" unless doc.is_a?(FN::Document)
40
+ @logger = Logger.new("#{RAILS_ROOT}/log/pdf_writer_logs/#{Time.now.strftime('pdf_writer_log_%Y_%m_%d')}.log")
41
+
42
+
43
+ debug = options[:debug]
44
+ file = options[:save_as]
45
+ bkg = options[:background]
46
+ pdf_version = options[:pdf_version] ||
47
+ 1.5
48
+ license = options[:license]
49
+ resource_file = options[:font_list] ||
50
+ File.join(File.dirname(__FILE__), "pdflib.upr")
51
+ self.class.encoding = options[:encoding] ||
52
+ "unicode"
53
+ textformat = options[:textformat] || "utf8"
54
+ root = options[:resource_root] ||
55
+ ""
56
+ watermark = options[:watermark]
57
+
58
+ context = FN::Node::Context.new
59
+
60
+ # @logger.info "===== Setting Parameters ====="
61
+ context.add SetParameter("license", license)
62
+ context.add SetParameter("resourcefile", resource_file)
63
+ context.add SetParameter("hypertextencoding", self.class.encoding)
64
+ context.add SetParameter("textformat", textformat)
65
+
66
+ if bkg
67
+ # @logger.info "===== This Document Has a Background ====="
68
+ context << OpenPdi(bkg, assigns = "pdi")
69
+ end
70
+
71
+ # @logger.info "===== Uh... Begin Document ====="
72
+ context << BeginDocument(file, pdf_version)
73
+
74
+ # @logger.info "===== Setting A Forgotten Parameter ====="
75
+ context.add SetParameter("topdown", true)
76
+
77
+ # @logger.info "===== Adding #{doc.fonts.length} Fonts ====="
78
+ doc.fonts do |name|
79
+ LoadFont.add_all_variants_to context, name
80
+ end
81
+
82
+ # @logger.info "===== Adding #{doc.textflows.length} Text Flows ====="
83
+ flows_by_name = {}
84
+ doc.textflows.each do |flow|
85
+ ctf = CreateTextflow(flow)
86
+ flows_by_name[ctf.flow_name] = ctf
87
+ context.add ctf
88
+ end
89
+
90
+
91
+ # @logger.info "===== Adding #{doc.pages.length} Pages ====="
92
+ doc.pages.each_with_index do |page,index|
93
+ context.retain_after do
94
+ # @logger.info "Page #{index+1}:"
95
+ # @logger.info "BeginPageExt(:width => #{page["width"]}, :height => #{page["height"]}, :number => #{page["number"]})"
96
+ context << BeginPageExt(page["width"], page["height"], page["number"])
97
+ if bkg
98
+ # @logger.info "This Document Has Background"
99
+ # @logger.info "OpenPdiPage(:pdi_var => \"{pdi}\", :page_number => #{page["number"]}, :page_var => \"page\")"
100
+ context << OpenPdiPage("{pdi}", page["number"], assigns = "page" )
101
+ # @logger.info "FitPdiPage(:var => \"{page}\")"
102
+ context.add FitPdiPage("{page}")
103
+ elsif page["background"]
104
+ # @logger.info "Page #{index+1}: This Document does NOT have a Background, but the page does"
105
+ bkg_image = doc.resource(page["background"]).path_from(root)
106
+ context.add LoadImage(bkg_image, "tmp")
107
+ context.add FitImage("{tmp}", 0, page["height"],
108
+ :fitmethod => :meet,
109
+ :boxsize => [page["width"], page["height"]])
110
+ else
111
+ # @logger.info "Page #{index+1}: Does not have a background at all"
112
+ end
113
+ context.add(Watermark(watermark)) if watermark
114
+ end
115
+ end
116
+
117
+ # @logger.info "===== Adding #{doc.text_blocks.length} Text Blocks ====="
118
+ doc.text_blocks.each do |block|
119
+ flow = CreateTextflow(block)
120
+
121
+ context.retain_after do
122
+ key = "flow-#{block.text}"
123
+ if flow.empty?
124
+ flow = flows_by_name[key]
125
+ else
126
+ flows_by_name.delete(key)
127
+ context.add(flow)
128
+ end
129
+
130
+ if flow
131
+ context << ResumePage(block.page_number)
132
+ context << Invert(block) if block["flip"] == "yes"
133
+ context.add FitTextflow(flow, block)
134
+ end
135
+ end
136
+ end
137
+
138
+ # @logger.info "===== Adding #{doc.photo_blocks.length} Photo Blocks ====="
139
+ doc.photo_blocks.each do |block|
140
+ # require "ruby-debug"
141
+ # debugger
142
+ image = nil
143
+ begin
144
+ context.inject_at_page(block.page_number) do
145
+ image = doc.resource(block.src).path_from(root)
146
+ tmp = Magick::Image::read(image).first
147
+ dims = [tmp.columns.to_f, tmp.rows.to_f]
148
+ x, y, width, height = calculate(block, dims)
149
+ fi = FitImage("{tmp}", x, y, :fitmethod => "meet", :boxsize => [width, height], :inverted => block["flip"] == "yes")
150
+ context.add LoadImage(image, "tmp")
151
+ context.add(fi)
152
+ end
153
+ rescue Magick::ImageMagickError => e
154
+ STDERR.puts e.message
155
+ STDERR.puts e.backtrace.join("\n")
156
+ raise WriterError.new("Couldn't load '#{block.src}', given by #{doc.resource(block.src).node}")
157
+ end
158
+ end
159
+
160
+ # @logger.info "===== Closing #{doc.pages.length} Pages ====="
161
+ doc.pages.each do |page|
162
+ context.add EndPageExt(page["number"])
163
+ end
164
+
165
+
166
+ # @logger.info "====================================================================="
167
+ # @logger.info "===== Somewhere after this point, the major churning begins ====="
168
+ # @logger.info "===== And starts looping through all the context things we just ====="
169
+ # @logger.info "===== added and tries to execute them all ====="
170
+ # @logger.info "====================================================================="
171
+
172
+ # @logger.info "================================== doc.xml_to_s ================================="
173
+ # @logger.info doc.xml_to_s
174
+ # @logger.info "================================== context.doc.to_s ============================="
175
+ # @logger.info context.doc.to_s
176
+ return context.doc
177
+ end
178
+
179
+
180
+ def calculate(block, dims)
181
+ x = bx = block["boxX"].to_i
182
+ y = by = block["boxY"].to_i
183
+ bw = block["boxWidth"].to_i
184
+ bh = block["boxHeight"].to_i
185
+ aw = block["width"].to_i
186
+ iw, ih = dims
187
+ ah = aw * ih / iw
188
+ case block["align"]
189
+ when /right/: x = bx + bw - aw
190
+ when /center/: x = bx + (bw - aw) / 2
191
+ else x = bx
192
+ end
193
+
194
+ case block["align"]
195
+ when /bottom/: y = by + bh
196
+ when /middle/: y = by + bh / 2
197
+ else y = by
198
+ end
199
+
200
+ return [x, y, aw, ah]
201
+ end
202
+
203
+ end
204
+ end
205
+ end