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 +4 -4
- data/lib/htmltoword.rb +9 -6
- data/lib/htmltoword/document.rb +82 -29
- data/lib/htmltoword/helpers/templates_helper.rb +9 -0
- data/lib/htmltoword/helpers/xslt_helper.rb +17 -0
- data/lib/htmltoword/railtie.rb +25 -0
- data/lib/htmltoword/renderer.rb +43 -0
- data/lib/htmltoword/version.rb +1 -1
- data/lib/htmltoword/xslt/base.xslt +24 -3
- data/lib/htmltoword/xslt/cleanup.xslt +70 -0
- data/lib/htmltoword/xslt/functions.xslt +3 -0
- data/lib/htmltoword/xslt/image_functions.xslt +148 -0
- data/lib/htmltoword/xslt/images.xslt +129 -0
- data/lib/htmltoword/xslt/inline_elements.xslt +13 -12
- data/lib/htmltoword/xslt/links.xslt +2 -1
- data/lib/htmltoword/xslt/relations.xslt +33 -1
- data/lib/htmltoword/xslt/tables.xslt +3 -0
- metadata +10 -5
- data/lib/htmltoword/action_controller.rb +0 -54
- data/lib/htmltoword/htmltoword_helper.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7034d51bc5f9b9251aec22334f9ee4427bc3d520
|
4
|
+
data.tar.gz: b5925e2169eca6f9de534788a2f62850da43ac4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5ac9f6e7c6f26acbb06ee4c85a042cce12318802aabb08a836df3db136b408f6622b34366e0993ec4b8de7b2183b7f4e83c639c858342b5aa2586b264c2ed89
|
7
|
+
data.tar.gz: 6747b7abea40e7b43d52322ebe4283e4edc5f97c95487cb3de57e0f66bcfd209f379012572a88a96b4f95fe95651984eaeaf2b4cfb7b9410e648a80a0b7a3426
|
data/lib/htmltoword.rb
CHANGED
@@ -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
|
-
|
17
|
+
alias_method :config, :configuration
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
|
-
|
23
21
|
require_relative 'htmltoword/version'
|
24
|
-
require_relative 'htmltoword/
|
22
|
+
require_relative 'htmltoword/helpers/templates_helper'
|
23
|
+
require_relative 'htmltoword/helpers/xslt_helper'
|
25
24
|
require_relative 'htmltoword/document'
|
26
|
-
|
25
|
+
|
26
|
+
if defined?(Rails)
|
27
|
+
require_relative 'htmltoword/renderer'
|
28
|
+
require_relative 'htmltoword/railtie'
|
29
|
+
end
|
data/lib/htmltoword/document.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
module Htmltoword
|
2
2
|
class Document
|
3
|
-
|
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.
|
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,
|
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,
|
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.
|
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
|
44
|
-
|
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
|
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
|
84
|
-
html =
|
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,
|
87
|
-
transform_and_replace(source,
|
88
|
-
|
89
|
-
|
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
|
95
|
-
|
96
|
-
content =
|
97
|
-
content.gsub!(/\s*xmlns:(\w+)="(.*?)\s*"/,'') if remove_ns
|
98
|
-
@replaceable_files[file] =
|
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
|
data/lib/htmltoword/version.rb
CHANGED
@@ -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
|
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
|
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],' 	
','')" />
|
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::
|
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::
|
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="
|
25
|
-
|
26
|
-
|
|
27
|
-
|i[preceding-sibling::node()[1][self::a|self::b|self::
|
28
|
-
|
|
29
|
-
|span[preceding-sibling::node()[1][self::a|self::b|self::
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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::
|
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"
|
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"
|
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 > 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.
|
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:
|
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/
|
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.
|
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
|