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,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">&lt;<xsl:value-of select="name()"/><xsl:apply-templates select="@*" mode="innerxml"/>&gt;<xsl:apply-templates select="text()" mode="innerxml"/>&lt;/<xsl:value-of select="name()"/>&gt;</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>
@@ -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
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + "/base"
2
+ require "PDFlib"
3
+ module FN
4
+ module Node
5
+ def self.Root
6
+ Base("root")
7
+ end
8
+
9
+ module Root
10
+ include FN::Node::Base
11
+
12
+ end
13
+ end
14
+ 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 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 << "&bull;&nbsp;"
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