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.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +20 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/fn_document.gemspec +95 -0
- data/lib/fn/block.rb +27 -0
- data/lib/fn/document.rb +204 -0
- data/lib/fn/migrate.xslt +224 -0
- data/lib/fn/node/base.rb +69 -0
- data/lib/fn/node/context.rb +70 -0
- data/lib/fn/node/root.rb +14 -0
- data/lib/fn/pdf/node/begin_document.rb +22 -0
- data/lib/fn/pdf/node/begin_page_ext.rb +24 -0
- data/lib/fn/pdf/node/create_textflow.rb +137 -0
- data/lib/fn/pdf/node/end_page_ext.rb +22 -0
- data/lib/fn/pdf/node/fit_image.rb +28 -0
- data/lib/fn/pdf/node/fit_pdi_page.rb +26 -0
- data/lib/fn/pdf/node/fit_textflow.rb +27 -0
- data/lib/fn/pdf/node/invert.rb +36 -0
- data/lib/fn/pdf/node/load_image.rb +21 -0
- data/lib/fn/pdf/node/open_pdi.rb +22 -0
- data/lib/fn/pdf/node/open_pdi_page.rb +31 -0
- data/lib/fn/pdf/node/resume_page.rb +21 -0
- data/lib/fn/pdf/node/set_parameter.rb +22 -0
- data/lib/fn/pdf/node/watermark.rb +24 -0
- data/lib/fn/pdf/struct.rb +40 -0
- data/lib/fn/pdf/writer.rb +205 -0
- data/lib/fn/resource.rb +53 -0
- data/lib/fn/swf/node/break.rb +19 -0
- data/lib/fn/swf/node/flash.rb +24 -0
- data/lib/fn/swf/node/font.rb +24 -0
- data/lib/fn/swf/node/frame.rb +19 -0
- data/lib/fn/swf/node/hot_spot.rb +43 -0
- data/lib/fn/swf/node/image.rb +19 -0
- data/lib/fn/swf/node/page.rb +30 -0
- data/lib/fn/swf/node/photo_block.rb +53 -0
- data/lib/fn/swf/node/text.rb +51 -0
- data/lib/fn/swf/struct.rb +43 -0
- data/lib/fn/swf/writer.rb +89 -0
- data/lib/fn/util.rb +27 -0
- data/lib/fn/validation.rng +172 -0
- data/lib/fn_document.rb +3 -0
- data/test/helper.rb +18 -0
- data/test/test_fn_document.rb +7 -0
- metadata +150 -0
data/lib/fn/migrate.xslt
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
3
|
+
xmlns:func="http://exslt.org/functions" xmlns:math="http://exslt.org/math" xmlns:str="http://exslt.org/strings"
|
4
|
+
xmlns:dyn="http://exslt.org/dynamic"
|
5
|
+
extension-element-prefixes="func math str dyn">
|
6
|
+
<xsl:output indent="yes" />
|
7
|
+
|
8
|
+
<!-- User variables -->
|
9
|
+
<xsl:variable name="document_version" select="2.0" />
|
10
|
+
<xsl:variable name="insert_sample_menu" select="true()" />
|
11
|
+
|
12
|
+
<func:function name="func:dec2hex">
|
13
|
+
<xsl:param name="dec" />
|
14
|
+
<xsl:param name="level" select="0" />
|
15
|
+
<xsl:variable name="divisor" select="math:power(16, $level)"/>
|
16
|
+
<xsl:variable name="digit" select="floor($dec div $divisor) mod 16"/>
|
17
|
+
<xsl:variable name="s" select="substring('0123456789ABCDEF', $digit + 1, 1)"/>
|
18
|
+
<xsl:choose>
|
19
|
+
<xsl:when test="$level=6">
|
20
|
+
<func:result select="'#'" />
|
21
|
+
</xsl:when>
|
22
|
+
<xsl:otherwise>
|
23
|
+
<func:result select="concat(func:dec2hex($dec, $level + 1), $s)" />
|
24
|
+
</xsl:otherwise>
|
25
|
+
</xsl:choose>
|
26
|
+
</func:function>
|
27
|
+
|
28
|
+
<xsl:template match="document">
|
29
|
+
<xsl:copy>
|
30
|
+
<xsl:attribute name="version"><xsl:value-of select="$document_version" /></xsl:attribute>
|
31
|
+
|
32
|
+
<resources>
|
33
|
+
<xsl:apply-templates select="." mode="resources" />
|
34
|
+
</resources>
|
35
|
+
|
36
|
+
<menu>
|
37
|
+
<!-- We usually assign dynamically -->
|
38
|
+
<xsl:if test="$insert_sample_menu">
|
39
|
+
<item label="File">
|
40
|
+
<item label="New" url="template.xml" action="redirect" />
|
41
|
+
<item label="Open" url="fixtures/document-list.xml" action="open" />
|
42
|
+
<item label="Save" url="save.do" action="save" />
|
43
|
+
<item label="Save as.." url="test/fixtures/dummyaction" action="saveas" />
|
44
|
+
<item label="Quit" url="javascript:window.close()" action="redirect" />
|
45
|
+
</item>
|
46
|
+
<item label="Edit">
|
47
|
+
<xsl:apply-templates select="style"/>
|
48
|
+
</item>
|
49
|
+
<item label="Output">
|
50
|
+
<item label="Publish" url="publish.do" action="send" />
|
51
|
+
</item>
|
52
|
+
<item label="Compliance">
|
53
|
+
<item label="Send!" url="compliance.do" action="send" />
|
54
|
+
</item>
|
55
|
+
</xsl:if>
|
56
|
+
</menu>
|
57
|
+
|
58
|
+
<pages>
|
59
|
+
<xsl:apply-templates select="node()[name() != 'style']" mode="pages" />
|
60
|
+
</pages>
|
61
|
+
<texts>
|
62
|
+
<xsl:apply-templates select="node()[name() != 'style']" mode="texts"/>
|
63
|
+
</texts>
|
64
|
+
</xsl:copy>
|
65
|
+
</xsl:template>
|
66
|
+
|
67
|
+
<xsl:template match="document" mode="resources">
|
68
|
+
<xsl:for-each select="//page">
|
69
|
+
<xsl:variable name="n" select="concat('bkg', @number)" />
|
70
|
+
<resource type="bkg"><xsl:attribute name="id"><xsl:value-of select="$n" /></xsl:attribute></resource>
|
71
|
+
</xsl:for-each>
|
72
|
+
<xsl:for-each select="//style">
|
73
|
+
<resource type="font"><xsl:attribute name="id"><xsl:value-of select="@font" /></xsl:attribute></resource>
|
74
|
+
</xsl:for-each>
|
75
|
+
<xsl:for-each select="//block[starts-with(@name,'photo')]">
|
76
|
+
<resource type="photo"><xsl:attribute name="id"><xsl:value-of select="@contents" /></xsl:attribute></resource>
|
77
|
+
</xsl:for-each>
|
78
|
+
</xsl:template>
|
79
|
+
|
80
|
+
<xsl:template match="page" mode="pages">
|
81
|
+
<xsl:copy>
|
82
|
+
<xsl:attribute name="background"><xsl:value-of select="concat('bkg', @number)" /></xsl:attribute>
|
83
|
+
<xsl:apply-templates select="@*|node()" mode="pages" />
|
84
|
+
</xsl:copy>
|
85
|
+
</xsl:template>
|
86
|
+
|
87
|
+
<xsl:template match="block[starts-with(@name, 'photo')]" mode="pages">
|
88
|
+
<!-- <block actualwidth="90" name="photo3" x="487" y="710" width="90" height="37" contents="company_logo" anchor="1" pdfxheight="0" static="yes"/> -->
|
89
|
+
<!-- to -->
|
90
|
+
<!-- <block type="photo" id="3" boxX="0" boxY="220" boxWidth="362" boxHeight="129" width="170" src="photo" align="middlecenter" /> -->
|
91
|
+
<block>
|
92
|
+
<xsl:attribute name="type">photo</xsl:attribute>
|
93
|
+
<xsl:attribute name="id"><xsl:value-of select="substring(@name, 6)"/></xsl:attribute>
|
94
|
+
<xsl:attribute name="src"><xsl:value-of select="@contents"/></xsl:attribute>
|
95
|
+
<xsl:attribute name="static"><xsl:value-of select="@static"/></xsl:attribute>
|
96
|
+
<xsl:attribute name="multipage"><xsl:value-of select="@multipage"/></xsl:attribute>
|
97
|
+
<xsl:attribute name="width"><xsl:value-of select="@actualwidth"/></xsl:attribute>
|
98
|
+
<xsl:attribute name="boxX"><xsl:value-of select="@x"/></xsl:attribute>
|
99
|
+
<xsl:attribute name="boxY"><xsl:value-of select="@y"/></xsl:attribute>
|
100
|
+
<xsl:attribute name="boxWidth"><xsl:value-of select="@width"/></xsl:attribute>
|
101
|
+
<xsl:attribute name="boxHeight"><xsl:value-of select="@height"/></xsl:attribute>
|
102
|
+
<xsl:choose>
|
103
|
+
<xsl:when test="@anchor='0'"><xsl:attribute name="align">topleft</xsl:attribute></xsl:when>
|
104
|
+
<xsl:when test="@anchor='1'"><xsl:attribute name="align">bottomleft</xsl:attribute></xsl:when>
|
105
|
+
<xsl:when test="@anchor='2'"><xsl:attribute name="align">topright</xsl:attribute></xsl:when>
|
106
|
+
<xsl:when test="@anchor='3'"><xsl:attribute name="align">bottomright</xsl:attribute></xsl:when>
|
107
|
+
<xsl:when test="@anchor='4'"><xsl:attribute name="align">middlecenter</xsl:attribute></xsl:when>
|
108
|
+
</xsl:choose>
|
109
|
+
</block>
|
110
|
+
</xsl:template>
|
111
|
+
|
112
|
+
<xsl:template match="block[starts-with(@name, 'text')]" mode="pages">
|
113
|
+
<!-- <block name="text2_2" x="322" y="348" width="252" height="340" pdfxheight="4.5" /> -->
|
114
|
+
<!-- to -->
|
115
|
+
<!-- <block type="text" x="250" y="145" width="385" height="229" text="1" index="2"> -->
|
116
|
+
<block>
|
117
|
+
<xsl:attribute name="type">text</xsl:attribute>
|
118
|
+
<xsl:attribute name="text"><xsl:value-of select="substring(str:split(@name, '_')[1], 5)"/></xsl:attribute>
|
119
|
+
<xsl:attribute name="static"><xsl:value-of select="@static"/></xsl:attribute>
|
120
|
+
<xsl:attribute name="index"><xsl:value-of select="str:split(@name, '_')[2]"/></xsl:attribute>
|
121
|
+
<xsl:attribute name="x"><xsl:value-of select="@x"/></xsl:attribute>
|
122
|
+
<xsl:attribute name="y"><xsl:value-of select="@y"/></xsl:attribute>
|
123
|
+
<xsl:attribute name="width"><xsl:value-of select="@width"/></xsl:attribute>
|
124
|
+
<xsl:attribute name="height"><xsl:value-of select="@height"/></xsl:attribute>
|
125
|
+
<xsl:attribute name="multipage"><xsl:value-of select="@multipage"/></xsl:attribute>
|
126
|
+
</block>
|
127
|
+
</xsl:template>
|
128
|
+
|
129
|
+
<!-- Ignore these elements/attributes -->
|
130
|
+
<xsl:template match="content" mode="pages" />
|
131
|
+
|
132
|
+
<xsl:template match="content" mode="texts">
|
133
|
+
<text>
|
134
|
+
<xsl:attribute name="id"><xsl:value-of select="@number"/></xsl:attribute>
|
135
|
+
<xsl:apply-templates select="p" mode="p"/>
|
136
|
+
</text>
|
137
|
+
</xsl:template>
|
138
|
+
|
139
|
+
<xsl:template match="p" mode="p">
|
140
|
+
<xsl:variable name="class" select="@class" />
|
141
|
+
<xsl:variable name="style" select="//style[@name=$class]" />
|
142
|
+
<p>
|
143
|
+
<xsl:attribute name="align"><xsl:choose><xsl:when test="@center='1'">center</xsl:when><xsl:otherwise>left</xsl:otherwise></xsl:choose></xsl:attribute>
|
144
|
+
<xsl:apply-templates select="." mode="b"/>
|
145
|
+
</p>
|
146
|
+
</xsl:template>
|
147
|
+
|
148
|
+
<xsl:template match="p" mode="b">
|
149
|
+
<xsl:variable name="class" select="@class" />
|
150
|
+
<xsl:variable name="style" select="//style[@name=$class]" />
|
151
|
+
<xsl:choose>
|
152
|
+
<xsl:when test="$style/@bold='1' and text()[normalize-space(.)]">
|
153
|
+
<b><xsl:apply-templates select="." mode="i"/></b>
|
154
|
+
</xsl:when>
|
155
|
+
<xsl:otherwise>
|
156
|
+
<xsl:apply-templates select="." mode="i"/>
|
157
|
+
</xsl:otherwise>
|
158
|
+
</xsl:choose>
|
159
|
+
</xsl:template>
|
160
|
+
|
161
|
+
<xsl:template match="p" mode="i">
|
162
|
+
<xsl:variable name="class" select="@class" />
|
163
|
+
<xsl:variable name="style" select="//style[@name=$class]" />
|
164
|
+
<xsl:choose>
|
165
|
+
<xsl:when test="$style/@italic='1' and text()[normalize-space(.)]">
|
166
|
+
<i><xsl:apply-templates select="." mode="etc"/></i>
|
167
|
+
</xsl:when>
|
168
|
+
<xsl:otherwise>
|
169
|
+
<xsl:apply-templates select="." mode="etc"/>
|
170
|
+
</xsl:otherwise>
|
171
|
+
</xsl:choose>
|
172
|
+
</xsl:template>
|
173
|
+
|
174
|
+
<xsl:template match="p" mode="etc">
|
175
|
+
<xsl:variable name="class" select="@class" />
|
176
|
+
<xsl:variable name="style" select="//style[@name=$class]" />
|
177
|
+
<font>
|
178
|
+
<xsl:attribute name="size"><xsl:value-of select="$style/@size" /></xsl:attribute>
|
179
|
+
<xsl:attribute name="face"><xsl:value-of select="translate($style/@font, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" /></xsl:attribute>
|
180
|
+
<xsl:attribute name="color"><xsl:value-of select="func:dec2hex($style/@color)" /></xsl:attribute>
|
181
|
+
<textformat>
|
182
|
+
<xsl:value-of select="." />
|
183
|
+
</textformat>
|
184
|
+
</font>
|
185
|
+
</xsl:template>
|
186
|
+
|
187
|
+
<xsl:template match="text()" mode="innerxml"><xsl:value-of select="."/></xsl:template>
|
188
|
+
<xsl:template match="node()" mode="innerxml"><<xsl:value-of select="name()"/><xsl:apply-templates select="@*" mode="innerxml"/>><xsl:apply-templates select="text()" mode="innerxml"/></<xsl:value-of select="name()"/>></xsl:template>
|
189
|
+
<xsl:template match="@*" mode="innerxml"><xsl:text> </xsl:text><xsl:value-of select="name()"/>="<xsl:value-of select="."/>"</xsl:template>
|
190
|
+
|
191
|
+
<xsl:template match="style">
|
192
|
+
<item>
|
193
|
+
<!-- Label -->
|
194
|
+
<xsl:attribute name="label"><xsl:value-of select="@name" /></xsl:attribute>
|
195
|
+
<xsl:attribute name="action">format</xsl:attribute>
|
196
|
+
|
197
|
+
<!-- Boolean attributes -->
|
198
|
+
<xsl:attribute name="bold"><xsl:choose><xsl:when test="@bold='1'">true</xsl:when><xsl:otherwise>false</xsl:otherwise></xsl:choose></xsl:attribute>
|
199
|
+
<xsl:attribute name="italic"><xsl:choose><xsl:when test="@italic='1'">true</xsl:when><xsl:otherwise>false</xsl:otherwise></xsl:choose></xsl:attribute>
|
200
|
+
|
201
|
+
<!-- Integer attributes -->
|
202
|
+
<xsl:attribute name="color"><xsl:value-of select="@color" /></xsl:attribute>
|
203
|
+
<xsl:attribute name="leading"><xsl:value-of select="@leading" /></xsl:attribute>
|
204
|
+
<xsl:attribute name="leftMargin"><xsl:value-of select="@x" /></xsl:attribute>
|
205
|
+
<xsl:attribute name="size"><xsl:value-of select="@size" /></xsl:attribute>
|
206
|
+
|
207
|
+
<!-- String attributes -->
|
208
|
+
<xsl:attribute name="align"><xsl:choose><xsl:when test="@center='1'">center</xsl:when><xsl:otherwise>left</xsl:otherwise></xsl:choose></xsl:attribute>
|
209
|
+
<xsl:attribute name="font"><xsl:value-of select="translate(@font, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" /></xsl:attribute>
|
210
|
+
</item>
|
211
|
+
</xsl:template>
|
212
|
+
|
213
|
+
<xsl:template match="@*|node()" mode="pages">
|
214
|
+
<xsl:copy>
|
215
|
+
<xsl:apply-templates select="@*|node()"/>
|
216
|
+
</xsl:copy>
|
217
|
+
</xsl:template>
|
218
|
+
|
219
|
+
<xsl:template match="@*|node()">
|
220
|
+
<xsl:copy>
|
221
|
+
<xsl:apply-templates select="@*|node()"/>
|
222
|
+
</xsl:copy>
|
223
|
+
</xsl:template>
|
224
|
+
</xsl:stylesheet>
|
data/lib/fn/node/base.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../util"
|
2
|
+
module FN
|
3
|
+
module Node
|
4
|
+
def self.Base(name, attributes = {})
|
5
|
+
n = XML::Node.new(name)
|
6
|
+
attributes.each {|k, v| n[k.to_s] = Base.value(v) }
|
7
|
+
n.extend(Base)
|
8
|
+
return n
|
9
|
+
end
|
10
|
+
|
11
|
+
module Base
|
12
|
+
|
13
|
+
CURRENT_PAGE_WIDTH = "__current_page_width"
|
14
|
+
CURRENT_PAGE_HEIGHT = "__current_page_height"
|
15
|
+
|
16
|
+
def visit(struct)
|
17
|
+
visit_children(struct)
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_no_children
|
21
|
+
@logger = Logger.new("#{RAILS_ROOT}/log/pdf_writer_logs/#{Time.now.strftime('pdf_writer_log_%Y_%m_%d')}.log")
|
22
|
+
if children.any?{|c| !c.cdata?}
|
23
|
+
# @logger.info "HAS NO CHILDREN "*88
|
24
|
+
raise "should have no children"
|
25
|
+
else
|
26
|
+
# @logger.info "This should not have any children and it does not"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_children(struct)
|
31
|
+
children.each do |c|
|
32
|
+
if c.element?
|
33
|
+
mixin(c)
|
34
|
+
c.visit(struct)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return struct
|
38
|
+
end
|
39
|
+
|
40
|
+
def mixin(node)
|
41
|
+
name = classify(node.name)
|
42
|
+
mod = FN::SWF::Node.const_get(name) rescue
|
43
|
+
FN::PDF::Node.const_get(name)
|
44
|
+
node.extend(mod)
|
45
|
+
end
|
46
|
+
|
47
|
+
def classify(name)
|
48
|
+
name.split("_").map{|s| s.capitalize}.join("")
|
49
|
+
end
|
50
|
+
|
51
|
+
def with_attributes_like(node)
|
52
|
+
node.attributes.each do |attr|
|
53
|
+
self[attr.name] = attr.value
|
54
|
+
end
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def value(v)
|
59
|
+
case v
|
60
|
+
when Array: v.map{|e| value(e)}.join(" ")
|
61
|
+
else
|
62
|
+
v.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
extend self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "PDFlib"
|
2
|
+
Dir[File.dirname(__FILE__) + "/node/*.rb"].each do |f|
|
3
|
+
require f.sub(/\.rb$/, '')
|
4
|
+
end
|
5
|
+
module FN
|
6
|
+
module Node
|
7
|
+
class Context
|
8
|
+
attr_reader :doc
|
9
|
+
attr_accessor :current
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@doc = XML::Document.new
|
13
|
+
@doc.root = @current = FN::Node::Root()
|
14
|
+
end
|
15
|
+
|
16
|
+
def <<(node)
|
17
|
+
add node
|
18
|
+
@current = node
|
19
|
+
end
|
20
|
+
|
21
|
+
def root
|
22
|
+
doc.root
|
23
|
+
end
|
24
|
+
|
25
|
+
def root?
|
26
|
+
current == root
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(node)
|
30
|
+
@current << node
|
31
|
+
end
|
32
|
+
|
33
|
+
def retain_after
|
34
|
+
old = @current
|
35
|
+
yield self
|
36
|
+
@current = old
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit(struct)
|
40
|
+
root.visit(struct)
|
41
|
+
struct
|
42
|
+
end
|
43
|
+
|
44
|
+
def pre(item)
|
45
|
+
if @current.first?
|
46
|
+
@current.first.prev = item
|
47
|
+
else
|
48
|
+
@current << item
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def inject_at_head(item)
|
53
|
+
head = @doc.find_first("//begin_document")
|
54
|
+
if head.first?
|
55
|
+
head.first.prev = item
|
56
|
+
else
|
57
|
+
head << item
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def inject_at_page(number)
|
62
|
+
old = @current
|
63
|
+
@current = @doc.find_first("//begin_page_ext[@number='#{number}']") or
|
64
|
+
raise "page not found: #{number}. Pages: #{@doc.find('//begin_page_ext').map{|n|n.to_s}.inspect}"
|
65
|
+
yield self
|
66
|
+
@current = old
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/fn/node/root.rb
ADDED
@@ -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 BeginDocument(file, compatibility)
|
8
|
+
FN::Node::Base("begin_document", :file => file, :compatibility => compatibility).extend(BeginDocument)
|
9
|
+
end
|
10
|
+
|
11
|
+
module BeginDocument
|
12
|
+
include FN::Node::Base
|
13
|
+
|
14
|
+
def visit(struct)
|
15
|
+
struct.begin_document(self[:file], :compatibility => self[:compatibility])
|
16
|
+
visit_children(struct)
|
17
|
+
struct.end_document("")
|
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 BeginPageExt(width, height, number)
|
8
|
+
FN::Node::Base("begin_page_ext", :width => width, :height => height, :number => number).extend(BeginPageExt)
|
9
|
+
end
|
10
|
+
|
11
|
+
module BeginPageExt
|
12
|
+
include FN::Node::Base
|
13
|
+
|
14
|
+
def visit(struct)
|
15
|
+
struct.begin_page_ext(self[:width].to_i, self[:height].to_i, "")
|
16
|
+
struct[CURRENT_PAGE_WIDTH] = self[:width].to_i
|
17
|
+
struct[CURRENT_PAGE_HEIGHT] = self[:height].to_i
|
18
|
+
visit_children struct
|
19
|
+
struct.suspend_page("")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../node/base"
|
2
|
+
require "PDFlib"
|
3
|
+
module FN
|
4
|
+
module PDF
|
5
|
+
module Node
|
6
|
+
def CreateTextflow(node)
|
7
|
+
out = FN::Node::Base("create_textflow", :assigns => CreateTextflow.flow_name(node))
|
8
|
+
out << XML::Node.new_cdata(CreateTextflow.format(node))
|
9
|
+
out.extend(CreateTextflow)
|
10
|
+
end
|
11
|
+
|
12
|
+
module CreateTextflow
|
13
|
+
include FN::Node::Base
|
14
|
+
NL = /\r|\n/
|
15
|
+
SPACE = /\s+/
|
16
|
+
|
17
|
+
BASE_FORMAT = {
|
18
|
+
"face" => "Arial",
|
19
|
+
"size" => "12",
|
20
|
+
"blockindent" => "0",
|
21
|
+
"indent" => "0",
|
22
|
+
"leftmargin" => "0",
|
23
|
+
"rightmargin" => "0",
|
24
|
+
"leading" => "2",
|
25
|
+
"color" => "#0000FF",
|
26
|
+
"align" => "left",
|
27
|
+
"bold" => false,
|
28
|
+
"italic" => false,
|
29
|
+
"underline" => false
|
30
|
+
}
|
31
|
+
|
32
|
+
def flow_name(node = nil)
|
33
|
+
if node
|
34
|
+
if node["id"]
|
35
|
+
"flow-#{node["id"]}"
|
36
|
+
else
|
37
|
+
"flow-#{node["text"]}-#{node["index"]}"
|
38
|
+
end
|
39
|
+
else
|
40
|
+
self["assigns"]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def empty?
|
45
|
+
inner_text.gsub(/<[^>]*>|\s+/, '').empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def inner_text
|
49
|
+
child.to_s.gsub("<![CDATA[", "").gsub("]]>", "")
|
50
|
+
end
|
51
|
+
|
52
|
+
def visit(struct)
|
53
|
+
tf = struct.create_textflow(inner_text, "")
|
54
|
+
struct.assigns self, tf
|
55
|
+
end
|
56
|
+
|
57
|
+
def formatter(options)
|
58
|
+
attrs = options.dup
|
59
|
+
attrs["leftindent"] = (attrs["blockindent"].to_f + attrs["leftmargin"].to_f).to_i
|
60
|
+
attrs["totalleading"] = (attrs["size"].to_f + attrs["leading"].to_f).to_i
|
61
|
+
# logger.warn attrs.inspect
|
62
|
+
attrs["formatted_color"] = format_color(attrs["color"])
|
63
|
+
attrs["formatted_face"] = format_face(attrs)
|
64
|
+
%(<encoding=unicode
|
65
|
+
embedding=true
|
66
|
+
adjustmethod=nofit
|
67
|
+
nofitlimit=100%
|
68
|
+
minspacing=100%
|
69
|
+
maxspacing=100%
|
70
|
+
kerning=true
|
71
|
+
charref=true
|
72
|
+
fontname={#{attrs["formatted_face"]}}
|
73
|
+
fontsize=#{attrs['size']}
|
74
|
+
leftindent=#{attrs['leftindent']}
|
75
|
+
rightindent=#{attrs['rightmargin']}
|
76
|
+
leading=#{attrs["totalleading"]}
|
77
|
+
fillcolor={rgb #{attrs["formatted_color"]}}
|
78
|
+
parindent=#{attrs["indent"]}
|
79
|
+
underline=#{attrs['underline']}
|
80
|
+
alignment=#{attrs["align"]}>).gsub(SPACE, ' ')
|
81
|
+
end
|
82
|
+
|
83
|
+
def format_face(attrs)
|
84
|
+
# titlecase
|
85
|
+
face = attrs["face"].split(SPACE).map{|s| s.capitalize }.join(" ")
|
86
|
+
face += ",Bold" if attrs["bold"]
|
87
|
+
face += ",Italic" if attrs["italic"]
|
88
|
+
return face
|
89
|
+
end
|
90
|
+
|
91
|
+
def format_color(hex)
|
92
|
+
hex.scan(/[\da-f]{2}/i).map{|s| s.to_i(16) / 255.0 }.join(" ")
|
93
|
+
end
|
94
|
+
|
95
|
+
def format(node, buffer = "<fontname=Arial encoding=#{Writer.encoding} fontsize=10>", options = BASE_FORMAT)
|
96
|
+
return nil unless node
|
97
|
+
|
98
|
+
options = options.dup
|
99
|
+
if node.text?
|
100
|
+
buffer << formatter(options)
|
101
|
+
buffer << node.to_s.gsub(NL, '')
|
102
|
+
elsif node.element?
|
103
|
+
case node.name.downcase
|
104
|
+
when "br"; buffer << "<nextline>"
|
105
|
+
when "b"; options["bold"] = true
|
106
|
+
when "i"; options["italic"] = true
|
107
|
+
when "u"; options["underline"] = true
|
108
|
+
when "li"; buffer << "• "
|
109
|
+
when "p";
|
110
|
+
options["align"] = node["ALIGN"] if node["ALIGN"]
|
111
|
+
when "font";
|
112
|
+
options["size"] = node["size"] if node["size"]
|
113
|
+
options["face"] = node["face"] if node["face"]
|
114
|
+
options["color"] = node["color"] if node["color"]
|
115
|
+
|
116
|
+
options["size"] = node["SIZE"] if node["SIZE"]
|
117
|
+
options["face"] = node["FACE"] if node["FACE"]
|
118
|
+
options["color"] = node["COLOR"] if node["COLOR"]
|
119
|
+
end
|
120
|
+
|
121
|
+
node.children.each{|c| format(c, buffer, options)}
|
122
|
+
|
123
|
+
case node.name.downcase
|
124
|
+
when "p", "li";
|
125
|
+
buffer << "<nextparagraph>"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
raise "unhandled node type"
|
129
|
+
end
|
130
|
+
return buffer
|
131
|
+
end
|
132
|
+
|
133
|
+
extend self
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|