htmltoword 0.5.1 → 0.7.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a0deb2026e95be6bde1c5af01b676e231dea63c0
4
- data.tar.gz: 94971919c3ad4bfae9dadf00a1d810828f33140c
3
+ metadata.gz: 7034d51bc5f9b9251aec22334f9ee4427bc3d520
4
+ data.tar.gz: b5925e2169eca6f9de534788a2f62850da43ac4d
5
5
  SHA512:
6
- metadata.gz: 19f266330f3e4c953f108b01e960517e8b00c3937de7a1ad9f443f6a7bbca2abc94678a0c2926bb8905fe04708ec8665014edb73605e3b68826b098443176d7e
7
- data.tar.gz: 93d0cf96fc81fa058ba6eda905152352d6b3c9dfbef4473c03cbf900c512af00ab9d8244dfa271df77a2a2b6d649b38e76ad292e793c582e44a61b03a260fc8c
6
+ metadata.gz: c5ac9f6e7c6f26acbb06ee4c85a042cce12318802aabb08a836df3db136b408f6622b34366e0993ec4b8de7b2183b7f4e83c639c858342b5aa2586b264c2ed89
7
+ data.tar.gz: 6747b7abea40e7b43d52322ebe4283e4edc5f97c95487cb3de57e0f66bcfd209f379012572a88a96b4f95fe95651984eaeaf2b4cfb7b9410e648a80a0b7a3426
@@ -1,8 +1,7 @@
1
1
  # encoding: UTF-8
2
- # require 'action_controller'
3
- # require 'action_view'
4
2
  require 'nokogiri'
5
3
  require 'zip'
4
+ require 'open-uri'
6
5
  require_relative 'htmltoword/configuration'
7
6
 
8
7
  module Htmltoword
@@ -15,12 +14,16 @@ module Htmltoword
15
14
  @configuration ||= Configuration.new
16
15
  end
17
16
 
18
- alias :config :configuration
17
+ alias_method :config, :configuration
19
18
  end
20
19
  end
21
20
 
22
-
23
21
  require_relative 'htmltoword/version'
24
- require_relative 'htmltoword/htmltoword_helper'
22
+ require_relative 'htmltoword/helpers/templates_helper'
23
+ require_relative 'htmltoword/helpers/xslt_helper'
25
24
  require_relative 'htmltoword/document'
26
- require_relative 'htmltoword/action_controller'
25
+
26
+ if defined?(Rails)
27
+ require_relative 'htmltoword/renderer'
28
+ require_relative 'htmltoword/railtie'
29
+ end
@@ -1,26 +1,26 @@
1
1
  module Htmltoword
2
2
  class Document
3
- class << self
4
- include HtmltowordHelper
3
+ include XSLTHelper
5
4
 
5
+ class << self
6
+ include TemplatesHelper
6
7
  def create(content, template_name = nil, extras = false)
7
8
  template_name += extension if template_name && !template_name.end_with?(extension)
8
9
  document = new(template_file(template_name))
9
- document.replace_file(content, Document.doc_xml_file, extras)
10
+ document.replace_files(content, extras)
10
11
  document.generate
11
12
  end
12
13
 
13
14
  def create_and_save(content, file_path, template_name = nil, extras = false)
14
- File.open(file_path, "wb") do |out|
15
+ File.open(file_path, 'wb') do |out|
15
16
  out << create(content, template_name, extras)
16
17
  end
17
18
  end
18
19
 
19
- def create_with_content(template, content, set=nil, extras = false)
20
+ def create_with_content(template, content, extras = false)
20
21
  template += extension unless template.end_with?(extension)
21
- content = replace_values(content, set) if set
22
22
  document = new(template_file(template))
23
- document.replace_file(content, Document.doc_xml_file, extras)
23
+ document.replace_files(content, extras)
24
24
  document.generate
25
25
  end
26
26
 
@@ -40,22 +40,15 @@ module Htmltoword
40
40
  'word/_rels/document.xml.rels'
41
41
  end
42
42
 
43
- def numbering_xslt
44
- File.join(Htmltoword.config.default_xslt_path, 'numbering.xslt')
45
- end
46
-
47
- def relations_xslt
48
- File.join(Htmltoword.config.default_xslt_path, 'relations.xslt')
49
- end
50
-
51
- def xslt_template(extras = false)
52
- File.join(Htmltoword.config.default_xslt_path, (extras ? 'htmltoword.xslt' : 'base.xslt'))
43
+ def content_types_xml_file
44
+ '[Content_Types].xml'
53
45
  end
54
46
  end
55
47
 
56
48
  def initialize(template_path)
57
49
  @replaceable_files = {}
58
50
  @template_path = template_path
51
+ @image_files = []
59
52
  end
60
53
 
61
54
  #
@@ -67,35 +60,95 @@ module Htmltoword
67
60
  template_zip.each do |entry|
68
61
  out.put_next_entry entry.name
69
62
  if @replaceable_files[entry.name] && entry.name == Document.doc_xml_file
70
- source = entry.get_input_stream.read.sub(/(<w:body>)((.|\n)*?)(<w:sectPr)/, "\\1#{@replaceable_files[entry.name]}\\4")
63
+ source = entry.get_input_stream.read
64
+ # Change only the body of document. TODO: Improve this...
65
+ source = source.sub(/(<w:body>)((.|\n)*?)(<w:sectPr)/, "\\1#{@replaceable_files[entry.name]}\\4")
71
66
  out.write(source)
72
67
  elsif @replaceable_files[entry.name]
73
68
  out.write(@replaceable_files[entry.name])
69
+ elsif entry.name == Document.content_types_xml_file
70
+ raw_file = entry.get_input_stream.read
71
+ content_types = @image_files.empty? ? raw_file : inject_image_content_types(raw_file)
72
+
73
+ out.write(content_types)
74
74
  else
75
75
  out.write(template_zip.read(entry.name))
76
76
  end
77
77
  end
78
+ unless @image_files.empty?
79
+ #stream the image files into the media folder using open-uri
80
+ @image_files.each do |hash|
81
+ out.put_next_entry("word/media/#{hash[:filename]}")
82
+ open(hash[:url], 'rb') do |f|
83
+ out.write(f.read)
84
+ end
85
+ end
86
+ end
78
87
  end
79
88
  buffer.string
80
89
  end
81
90
  end
82
91
 
83
- def replace_file(html, file_name = Document.doc_xml_file, extras = false)
84
- html = html.presence || '<body></body>'
92
+ def replace_files(html, extras = false)
93
+ html = '<body></body>' if html.nil? || html.empty?
85
94
  source = Nokogiri::HTML(html.gsub(/>\s+</, '><'))
86
- transform_and_replace(source, Document.numbering_xslt, Document.numbering_xml_file)
87
- transform_and_replace(source, Document.relations_xslt, Document.relations_xml_file)
88
- cleaned_source = Nokogiri::XSLT(File.open(File.join(Htmltoword.config.default_xslt_path, 'inline_elements.xslt'))).transform(source)
89
- transform_and_replace(cleaned_source, Document.xslt_template(extras), file_name, extras)
95
+ transform_and_replace(source, xslt_path('numbering'), Document.numbering_xml_file)
96
+ transform_and_replace(source, xslt_path('relations'), Document.relations_xml_file)
97
+ transform_doc_xml(source, extras)
98
+ local_images(source)
99
+ end
100
+
101
+ def transform_doc_xml(source, extras = false)
102
+ transformed_source = xslt(stylesheet_name: 'cleanup').transform(source)
103
+ transformed_source = xslt(stylesheet_name: 'inline_elements').transform(transformed_source)
104
+ transform_and_replace(transformed_source, document_xslt(extras), Document.doc_xml_file, extras)
90
105
  end
91
106
 
92
107
  private
93
108
 
94
- def transform_and_replace source, xslt, file, remove_ns = false
95
- xslt = Nokogiri::XSLT(File.open(xslt))
96
- content = xslt.apply_to(source)
97
- content.gsub!(/\s*xmlns:(\w+)="(.*?)\s*"/,'') if remove_ns
98
- @replaceable_files[file] = content
109
+ def transform_and_replace(source, stylesheet_path, file, remove_ns = false)
110
+ stylesheet = xslt(stylesheet_path: stylesheet_path)
111
+ content = stylesheet.apply_to(source)
112
+ content.gsub!(/\s*xmlns:(\w+)="(.*?)\s*"/, '') if remove_ns
113
+ @replaceable_files[file] = content
114
+ end
115
+
116
+ #generates an array of hashes with filename and full url
117
+ #for all images to be embeded in the word document
118
+ def local_images(source)
119
+ source.css('img').each_with_index do |image,i|
120
+ filename = image['data-filename'] ? image['data-filename'] : image['src'].split("/").last
121
+ ext = File.extname(filename).delete(".").downcase
122
+
123
+ @image_files << { filename: "image#{i+1}.#{ext}", url: image['src'], ext: ext }
124
+ end
125
+ end
126
+
127
+ #get extension from filename and clean to match content_types
128
+ def content_type_from_extension(ext)
129
+ ext == "jpg" ? "jpeg" : ext
130
+ end
131
+
132
+ #inject the required content_types into the [content_types].xml file...
133
+ def inject_image_content_types(source)
134
+ doc = Nokogiri::XML(source)
135
+
136
+ #get a list of all extensions currently in content_types file
137
+ existing_exts = doc.css("Default").map { |node| node.attribute("Extension").value }.compact
138
+
139
+ #get a list of extensions we need for our images
140
+ required_exts = @image_files.map{ |i| i[:ext] }
141
+
142
+ #workout which required extensions are missing from the content_types file
143
+ missing_exts = (required_exts - existing_exts).uniq
144
+
145
+ #inject missing extensions into document
146
+ missing_exts.each do |ext|
147
+ doc.at_css("Types").add_child( "<Default Extension='#{ext}' ContentType='image/#{content_type_from_extension(ext)}'/>")
148
+ end
149
+
150
+ #return the amended source to be saved into the zip
151
+ doc.to_s
99
152
  end
100
153
  end
101
154
  end
@@ -0,0 +1,9 @@
1
+ module Htmltoword
2
+ module TemplatesHelper
3
+ def template_file(template_file_name = nil)
4
+ default_path = File.join(::Htmltoword.config.default_templates_path, 'default.docx')
5
+ template_path = template_file_name.nil? ? '' : File.join(::Htmltoword.config.custom_templates_path, template_file_name)
6
+ File.exist?(template_path) ? template_path : default_path
7
+ end
8
+ end
9
+ end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Htmltoword
2
- VERSION = "0.5.1"
2
+ VERSION = '0.7.0'
3
3
  end
@@ -19,7 +19,7 @@
19
19
  <xsl:include href="./functions.xslt"/>
20
20
  <xsl:include href="./tables.xslt"/>
21
21
  <xsl:include href="./links.xslt"/>
22
-
22
+ <xsl:include href="./images.xslt"/>
23
23
  <xsl:template match="/">
24
24
  <xsl:apply-templates />
25
25
  </xsl:template>
@@ -204,6 +204,12 @@
204
204
  <xsl:when test="self::a[starts-with(@href, 'http://') or starts-with(@href, 'https://')]">
205
205
  <xsl:call-template name="link" />
206
206
  </xsl:when>
207
+ <xsl:when test="self::img">
208
+ <xsl:comment>
209
+ This template adds images.
210
+ </xsl:comment>
211
+ <xsl:call-template name="image"/>
212
+ </xsl:when>
207
213
  <xsl:otherwise>
208
214
  <xsl:apply-templates />
209
215
  </xsl:otherwise>
@@ -277,12 +283,12 @@
277
283
  <xsl:template match="text()">
278
284
  <xsl:if test="string-length(.) > 0">
279
285
  <w:r>
280
- <xsl:if test="ancestor::i or ancestor::em">
286
+ <xsl:if test="ancestor::i">
281
287
  <w:rPr>
282
288
  <w:i />
283
289
  </w:rPr>
284
290
  </xsl:if>
285
- <xsl:if test="ancestor::b or ancestor::strong">
291
+ <xsl:if test="ancestor::b">
286
292
  <w:rPr>
287
293
  <w:b />
288
294
  </w:rPr>
@@ -292,6 +298,21 @@
292
298
  <w:u w:val="single"/>
293
299
  </w:rPr>
294
300
  </xsl:if>
301
+ <xsl:if test="ancestor::s">
302
+ <w:rPr>
303
+ <w:strike w:val="true"/>
304
+ </w:rPr>
305
+ </xsl:if>
306
+ <xsl:if test="ancestor::sub">
307
+ <w:rPr>
308
+ <w:vertAlign w:val="subscript"/>
309
+ </w:rPr>
310
+ </xsl:if>
311
+ <xsl:if test="ancestor::sup">
312
+ <w:rPr>
313
+ <w:vertAlign w:val="superscript"/>
314
+ </w:rPr>
315
+ </xsl:if>
295
316
  <w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
296
317
  </w:r>
297
318
  </xsl:if>
@@ -0,0 +1,70 @@
1
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
2
+ xmlns:str="http://exslt.org/strings"
3
+ xmlns:func="http://exslt.org/functions"
4
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
5
+ version="1.0"
6
+ exclude-result-prefixes="java msxsl ext w o v WX aml w10"
7
+ extension-element-prefixes="func">
8
+ <xsl:output method="html" encoding="utf-8" omit-xml-declaration="yes" indent="yes"/>
9
+
10
+ <xsl:template match="/">
11
+ <xsl:apply-templates/>
12
+ </xsl:template>
13
+
14
+ <xsl:template match="head"/>
15
+
16
+ <!-- Elements not supported -->
17
+ <xsl:template match="applet"/>
18
+ <xsl:template match="area"/>
19
+ <xsl:template match="audio"/>
20
+ <xsl:template match="base"/>
21
+ <xsl:template match="basefont"/>
22
+ <xsl:template match="canvas"/>
23
+ <xsl:template match="command"/>
24
+ <xsl:template match="font"/>
25
+ <xsl:template match="iframe"/>
26
+ <xsl:template match="isindex"/>
27
+ <xsl:template match="map"/>
28
+ <xsl:template match="noframes"/>
29
+ <xsl:template match="noscript"/>
30
+ <xsl:template match="object"/>
31
+ <xsl:template match="param"/>
32
+ <xsl:template match="script"/>
33
+ <xsl:template match="source"/>
34
+ <xsl:template match="style"/>
35
+ <xsl:template match="video"/>
36
+
37
+ <!-- Elements currently being handled as normal text. Remove tags only -->
38
+ <xsl:template match="abbr"><xsl:apply-templates/></xsl:template>
39
+ <xsl:template match="acronym"><xsl:apply-templates/></xsl:template>
40
+ <xsl:template match="bdi"><xsl:apply-templates/></xsl:template>
41
+ <xsl:template match="bdo"><xsl:apply-templates/></xsl:template>
42
+ <xsl:template match="big"><xsl:apply-templates/></xsl:template>
43
+ <xsl:template match="code"><xsl:apply-templates/></xsl:template>
44
+ <xsl:template match="kbd"><xsl:apply-templates/></xsl:template>
45
+ <xsl:template match="samp"><xsl:apply-templates/></xsl:template>
46
+ <xsl:template match="small"><xsl:apply-templates/></xsl:template>
47
+ <xsl:template match="tt"><xsl:apply-templates/></xsl:template>
48
+ <xsl:template match="var"><xsl:apply-templates/></xsl:template>
49
+
50
+ <!-- Inline elements transformations -->
51
+ <xsl:template match="cite"><i><xsl:apply-templates/></i></xsl:template>
52
+ <xsl:template match="del"><s><xsl:apply-templates/></s></xsl:template>
53
+ <xsl:template match="dfn"><i><xsl:apply-templates/></i></xsl:template>
54
+ <xsl:template match="em"><i><xsl:apply-templates/></i></xsl:template>
55
+ <xsl:template match="ins"><u><xsl:apply-templates/></u></xsl:template>
56
+ <xsl:template match="mark"><span class="h" data-style="yellow"><xsl:apply-templates/></span></xsl:template>
57
+ <xsl:template match="q">"<xsl:apply-templates/>"</xsl:template>
58
+ <xsl:template match="strike"><s><xsl:apply-templates/></s></xsl:template>
59
+ <xsl:template match="strong"><b><xsl:apply-templates/></b></xsl:template>
60
+
61
+ <!-- Block elements transformations -->
62
+ <xsl:template match="section"><div class="{@class}" style="{@style}"><xsl:apply-templates/></div></xsl:template>
63
+ <xsl:template match="article"><div class="{@class}" style="{@style}"><xsl:apply-templates/></div></xsl:template>
64
+
65
+ <xsl:template match="@*|node()">
66
+ <xsl:copy>
67
+ <xsl:apply-templates select="@*|node()"/>
68
+ </xsl:copy>
69
+ </xsl:template>
70
+ </xsl:stylesheet>
@@ -31,4 +31,7 @@
31
31
  </xsl:choose>
32
32
  </func:result>
33
33
  </func:function>
34
+
35
+ <!-- template as function used to return the relationship id of the element (currently links or images) -->
36
+ <xsl:template name="relationship-id">rId<xsl:value-of select="count(preceding::a[starts-with(@href, 'http://') or starts-with(@href, 'https://')])+count(preceding::img)+8"/></xsl:template>
34
37
  </xsl:stylesheet>
@@ -0,0 +1,148 @@
1
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
2
+ xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
3
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
4
+ xmlns:o="urn:schemas-microsoft-com:office:office"
5
+ xmlns:v="urn:schemas-microsoft-com:vml"
6
+ xmlns:WX="http://schemas.microsoft.com/office/word/2003/auxHint"
7
+ xmlns:aml="http://schemas.microsoft.com/aml/2001/core"
8
+ xmlns:w10="urn:schemas-microsoft-com:office:word"
9
+ xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"
10
+ xmlns:msxsl="urn:schemas-microsoft-com:xslt"
11
+ xmlns:ext="http://www.xmllab.net/wordml2html/ext"
12
+ xmlns:java="http://xml.apache.org/xalan/java"
13
+ xmlns:str="http://exslt.org/strings"
14
+ xmlns:func="http://exslt.org/functions"
15
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
16
+ version="1.0"
17
+ exclude-result-prefixes="java msxsl ext w o v WX aml w10"
18
+ extension-element-prefixes="func">
19
+
20
+ <!-- template as function used to return the file extension of an image. -->
21
+ <xsl:template name="image-extension">
22
+ <xsl:param name="data-filename" select="." />
23
+ <xsl:param name="source" select="." />
24
+ <xsl:variable name="filename">
25
+ <xsl:call-template name="image-name">
26
+ <xsl:with-param name="source" select="$source"/>
27
+ <xsl:with-param name="data-filename" select="$data-filename"/>
28
+ </xsl:call-template>
29
+ </xsl:variable>
30
+ <xsl:value-of select="substring-after($filename,'.')"/>
31
+ </xsl:template>
32
+
33
+ <!-- template as function used to return the name of an image. -->
34
+ <xsl:template name="image-name">
35
+ <xsl:param name="data-filename" select="." />
36
+ <xsl:param name="source" select="." />
37
+ <xsl:choose>
38
+ <xsl:when test="string-length($data-filename) > 0">
39
+ <xsl:value-of select="$data-filename"/>
40
+ </xsl:when>
41
+ <xsl:otherwise>
42
+ <xsl:call-template name="extract-filename-from-path">
43
+ <xsl:with-param name="path" select="$source"/>
44
+ </xsl:call-template>
45
+ </xsl:otherwise>
46
+ </xsl:choose>
47
+ </xsl:template>
48
+
49
+ <!-- template as function used to extract the filename from the image source. Can't use tokenize or other functions which return a fragment tree as xpath can't process them. -->
50
+ <xsl:template name="extract-filename-from-path">
51
+ <xsl:param name="path" select="." />
52
+ <xsl:choose>
53
+ <xsl:when test="not(contains($path, '/'))">
54
+ <xsl:value-of select="$path" />
55
+ </xsl:when>
56
+ <xsl:otherwise>
57
+ <xsl:call-template name="extract-filename-from-path">
58
+ <xsl:with-param name="path" select="substring-after($path, '/')" />
59
+ </xsl:call-template>
60
+ </xsl:otherwise>
61
+ </xsl:choose>
62
+ </xsl:template>
63
+
64
+ <!--
65
+ Returns the unqualified dimension from a length specification copied from:
66
+ http://docbook.sourceforge.net/release/xsl/1.76.1/doc/lib/length-magnitude.html
67
+ -->
68
+
69
+ <xsl:template name="length-magnitude">
70
+ <xsl:param name="length" select="'0pt'"></xsl:param>
71
+
72
+ <xsl:choose>
73
+ <xsl:when test="string-length($length) = 0"></xsl:when>
74
+ <xsl:when test="substring($length,1,1) = '0' or substring($length,1,1) = '1' or substring($length,1,1) = '2' or substring($length,1,1) = '3' or substring($length,1,1) = '4' or substring($length,1,1) = '5' or substring($length,1,1) = '6' or substring($length,1,1) = '7' or substring($length,1,1) = '8' or substring($length,1,1) = '9' or substring($length,1,1) = '.'">
75
+ <xsl:value-of select="substring($length,1,1)"></xsl:value-of>
76
+ <xsl:call-template name="length-magnitude">
77
+ <xsl:with-param name="length" select="substring($length,2)"></xsl:with-param>
78
+ </xsl:call-template>
79
+ </xsl:when>
80
+ </xsl:choose>
81
+ </xsl:template>
82
+
83
+
84
+
85
+
86
+ <!-- Convert em and pixel sizes to inches. Inspired by from:
87
+ http://docbook.sourceforge.net/release/xsl/1.76.1/doc/lib/length-in-points.html
88
+ EMU info from: https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
89
+ -->
90
+ <xsl:template name="length-in-emus">
91
+ <xsl:param name="length" select="'0px'"/>
92
+ <xsl:param name="em.size" select="6.02250006023"/>
93
+ <xsl:param name="pixels.per.inch" select="90"/>
94
+ <xsl:variable name="emus.per.inch" select="914400"/>
95
+
96
+ <xsl:variable name="magnitude">
97
+ <xsl:call-template name="length-magnitude">
98
+ <xsl:with-param name="length" select="$length"/>
99
+ </xsl:call-template>
100
+ </xsl:variable>
101
+
102
+ <xsl:variable name="units">
103
+ <xsl:value-of select="substring($length, string-length($magnitude)+1)"/>
104
+ </xsl:variable>
105
+
106
+ <xsl:variable name="inches">
107
+ <xsl:choose>
108
+ <xsl:when test="$units = 'px'">
109
+ <xsl:value-of select="$magnitude div $pixels.per.inch"/>
110
+ </xsl:when>
111
+ <xsl:when test="$units = 'em'">
112
+ <xsl:value-of select="$magnitude div $em.size"/>
113
+ </xsl:when>
114
+ <xsl:otherwise>
115
+ <xsl:message>
116
+ <xsl:text>Unrecognized unit of measure: </xsl:text>
117
+ <xsl:value-of select="$units"></xsl:value-of>
118
+ <xsl:text>.</xsl:text>
119
+ </xsl:message>
120
+ </xsl:otherwise>
121
+ </xsl:choose>
122
+ </xsl:variable>
123
+
124
+ <xsl:value-of select="$inches * $emus.per.inch"/>
125
+ </xsl:template>
126
+
127
+ <!-- template as function used to get the width or height of an image in points. -->
128
+ <xsl:template name="image-dimention">
129
+ <xsl:param name="style" />
130
+ <xsl:param name="data-value" />
131
+ <xsl:param name="type" />
132
+
133
+ <xsl:variable name="size">
134
+ <xsl:choose>
135
+ <xsl:when test="string-length($data-value) > 0">
136
+ <xsl:value-of select="$data-value" />
137
+ </xsl:when>
138
+ <xsl:when test="contains($style, concat($type,':'))">
139
+ <xsl:value-of select="translate(str:tokenize(substring-after($style, concat($type,':')), ';')[1],'&#x20;&#x9;&#xD;&#xA;','')" />
140
+ </xsl:when>
141
+ <xsl:otherwise>1</xsl:otherwise>
142
+ </xsl:choose>
143
+ </xsl:variable>
144
+ <xsl:call-template name="length-in-emus">
145
+ <xsl:with-param name="length" select="$size"/>
146
+ </xsl:call-template>
147
+ </xsl:template>
148
+ </xsl:stylesheet>
@@ -0,0 +1,129 @@
1
+ <xsl:stylesheet xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas"
2
+ xmlns:mo="http://schemas.microsoft.com/office/mac/office/2008/main"
3
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
4
+ xmlns:mv="urn:schemas-microsoft-com:mac:vml"
5
+ xmlns:o="urn:schemas-microsoft-com:office:office"
6
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
7
+ xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
8
+ xmlns:v="urn:schemas-microsoft-com:vml"
9
+ xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
10
+ xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
11
+ xmlns:w10="urn:schemas-microsoft-com:office:word"
12
+ xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
13
+ xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
14
+ xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
15
+ xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
16
+ xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"
17
+ xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
18
+ xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
19
+ xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
20
+ xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main"
21
+ xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"
22
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
23
+ xmlns:WX="http://schemas.microsoft.com/office/word/2003/auxHint"
24
+ xmlns:aml="http://schemas.microsoft.com/aml/2001/core"
25
+ xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"
26
+ xmlns:msxsl="urn:schemas-microsoft-com:xslt"
27
+ xmlns:ext="http://www.xmllab.net/wordml2html/ext"
28
+ xmlns:java="http://xml.apache.org/xalan/java"
29
+ xmlns:str="http://exslt.org/strings"
30
+ xmlns:func="http://exslt.org/functions"
31
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
32
+ mc:Ignorable="w14 w15 wp14 a14"
33
+ version="1.0"
34
+ exclude-result-prefixes="java msxsl ext w o v WX aml w10"
35
+ extension-element-prefixes="func">
36
+
37
+ <xsl:include href="./image_functions.xslt"/>
38
+
39
+ <xsl:template match="img|body/img" name="image">
40
+ <w:drawing>
41
+ <wp:inline distT="0" distB="0" distL="0" distR="0">
42
+ <wp:extent>
43
+ <xsl:call-template name="image-dimention-attributes"/>
44
+ </wp:extent>
45
+ <wp:effectExtent l="0" t="0" r="0" b="0"/>
46
+ <wp:docPr>
47
+ <xsl:attribute name="id"><xsl:value-of select="count(preceding::img)+1" /></xsl:attribute>
48
+ <xsl:attribute name="name">Picture <xsl:value-of select="count(preceding::img)+1" /></xsl:attribute>
49
+ </wp:docPr>
50
+ <wp:cNvGraphicFramePr>
51
+ <a:graphicFrameLocks noChangeAspect="1"/>
52
+ </wp:cNvGraphicFramePr>
53
+ <a:graphic>
54
+ <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
55
+ <pic:pic>
56
+ <pic:nvPicPr>
57
+ <pic:cNvPr>
58
+ <xsl:attribute name="id"><xsl:value-of select="count(preceding::img)+1" /></xsl:attribute>
59
+ <xsl:attribute name="title"><xsl:value-of select="@alt" /></xsl:attribute>
60
+ <xsl:attribute name="name"><xsl:call-template name="image-name">
61
+ <xsl:with-param name="source" select="@src"/>
62
+ <xsl:with-param name="data-filename" select="@data-filename"/>
63
+ </xsl:call-template></xsl:attribute>
64
+ </pic:cNvPr>
65
+ <pic:cNvPicPr/>
66
+ </pic:nvPicPr>
67
+ <pic:blipFill>
68
+ <a:blip>
69
+ <xsl:attribute name="r:embed"><xsl:call-template name="relationship-id"/></xsl:attribute>
70
+ <a:extLst>
71
+ <a:ext uri="{{28A0092B-C50C-407E-A947-70E740481C1C}}">
72
+ <a14:useLocalDpi val="0"/>
73
+ </a:ext>
74
+ </a:extLst>
75
+ </a:blip>
76
+ <a:stretch>
77
+ <a:fillRect/>
78
+ </a:stretch>
79
+ </pic:blipFill>
80
+ <pic:spPr>
81
+ <a:xfrm>
82
+ <a:off x="0" y="0"/>
83
+ <a:ext>
84
+ <xsl:call-template name="image-dimention-attributes"/>
85
+ </a:ext>
86
+ </a:xfrm>
87
+ <a:prstGeom prst="rect">
88
+ <a:avLst/>
89
+ </a:prstGeom>
90
+ </pic:spPr>
91
+ </pic:pic>
92
+ </a:graphicData>
93
+ </a:graphic>
94
+ </wp:inline>
95
+ </w:drawing>
96
+ </xsl:template>
97
+
98
+ <!--
99
+ A style specifiying the width and height is required to render an image correctly in word.
100
+ Styles can be provided either via data attributes:
101
+ img src="pathtosomeimage" data-width="200px" data-height="250px"
102
+
103
+ or via the html style attribute
104
+ img src="pathtosomeimage" style="width:150px;height:200px"
105
+
106
+ If both a style and data attributes are provided then data attributes take president.
107
+ e.g.
108
+ img src="pathtosomeimage" data-width="200em" style="width:150px;height:200px"
109
+ becomes: width:200em;height:200px
110
+
111
+ Note: All sizes must be in pixles or em
112
+ -->
113
+ <xsl:template name="image-dimention-attributes">
114
+ <xsl:attribute name="cx">
115
+ <xsl:call-template name="image-dimention">
116
+ <xsl:with-param name="style" select="@style" />
117
+ <xsl:with-param name="data-value" select="@data-width" />
118
+ <xsl:with-param name="type" select="'width'" />
119
+ </xsl:call-template>
120
+ </xsl:attribute>
121
+ <xsl:attribute name="cy">
122
+ <xsl:call-template name="image-dimention">
123
+ <xsl:with-param name="style" select="@style" />
124
+ <xsl:with-param name="data-value" select="@data-height" />
125
+ <xsl:with-param name="type" select="'height'" />
126
+ </xsl:call-template>
127
+ </xsl:attribute>
128
+ </xsl:template>
129
+ </xsl:stylesheet>
@@ -11,29 +11,30 @@
11
11
  </xsl:template>
12
12
 
13
13
  <!-- get first inline element of a sequence or text having block element siblings... -->
14
- <xsl:template match="node()[self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()][parent::div|parent::li|parent::td]">
14
+ <xsl:template match="node()[self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()][parent::div|parent::li|parent::td]">
15
15
  <div>
16
16
  <xsl:attribute name="class"><xsl:value-of select="../@class"/></xsl:attribute>
17
17
  <xsl:attribute name="style"><xsl:value-of select="../@style"/></xsl:attribute>
18
18
  <xsl:call-template name="inlineElement"/>
19
19
  </div>
20
- <xsl:apply-templates select="following-sibling::node()[not((self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text())[parent::div|parent::li|parent::td])][1]"/>
20
+ <xsl:apply-templates select="following-sibling::node()[not((self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text())[parent::div|parent::li|parent::td])][1]"/>
21
21
  </xsl:template>
22
22
 
23
23
  <!-- get following inline elements... -->
24
- <xsl:template match="a[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
25
- |b[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
26
- |em[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
27
- |i[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
28
- |small[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
29
- |span[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
30
- |strong[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
31
- |u[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]
32
- |text()[preceding-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]]"
24
+ <xsl:template match="
25
+ a[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
26
+ |b[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
27
+ |i[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
28
+ |s[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
29
+ |span[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
30
+ |sub[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
31
+ |sup[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
32
+ |u[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]
33
+ |text()[preceding-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]]"
33
34
  name="inlineElement">
34
35
  <xsl:copy>
35
36
  <xsl:apply-templates select="@*|node()[1]"/>
36
37
  </xsl:copy>
37
- <xsl:apply-templates select="following-sibling::node()[1][self::a|self::b|self::em|self::i|self::small|self::span|self::strong|self::u|self::text()]"/>
38
+ <xsl:apply-templates select="following-sibling::node()[1][self::a|self::b|self::i|self::s|self::span|self::sub|self::sup|self::u|self::text()]"/>
38
39
  </xsl:template>
39
40
  </xsl:stylesheet>
@@ -17,9 +17,10 @@
17
17
  exclude-result-prefixes="java msxsl ext w o v WX aml w10"
18
18
  extension-element-prefixes="func">
19
19
 
20
+
20
21
  <xsl:template match="a[starts-with(@href, 'http://') or starts-with(@href, 'https://')]" name="link">
21
22
  <w:hyperlink>
22
- <xsl:attribute name="r:id">rId<xsl:value-of select="count(preceding::a[starts-with(@href, 'http://') or starts-with(@href, 'https://')]) + 8" /></xsl:attribute>
23
+ <xsl:attribute name="r:id"><xsl:call-template name="relationship-id"/></xsl:attribute>
23
24
  <w:r>
24
25
  <w:rPr>
25
26
  <w:rStyle w:val="Hyperlink"/>
@@ -3,13 +3,45 @@
3
3
  version="1.0">
4
4
  <xsl:output method="xml" encoding="utf-8" omit-xml-declaration="yes" indent="yes" />
5
5
 
6
+ <xsl:include href="./image_functions.xslt"/>
7
+ <xsl:include href="./functions.xslt"/>
8
+
6
9
  <xsl:template match="a[starts-with(@href, 'http://') or starts-with(@href, 'https://')]" priority="1">
7
10
  <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="{@href}" TargetMode="External">
8
- <xsl:attribute name="Id">rId<xsl:value-of select="count(preceding::a[starts-with(@href, 'http://') or starts-with(@href, 'https://')]) + 8"/></xsl:attribute>
11
+ <xsl:attribute name="Id"><xsl:call-template name="relationship-id"/></xsl:attribute>
12
+ </Relationship>
13
+ </xsl:template>
14
+
15
+ <!--
16
+ Images can either be embedded in the document or referenced via the external url.
17
+ By default images are embedded and a copy will be stored in the final zip folder.
18
+ If you want your images to be referenced externally (don't store the image files) then add data-external="true" to your image tag(s).
19
+ If you are embedding them images need a filename. You can either define one by passing the name and extension to data-filename or rely on the defaul which is the last part of the source url.
20
+ **Word files can be corrupted if an extension isn't present so if you are relying on the source url ensure that is has an extension.**
21
+ -->
22
+ <xsl:template match="img" priority="0">
23
+ <Relationship Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image">
24
+ <xsl:choose>
25
+ <xsl:when test="@data-external = 'true'">
26
+ <xsl:attribute name="Target"><xsl:value-of select="@src"/></xsl:attribute>
27
+ <xsl:attribute name="TargetMode">External</xsl:attribute>
28
+ </xsl:when>
29
+
30
+ <xsl:otherwise>
31
+ <xsl:attribute name="Target">media/image<xsl:value-of select="count(preceding::img)+1"/>.<xsl:call-template name="image-extension">
32
+ <xsl:with-param name="source" select="@src"/>
33
+ <xsl:with-param name="data-filename" select="@data-filename"/>
34
+ </xsl:call-template>
35
+ </xsl:attribute>
36
+ </xsl:otherwise>
37
+ </xsl:choose>
38
+
39
+ <xsl:attribute name="Id"><xsl:call-template name="relationship-id"/></xsl:attribute>
9
40
  </Relationship>
10
41
  </xsl:template>
11
42
 
12
43
  <xsl:template match="/">
44
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
13
45
  <Relationships>
14
46
  <Relationship Id="rId3" Type="http://schemas.microsoft.com/office/2007/relationships/stylesWithEffects" Target="stylesWithEffects.xml"/>
15
47
  <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>
@@ -152,6 +152,9 @@
152
152
  <xsl:variable name="cell-bg" select="str:tokenize(substring-after(@class, 'ms-fill-'), ' ')[1]"/>
153
153
  <w:shd w:val="clear" w:color="auto" w:fill="{$cell-bg}" />
154
154
  </xsl:if>
155
+ <xsl:if test="@colspan &gt; 1">
156
+ <w:gridSpan w:val="{@colspan}"/>
157
+ </xsl:if>
155
158
  </w:tcPr>
156
159
  </xsl:template>
157
160
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: htmltoword
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicholas Frandsen, Cristina Matonte
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-24 00:00:00.000000000 Z
11
+ date: 2017-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -134,16 +134,21 @@ files:
134
134
  - Rakefile
135
135
  - bin/htmltoword
136
136
  - lib/htmltoword.rb
137
- - lib/htmltoword/action_controller.rb
138
137
  - lib/htmltoword/configuration.rb
139
138
  - lib/htmltoword/document.rb
140
- - lib/htmltoword/htmltoword_helper.rb
139
+ - lib/htmltoword/helpers/templates_helper.rb
140
+ - lib/htmltoword/helpers/xslt_helper.rb
141
+ - lib/htmltoword/railtie.rb
142
+ - lib/htmltoword/renderer.rb
141
143
  - lib/htmltoword/templates/default.docx
142
144
  - lib/htmltoword/version.rb
143
145
  - lib/htmltoword/xslt/base.xslt
146
+ - lib/htmltoword/xslt/cleanup.xslt
144
147
  - lib/htmltoword/xslt/extras.xslt
145
148
  - lib/htmltoword/xslt/functions.xslt
146
149
  - lib/htmltoword/xslt/htmltoword.xslt
150
+ - lib/htmltoword/xslt/image_functions.xslt
151
+ - lib/htmltoword/xslt/images.xslt
147
152
  - lib/htmltoword/xslt/inline_elements.xslt
148
153
  - lib/htmltoword/xslt/links.xslt
149
154
  - lib/htmltoword/xslt/numbering.xslt
@@ -170,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
175
  version: '0'
171
176
  requirements: []
172
177
  rubyforge_project:
173
- rubygems_version: 2.4.8
178
+ rubygems_version: 2.6.14
174
179
  signing_key:
175
180
  specification_version: 4
176
181
  summary: This simple gem allows you to create MS Word docx documents from simple html
@@ -1,54 +0,0 @@
1
- require 'rack/test'
2
- require 'action_controller'
3
-
4
- unless defined? Mime::DOCX
5
- Mime::Type.register 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', :docx
6
- end
7
-
8
- ActionController::Renderers.add :docx do |filename, options|
9
- formats << :docx unless formats.include?(:docx) || Rails.version < '3.2'
10
-
11
- # This is ugly and should be solved with regular file utils
12
- if options[:template] == action_name
13
- if filename =~ %r{^([^\/]+)/(.+)$}
14
- options[:prefixes] ||= []
15
- options[:prefixes].unshift $1
16
- options[:template] = $2
17
- else
18
- options[:template] = filename
19
- end
20
- end
21
-
22
- # disposition / filename
23
- disposition = options.delete(:disposition) || 'attachment'
24
- if file_name = options.delete(:filename)
25
- file_name += '.docx' unless file_name =~ /\.docx$/
26
- else
27
- file_name = "#{filename.gsub(/^.*\//, '')}.docx"
28
- end
29
-
30
- # other properties
31
- word_template = options.delete(:word_template) || nil
32
- extras = options.delete(:extras) || false
33
- # content will come from property content unless not specified
34
- # then it will look for a template.
35
- content = options.delete(:content) || render_to_string(options)
36
-
37
- document = Htmltoword::Document.create(content, word_template, extras)
38
- send_data document, filename: file_name, type: Mime::DOCX, disposition: disposition
39
- end
40
-
41
- if defined? ActionController::Responder
42
- module ActionController
43
- # For respond_with default
44
- class Responder
45
- def to_docx
46
- if @default_response
47
- @default_response.call(options)
48
- else
49
- controller.render({ docx: controller.action_name }.merge(options))
50
- end
51
- end
52
- end
53
- end
54
- end
@@ -1,35 +0,0 @@
1
- module Htmltoword
2
- module HtmltowordHelper
3
-
4
- def template_file template_file_name=nil
5
- default_path = File.join(::Htmltoword.config.default_templates_path, 'default.docx')
6
- template_path = template_file_name.present? ? File.join(::Htmltoword.config.custom_templates_path, template_file_name) : ''
7
- File.exist?(template_path) ? template_path : default_path
8
- end
9
-
10
- def replace_values content, set
11
- doc = Nokogiri::HTML(content)
12
- set.each_pair do |key, value|
13
- fields = (doc/"//span[@data-id='#{key}']")
14
- fields.each do |f|
15
- date_format = f.attr("date-format") || "long"
16
- data_transform = f.attr("data-transform")
17
- if value.is_a? Hash
18
- view = ActionView::Base.new(ActionController::Base.view_paths, {})
19
- final_value = view.render 'partials/answer_table', answer: value
20
- fragment = doc.root.parse(final_value).first
21
- new_node = doc.root.add_child(fragment)
22
- f.parent.replace new_node
23
- elsif value.is_a? Time
24
- f.content = I18n.l(value.to_date, format: date_format.to_sym)
25
- elsif data_transform == 'capitalized'
26
- f.content = value.mb_chars.capitalize rescue value
27
- else
28
- f.content = value
29
- end
30
- end
31
- end
32
- doc.to_s
33
- end
34
- end
35
- end