janie-htmltoword 1.1.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.
@@ -0,0 +1,17 @@
1
+ module Htmltoword
2
+ module XSLTHelper
3
+ def document_xslt(extras = false)
4
+ file_name = extras ? 'htmltoword' : 'base'
5
+ xslt_path(file_name)
6
+ end
7
+
8
+ def xslt_path(template_name)
9
+ File.join(Htmltoword.config.default_xslt_path, "#{template_name}.xslt")
10
+ end
11
+
12
+ def xslt(stylesheet_name: nil, stylesheet_path: nil)
13
+ return Nokogiri::XSLT(File.open(stylesheet_path)) if stylesheet_path
14
+ Nokogiri::XSLT(File.open(xslt_path(stylesheet_name)))
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Htmltoword
2
+ class Railtie < ::Rails::Railtie
3
+ initializer 'htmltoword.setup' do
4
+ if defined?(Mime) and Mime[:docx].nil?
5
+ Mime::Type.register 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', :docx
6
+ end
7
+
8
+ ActionController::Renderers.add :docx do |file_name, options|
9
+ Htmltoword::Renderer.send_file(self, file_name, options)
10
+ end
11
+
12
+ if defined? ActionController::Responder
13
+ ActionController::Responder.class_eval do
14
+ def to_docx
15
+ if @default_response
16
+ @default_response.call(options)
17
+ else
18
+ controller.render({ docx: controller.action_name }.merge(options))
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ module Htmltoword
2
+ class Renderer
3
+ class << self
4
+ def send_file(context, filename, options = {})
5
+ new(context, filename, options).send_file
6
+ end
7
+ end
8
+
9
+ def initialize(context, filename, options)
10
+ @word_template = options[:word_template].presence
11
+ @disposition = options.fetch(:disposition, 'attachment')
12
+ @use_extras = options.fetch(:extras, false)
13
+ @file_name = file_name(filename, options)
14
+ @context = context
15
+ define_template(filename, options)
16
+ @content = options[:content] || @context.render_to_string(options)
17
+ end
18
+
19
+ def send_file
20
+ document = Htmltoword::Document.create(@content, @word_template, @use_extras)
21
+ @context.send_data(document, filename: @file_name, type: Mime[:docx], disposition: @disposition)
22
+ end
23
+
24
+ private
25
+
26
+ def define_template(filename, options)
27
+ if options[:template] == @context.action_name
28
+ if filename =~ %r{^([^\/]+)/(.+)$}
29
+ options[:prefixes] ||= []
30
+ options[:prefixes].unshift $1
31
+ options[:template] = $2
32
+ else
33
+ options[:template] = filename
34
+ end
35
+ end
36
+ end
37
+
38
+ def file_name(filename, options)
39
+ name = options[:filename].presence || filename
40
+ name =~ /\.docx$/ ? name : "#{name}.docx"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module Htmltoword
2
+ VERSION = '1.1.2'
3
+ end
@@ -0,0 +1,363 @@
1
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
2
+ xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
3
+ xmlns:o="urn:schemas-microsoft-com:office:office"
4
+ xmlns:v="urn:schemas-microsoft-com:vml"
5
+ xmlns:WX="http://schemas.microsoft.com/office/word/2003/auxHint"
6
+ xmlns:aml="http://schemas.microsoft.com/aml/2001/core"
7
+ xmlns:w10="urn:schemas-microsoft-com:office:word"
8
+ xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"
9
+ xmlns:msxsl="urn:schemas-microsoft-com:xslt"
10
+ xmlns:ext="http://www.xmllab.net/wordml2html/ext"
11
+ xmlns:java="http://xml.apache.org/xalan/java"
12
+ xmlns:str="http://exslt.org/strings"
13
+ xmlns:func="http://exslt.org/functions"
14
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
15
+ version="1.0"
16
+ exclude-result-prefixes="java msxsl ext w o v WX aml w10"
17
+ extension-element-prefixes="func">
18
+ <xsl:output method="xml" encoding="utf-8" omit-xml-declaration="yes" indent="yes" />
19
+ <xsl:include href="./functions.xslt"/>
20
+ <xsl:include href="./tables.xslt"/>
21
+ <xsl:include href="./links.xslt"/>
22
+ <xsl:include href="./images.xslt"/>
23
+ <xsl:template match="/">
24
+ <xsl:apply-templates />
25
+ </xsl:template>
26
+
27
+ <xsl:template match="head" />
28
+
29
+ <xsl:template match="body">
30
+ <xsl:comment>
31
+ KNOWN BUGS:
32
+ div
33
+ h2
34
+ div
35
+ textnode (WONT BE WRAPPED IN A W:P)
36
+ div
37
+ table
38
+ span
39
+ text
40
+ </xsl:comment>
41
+ <xsl:apply-templates/>
42
+ </xsl:template>
43
+
44
+ <xsl:template match="body/*[not(*)]">
45
+ <w:p>
46
+ <xsl:call-template name="text-alignment" />
47
+ <w:r>
48
+ <w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
49
+ </w:r>
50
+ </w:p>
51
+ </xsl:template>
52
+
53
+
54
+ <xsl:template match="br[not(ancestor::p) and not(ancestor::div) and not(ancestor::td|ancestor::li) or
55
+ (preceding-sibling::div or following-sibling::div or preceding-sibling::p or following-sibling::p)]">
56
+ <w:p>
57
+ <w:r></w:r>
58
+ </w:p>
59
+ </xsl:template>
60
+
61
+ <xsl:template match="br[(ancestor::li or ancestor::td) and
62
+ (preceding-sibling::div or following-sibling::div or preceding-sibling::p or following-sibling::p)]">
63
+ <w:r>
64
+ <w:br />
65
+ </w:r>
66
+ </xsl:template>
67
+
68
+ <xsl:template match="br">
69
+ <w:r>
70
+ <w:br />
71
+ </w:r>
72
+ </xsl:template>
73
+
74
+ <xsl:template match="pre">
75
+ <w:p>
76
+ <xsl:apply-templates />
77
+ </w:p>
78
+ </xsl:template>
79
+
80
+ <xsl:template match="div[not(ancestor::li) and not(ancestor::td) and not(ancestor::th) and not(ancestor::p) and not(ancestor::dl) and not(descendant::dl) and not(descendant::div) and not(descendant::p) and not(descendant::h1) and not(descendant::h2) and not(descendant::h3) and not(descendant::h4) and not(descendant::h5) and not(descendant::h6) and not(descendant::table) and not(descendant::li) and not (descendant::pre)]">
81
+ <xsl:comment>Divs should create a p if nothing above them has and nothing below them will</xsl:comment>
82
+ <w:p>
83
+ <xsl:call-template name="text-alignment" />
84
+ <xsl:apply-templates />
85
+ </w:p>
86
+ </xsl:template>
87
+
88
+ <xsl:template match="div">
89
+ <xsl:apply-templates />
90
+ </xsl:template>
91
+
92
+ <!-- TODO: make this prettier. Headings shouldn't enter in template from L51 -->
93
+ <xsl:template match="body/h1|body/h2|body/h3|body/h4|body/h5|body/h6|h1|h2|h3|h4|h5|h6">
94
+ <xsl:variable name="length" select="string-length(name(.))"/>
95
+ <w:p>
96
+ <w:pPr>
97
+ <w:pStyle w:val="Heading{substring(name(.),$length)}"/>
98
+ </w:pPr>
99
+ <w:r>
100
+ <w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
101
+ </w:r>
102
+ </w:p>
103
+ </xsl:template>
104
+
105
+ <xsl:template match="p[not(ancestor::li)]">
106
+ <w:p>
107
+ <xsl:call-template name="text-alignment" />
108
+ <xsl:apply-templates />
109
+ </w:p>
110
+ </xsl:template>
111
+
112
+ <xsl:template match="ol|ul">
113
+ <xsl:param name="global_level" select="count(preceding::ol[not(ancestor::ol or ancestor::ul)]) + count(preceding::ul[not(ancestor::ol or ancestor::ul)]) + 1"/>
114
+ <xsl:apply-templates>
115
+ <xsl:with-param name="global_level" select="$global_level" />
116
+ </xsl:apply-templates>
117
+ </xsl:template>
118
+
119
+ <xsl:template name="listItem" match="li">
120
+ <xsl:param name="global_level" />
121
+ <xsl:param name="preceding-siblings" select="0"/>
122
+ <xsl:for-each select="node()">
123
+ <xsl:choose>
124
+ <xsl:when test="self::br">
125
+ <w:p>
126
+ <w:pPr>
127
+ <w:pStyle w:val="ListParagraph"></w:pStyle>
128
+ <w:numPr>
129
+ <w:ilvl w:val="0"/>
130
+ <w:numId w:val="0"/>
131
+ </w:numPr>
132
+ </w:pPr>
133
+ <w:r></w:r>
134
+ </w:p>
135
+ </xsl:when>
136
+ <xsl:when test="self::ol|self::ul">
137
+ <xsl:apply-templates>
138
+ <xsl:with-param name="global_level" select="$global_level" />
139
+ </xsl:apply-templates>
140
+ </xsl:when>
141
+ <xsl:when test="not(descendant::li)"> <!-- simpler div, p, headings, etc... -->
142
+ <xsl:variable name="ilvl" select="count(ancestor::ol) + count(ancestor::ul) - 1"></xsl:variable>
143
+ <xsl:choose>
144
+ <xsl:when test="$preceding-siblings + count(preceding-sibling::*) > 0">
145
+ <w:p>
146
+ <w:pPr>
147
+ <w:pStyle w:val="ListParagraph"></w:pStyle>
148
+ <w:numPr>
149
+ <w:ilvl w:val="0"/>
150
+ <w:numId w:val="0"/>
151
+ </w:numPr>
152
+ <w:ind w:left="{720 * ($ilvl + 1)}"/>
153
+ </w:pPr>
154
+ <xsl:apply-templates/>
155
+ </w:p>
156
+ </xsl:when>
157
+ <xsl:otherwise>
158
+ <w:p>
159
+ <w:pPr>
160
+ <w:pStyle w:val="ListParagraph"></w:pStyle>
161
+ <w:numPr>
162
+ <w:ilvl w:val="{$ilvl}"/>
163
+ <w:numId w:val="{$global_level}"/>
164
+ </w:numPr>
165
+ </w:pPr>
166
+ <xsl:apply-templates/>
167
+ </w:p>
168
+ </xsl:otherwise>
169
+ </xsl:choose>
170
+ </xsl:when>
171
+ <xsl:otherwise> <!-- mixed things div having list and stuff content... -->
172
+ <xsl:call-template name="listItem">
173
+ <xsl:with-param name="global_level" select="$global_level" />
174
+ <xsl:with-param name="preceding-siblings" select="$preceding-siblings + count(preceding-sibling::*)" />
175
+ </xsl:call-template>
176
+ </xsl:otherwise>
177
+ </xsl:choose>
178
+ </xsl:for-each>
179
+ </xsl:template>
180
+
181
+ <xsl:template match="dl">
182
+ <xsl:apply-templates/>
183
+ </xsl:template>
184
+
185
+ <xsl:template match="dt">
186
+ <w:p>
187
+ <xsl:apply-templates/>
188
+ </w:p>
189
+ </xsl:template>
190
+
191
+ <xsl:template match="dd">
192
+ <w:p>
193
+ <w:pPr>
194
+ <w:ind w:left="720"/>
195
+ </w:pPr>
196
+ <xsl:apply-templates/>
197
+ </w:p>
198
+ </xsl:template>
199
+
200
+ <xsl:template match="span[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
201
+ |a[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
202
+ |small[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
203
+ |strong[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
204
+ |em[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
205
+ |i[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
206
+ |b[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]
207
+ |u[not(ancestor::td) and not(ancestor::li) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]">
208
+ <xsl:comment>
209
+ In the following situation:
210
+
211
+ div
212
+ h2
213
+ span
214
+ textnode
215
+ span
216
+ textnode
217
+ p
218
+
219
+ The div template will not create a w:p because the div contains a h2. Therefore we need to wrap the inline elements span|a|small in a p here.
220
+ </xsl:comment>
221
+ <w:p>
222
+ <xsl:choose>
223
+ <xsl:when test="self::a[starts-with(@href, 'http://') or starts-with(@href, 'https://')]">
224
+ <xsl:call-template name="link" />
225
+ </xsl:when>
226
+ <xsl:when test="self::img">
227
+ <xsl:comment>
228
+ This template adds images.
229
+ </xsl:comment>
230
+ <xsl:call-template name="image"/>
231
+ </xsl:when>
232
+ <xsl:otherwise>
233
+ <xsl:apply-templates />
234
+ </xsl:otherwise>
235
+ </xsl:choose>
236
+ </w:p>
237
+ </xsl:template>
238
+
239
+ <xsl:template match="text()[not(parent::li) and not(parent::td) and not(parent::pre) and (preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div)]">
240
+ <xsl:comment>
241
+ In the following situation:
242
+
243
+ div
244
+ h2
245
+ textnode
246
+ p
247
+
248
+ The div template will not create a w:p because the div contains a h2. Therefore we need to wrap the textnode in a p here.
249
+ </xsl:comment>
250
+ <w:p>
251
+ <w:r>
252
+ <w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
253
+ </w:r>
254
+ </w:p>
255
+ </xsl:template>
256
+
257
+ <xsl:template match="span[contains(concat(' ', @class, ' '), ' h ')]">
258
+ <xsl:comment>
259
+ This template adds MS Word highlighting ability.
260
+ </xsl:comment>
261
+ <xsl:variable name="color">
262
+ <xsl:choose>
263
+ <xsl:when test="./@data-style='pink'">magenta</xsl:when>
264
+ <xsl:when test="./@data-style='blue'">cyan</xsl:when>
265
+ <xsl:when test="./@data-style='orange'">darkYellow</xsl:when>
266
+ <xsl:otherwise><xsl:value-of select="./@data-style"/></xsl:otherwise>
267
+ </xsl:choose>
268
+ </xsl:variable>
269
+ <xsl:choose>
270
+ <xsl:when test="preceding-sibling::h1 or preceding-sibling::h2 or preceding-sibling::h3 or preceding-sibling::h4 or preceding-sibling::h5 or preceding-sibling::h6 or preceding-sibling::table or preceding-sibling::p or preceding-sibling::ol or preceding-sibling::ul or preceding-sibling::div or following-sibling::h1 or following-sibling::h2 or following-sibling::h3 or following-sibling::h4 or following-sibling::h5 or following-sibling::h6 or following-sibling::table or following-sibling::p or following-sibling::ol or following-sibling::ul or following-sibling::div">
271
+ <w:p>
272
+ <w:r>
273
+ <w:rPr>
274
+ <w:highlight w:val="{$color}"/>
275
+ </w:rPr>
276
+ <w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
277
+ </w:r>
278
+ </w:p>
279
+ </xsl:when>
280
+ <xsl:otherwise>
281
+ <w:r>
282
+ <w:rPr>
283
+ <w:highlight w:val="{$color}"/>
284
+ </w:rPr>
285
+ <w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
286
+ </w:r>
287
+ </xsl:otherwise>
288
+ </xsl:choose>
289
+ </xsl:template>
290
+
291
+ <xsl:template match="div[contains(concat(' ', @class, ' '), ' -page-break ')]">
292
+ <w:p>
293
+ <w:r>
294
+ <w:br w:type="page" />
295
+ </w:r>
296
+ </w:p>
297
+ <xsl:apply-templates />
298
+ </xsl:template>
299
+
300
+ <xsl:template match="details" />
301
+
302
+ <xsl:template match="text()">
303
+ <xsl:if test="string-length(.) > 0">
304
+ <w:r>
305
+ <xsl:if test="ancestor::i">
306
+ <w:rPr>
307
+ <w:i />
308
+ </w:rPr>
309
+ </xsl:if>
310
+ <xsl:if test="ancestor::b">
311
+ <w:rPr>
312
+ <w:b />
313
+ </w:rPr>
314
+ </xsl:if>
315
+ <xsl:if test="ancestor::u">
316
+ <w:rPr>
317
+ <w:u w:val="single"/>
318
+ </w:rPr>
319
+ </xsl:if>
320
+ <xsl:if test="ancestor::s">
321
+ <w:rPr>
322
+ <w:strike w:val="true"/>
323
+ </w:rPr>
324
+ </xsl:if>
325
+ <xsl:if test="ancestor::sub">
326
+ <w:rPr>
327
+ <w:vertAlign w:val="subscript"/>
328
+ </w:rPr>
329
+ </xsl:if>
330
+ <xsl:if test="ancestor::sup">
331
+ <w:rPr>
332
+ <w:vertAlign w:val="superscript"/>
333
+ </w:rPr>
334
+ </xsl:if>
335
+ <w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
336
+ </w:r>
337
+ </xsl:if>
338
+ </xsl:template>
339
+
340
+ <xsl:template match="*">
341
+ <xsl:apply-templates/>
342
+ </xsl:template>
343
+
344
+ <xsl:template name="text-alignment">
345
+ <xsl:param name="class" select="@class" />
346
+ <xsl:param name="style" select="@style" />
347
+ <xsl:variable name="alignment">
348
+ <xsl:choose>
349
+ <xsl:when test="contains(concat(' ', $class, ' '), ' center ') or contains(translate(normalize-space($style),' ',''), 'text-align:center')">center</xsl:when>
350
+ <xsl:when test="contains(concat(' ', $class, ' '), ' right ') or contains(translate(normalize-space($style),' ',''), 'text-align:right')">right</xsl:when>
351
+ <xsl:when test="contains(concat(' ', $class, ' '), ' left ') or contains(translate(normalize-space($style),' ',''), 'text-align:left')">left</xsl:when>
352
+ <xsl:when test="contains(concat(' ', $class, ' '), ' justify ') or contains(translate(normalize-space($style),' ',''), 'text-align:justify')">both</xsl:when>
353
+ <xsl:otherwise></xsl:otherwise>
354
+ </xsl:choose>
355
+ </xsl:variable>
356
+ <xsl:if test="string-length(normalize-space($alignment)) > 0">
357
+ <w:pPr>
358
+ <w:jc w:val="{$alignment}"/>
359
+ </w:pPr>
360
+ </xsl:if>
361
+ </xsl:template>
362
+
363
+ </xsl:stylesheet>