htmltoword 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -3
- data/lib/htmltoword.rb +6 -71
- data/lib/htmltoword/document.rb +70 -0
- data/lib/htmltoword/version.rb +1 -1
- data/xslt/html_to_wordml.xslt +112 -124
- metadata +11 -19
- data/.gitignore +0 -18
- data/.travis.yml +0 -2
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -22
- data/htmltoword.gemspec +0 -27
- data/spec/spec_helper.rb +0 -3
- data/spec/xslt_spec.rb +0 -51
data/README.md
CHANGED
@@ -14,7 +14,7 @@ Or install it yourself as:
|
|
14
14
|
|
15
15
|
$ gem install htmltoword
|
16
16
|
|
17
|
-
## Usage
|
17
|
+
## Usage
|
18
18
|
|
19
19
|
### Standalone
|
20
20
|
|
@@ -47,7 +47,7 @@ All standard html elements are supported and will create the closest equivalent
|
|
47
47
|
|
48
48
|
### Highlighting text
|
49
49
|
|
50
|
-
You can add highlighting to text by wrapping it in a span with class h and adding a data style with a color that wordml supports (
|
50
|
+
You can add highlighting to text by wrapping it in a span with class h and adding a data style with a color that wordml supports (http://www.schemacentral.com/sc/ooxml/t-w_ST_HighlightColor.html) ie:
|
51
51
|
|
52
52
|
```html
|
53
53
|
<span class="h" data-style="green">This text will have a green highlight</span>
|
@@ -70,7 +70,7 @@ The basic functioning of this gem can be summarised as:
|
|
70
70
|
1. Transform inputed html to wordml.
|
71
71
|
2. Unzip empty word docx file bundled with gem and replace its document.xml content with the new transformed result of step 1.
|
72
72
|
3. Zip up contents again into a resulting .docx file.
|
73
|
-
|
73
|
+
|
74
74
|
For more info about WordML: http://rep.oio.dk/microsoft.com/officeschemas/wordprocessingml_article.htm
|
75
75
|
|
76
76
|
Contributions would be very much appreciated.
|
data/lib/htmltoword.rb
CHANGED
@@ -1,84 +1,19 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require "htmltoword/version"
|
3
|
-
require "htmltoword/htmltoword_helper"
|
4
2
|
require "action_controller"
|
5
3
|
require "action_view"
|
6
4
|
require "nokogiri"
|
7
|
-
require "zip
|
5
|
+
require "zip"
|
8
6
|
|
9
7
|
module Htmltoword
|
10
8
|
def self.root
|
11
9
|
File.expand_path '../..', __FILE__
|
12
10
|
end
|
11
|
+
|
13
12
|
def self.templates_path
|
14
13
|
File.join root, "templates"
|
15
14
|
end
|
16
|
-
|
17
|
-
class Document
|
18
|
-
|
19
|
-
DOC_XML_FILE = "word/document.xml"
|
20
|
-
BASIC_PATH = ::Htmltoword.root
|
21
|
-
FILE_EXTENSION = ".docx"
|
22
|
-
XSLT_TEMPLATE = File.join(BASIC_PATH, 'xslt', 'html_to_wordml.xslt')
|
23
|
-
|
24
|
-
class << self
|
25
|
-
include HtmltowordHelper
|
26
|
-
|
27
|
-
def create content, file_name
|
28
|
-
word_file = new(template_file, file_name)
|
29
|
-
word_file.replace_file content
|
30
|
-
word_file.save
|
31
|
-
end
|
32
|
-
|
33
|
-
def create_with_content template, file_name, content, set=nil
|
34
|
-
word_file = new(template_file("#{template}#{FILE_EXTENSION}"), file_name)
|
35
|
-
content = replace_values(content, set) if set
|
36
|
-
word_file.replace_file content
|
37
|
-
word_file.save
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def initialize(template_path, file_name)
|
42
|
-
@file_name = file_name
|
43
|
-
@replaceable_files = {}
|
44
|
-
@template_zip = Zip::ZipFile.open(template_path)
|
45
|
-
end
|
46
|
-
|
47
|
-
def file_name
|
48
|
-
@file_name
|
49
|
-
end
|
50
|
-
|
51
|
-
#
|
52
|
-
# It creates missing folders if needed, creates a new zip/word file on the
|
53
|
-
# specified location, copies all the files from the template word document
|
54
|
-
# and replace the content of the ones to be replaced.
|
55
|
-
# It will create a tempfile and return it. The rails app using the gem
|
56
|
-
# should decide what to do with it.
|
57
|
-
#
|
58
|
-
#
|
59
|
-
def save
|
60
|
-
output_file = Tempfile.new([file_name, FILE_EXTENSION], type: 'application/zip')
|
61
|
-
Zip::ZipOutputStream.open(output_file.path) do |out|
|
62
|
-
@template_zip.each do |entry|
|
63
|
-
out.put_next_entry entry.name
|
64
|
-
if @replaceable_files[entry.name]
|
65
|
-
out.write(@replaceable_files[entry.name])
|
66
|
-
else
|
67
|
-
out.write(@template_zip.read(entry.name))
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
@template_zip.close
|
72
|
-
output_file.close
|
73
|
-
return output_file
|
74
|
-
end
|
75
|
-
|
76
|
-
def replace_file html, file_name=DOC_XML_FILE
|
77
|
-
source = Nokogiri::HTML(html.gsub(/>\s+</, "><"))
|
78
|
-
xslt = Nokogiri::XSLT( File.read(XSLT_TEMPLATE) )
|
79
|
-
source = xslt.transform( source ) unless (source/"/html").blank?
|
80
|
-
@replaceable_files[file_name] = source.to_s
|
81
|
-
end
|
82
|
-
|
83
|
-
end
|
84
15
|
end
|
16
|
+
|
17
|
+
require "htmltoword/version"
|
18
|
+
require "htmltoword/htmltoword_helper"
|
19
|
+
require "htmltoword/document"
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Htmltoword
|
2
|
+
class Document
|
3
|
+
|
4
|
+
DOC_XML_FILE = "word/document.xml"
|
5
|
+
BASIC_PATH = ::Htmltoword.root
|
6
|
+
FILE_EXTENSION = ".docx"
|
7
|
+
XSLT_TEMPLATE = File.join(BASIC_PATH, 'xslt', 'html_to_wordml.xslt')
|
8
|
+
|
9
|
+
class << self
|
10
|
+
include HtmltowordHelper
|
11
|
+
|
12
|
+
def create content, file_name
|
13
|
+
word_file = new(template_file, file_name)
|
14
|
+
word_file.replace_file content
|
15
|
+
word_file.save
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_with_content template, file_name, content, set=nil
|
19
|
+
word_file = new(template_file("#{template}#{FILE_EXTENSION}"), file_name)
|
20
|
+
content = replace_values(content, set) if set
|
21
|
+
word_file.replace_file content
|
22
|
+
word_file.save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(template_path, file_name)
|
27
|
+
@file_name = file_name
|
28
|
+
@replaceable_files = {}
|
29
|
+
@template_path = template_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def file_name
|
33
|
+
@file_name
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# It creates missing folders if needed, creates a new zip/word file on the
|
38
|
+
# specified location, copies all the files from the template word document
|
39
|
+
# and replace the content of the ones to be replaced.
|
40
|
+
# It will create a tempfile and return it. The rails app using the gem
|
41
|
+
# should decide what to do with it.
|
42
|
+
#
|
43
|
+
#
|
44
|
+
def save
|
45
|
+
Tempfile.open([file_name, FILE_EXTENSION], type: 'application/zip') do |output_file|
|
46
|
+
Zip::File.open(@template_path) do |template_zip|
|
47
|
+
Zip::OutputStream.open(output_file.path) do |out|
|
48
|
+
template_zip.each do |entry|
|
49
|
+
out.put_next_entry entry.name
|
50
|
+
if @replaceable_files[entry.name]
|
51
|
+
out.write(@replaceable_files[entry.name])
|
52
|
+
else
|
53
|
+
out.write(template_zip.read(entry.name))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
output_file
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def replace_file html, file_name=DOC_XML_FILE
|
63
|
+
source = Nokogiri::HTML(html.gsub(/>\s+</, "><"))
|
64
|
+
xslt = Nokogiri::XSLT( File.read(XSLT_TEMPLATE) )
|
65
|
+
source = xslt.transform( source ) unless (source/"/html").blank?
|
66
|
+
@replaceable_files[file_name] = source.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
data/lib/htmltoword/version.rb
CHANGED
data/xslt/html_to_wordml.xslt
CHANGED
@@ -17,17 +17,17 @@
|
|
17
17
|
|
18
18
|
<xsl:output method="xml" encoding="utf-8" omit-xml-declaration="no" indent="yes" />
|
19
19
|
|
20
|
-
<xsl:template match="/
|
20
|
+
<xsl:template match="/">
|
21
21
|
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mo="http://schemas.microsoft.com/office/mac/office/2008/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14">
|
22
|
-
<xsl:apply-templates
|
22
|
+
<xsl:apply-templates />
|
23
23
|
</w:document>
|
24
24
|
</xsl:template>
|
25
25
|
|
26
|
+
<xsl:template match="head" />
|
27
|
+
|
26
28
|
<xsl:template match="body">
|
27
29
|
<w:body>
|
28
|
-
<
|
29
|
-
<xsl:apply-templates/>
|
30
|
-
</w:p>
|
30
|
+
<xsl:apply-templates/>
|
31
31
|
<w:sectPr>
|
32
32
|
<w:pgSz w:w="11906" w:h="16838"/>
|
33
33
|
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
|
@@ -37,83 +37,61 @@
|
|
37
37
|
</w:body>
|
38
38
|
</xsl:template>
|
39
39
|
|
40
|
-
<xsl:template match="
|
41
|
-
<xsl:
|
42
|
-
|
43
|
-
<w:r><w:br/></w:r>
|
44
|
-
</xsl:when>
|
45
|
-
<xsl:when test="name(..)='td'">
|
46
|
-
</xsl:when>
|
47
|
-
<xsl:otherwise>
|
48
|
-
<w:pPr><w:pStyle w:val="Afsnit"/></w:pPr><w:r><w:br/></w:r>
|
49
|
-
</xsl:otherwise>
|
50
|
-
</xsl:choose>
|
51
|
-
</xsl:template>
|
52
|
-
|
53
|
-
<xsl:template match="i|em">
|
54
|
-
<w:r>
|
55
|
-
<w:rPr>
|
56
|
-
<w:i />
|
57
|
-
</w:rPr>
|
58
|
-
<xsl:apply-templates />
|
59
|
-
</w:r>
|
60
|
-
</xsl:template>
|
61
|
-
|
62
|
-
<xsl:template match="b|strong">
|
63
|
-
<w:r>
|
64
|
-
<w:rPr>
|
65
|
-
<w:b />
|
66
|
-
</w:rPr>
|
40
|
+
<xsl:template match="div[not(ancestor::p) 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::table)]">
|
41
|
+
<xsl:comment>Divs should create a p if nothing above them has and nothing below them will</xsl:comment>
|
42
|
+
<w:p>
|
67
43
|
<xsl:apply-templates />
|
68
|
-
</w:
|
69
|
-
</xsl:template>
|
70
|
-
|
71
|
-
<xsl:template match="font">
|
72
|
-
<w:r>
|
73
|
-
<xsl:apply-templates />
|
74
|
-
</w:r>
|
75
|
-
</xsl:template>
|
76
|
-
|
77
|
-
<xsl:template match="div[@class='crumbNav']"/>
|
78
|
-
<xsl:template match="small"/>
|
79
|
-
|
80
|
-
<xsl:template match="div[contains(concat(' ', @class, ' '), ' -page-break ')]">
|
81
|
-
<xsl:comment>Making PAGEBREAKS</xsl:comment>
|
82
|
-
<w:r><w:br w:type="page" /></w:r>
|
83
|
-
<xsl:apply-templates select="node()"/>
|
44
|
+
</w:p>
|
84
45
|
</xsl:template>
|
85
46
|
|
86
47
|
<xsl:template match="div">
|
87
|
-
<xsl:
|
88
|
-
<xsl:when test="name(..)='body'">
|
89
|
-
<xsl:apply-templates select="node()"/>
|
90
|
-
</xsl:when>
|
91
|
-
<xsl:when test="./div">
|
92
|
-
<xsl:apply-templates select="node()"/>
|
93
|
-
</xsl:when>
|
94
|
-
<xsl:otherwise>
|
95
|
-
<w:r><w:br/></w:r>
|
96
|
-
<xsl:apply-templates select="node()"/>
|
97
|
-
<w:r><w:br/></w:r>
|
98
|
-
</xsl:otherwise>
|
99
|
-
</xsl:choose>
|
48
|
+
<xsl:apply-templates />
|
100
49
|
</xsl:template>
|
101
50
|
|
102
|
-
<xsl:template match="
|
103
|
-
<
|
51
|
+
<xsl:template match="h1|h2|h3|h4|h5|h6">
|
52
|
+
<w:p>
|
53
|
+
<w:r>
|
54
|
+
<w:rPr>
|
55
|
+
<w:rStyle w:val="{name(.)}"/>
|
56
|
+
</w:rPr>
|
57
|
+
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
58
|
+
</w:r>
|
59
|
+
</w:p>
|
104
60
|
</xsl:template>
|
105
61
|
|
106
|
-
<xsl:template match="
|
107
|
-
<w:
|
108
|
-
|
109
|
-
|
62
|
+
<xsl:template match="p">
|
63
|
+
<w:p>
|
64
|
+
<w:r>
|
65
|
+
<xsl:apply-templates />
|
66
|
+
</w:r>
|
67
|
+
</w:p>
|
110
68
|
</xsl:template>
|
111
69
|
|
112
70
|
<xsl:template match="li">
|
113
|
-
<w:
|
114
|
-
|
115
|
-
|
116
|
-
|
71
|
+
<w:p>
|
72
|
+
<w:r>
|
73
|
+
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
74
|
+
</w:r>
|
75
|
+
</w:p>
|
76
|
+
</xsl:template>
|
77
|
+
|
78
|
+
<xsl:template match="span[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]
|
79
|
+
|a[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]
|
80
|
+
|small[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]">
|
81
|
+
<xsl:comment>
|
82
|
+
In the following situation:
|
83
|
+
|
84
|
+
div
|
85
|
+
h2
|
86
|
+
span
|
87
|
+
textnode
|
88
|
+
p
|
89
|
+
|
90
|
+
The div template will not create a w:p because the div contains a h2. Therefore we need to wrap the span|a|small in a p here.
|
91
|
+
</xsl:comment>
|
92
|
+
<w:p>
|
93
|
+
<xsl:apply-templates />
|
94
|
+
</w:p>
|
117
95
|
</xsl:template>
|
118
96
|
|
119
97
|
<xsl:template match="span[contains(concat(' ', @class, ' '), ' h ')]">
|
@@ -121,6 +99,7 @@
|
|
121
99
|
<xsl:choose>
|
122
100
|
<xsl:when test="./@data-style='pink'">magenta</xsl:when>
|
123
101
|
<xsl:when test="./@data-style='blue'">cyan</xsl:when>
|
102
|
+
<xsl:when test="./@data-style='orange'">darkYellow</xsl:when>
|
124
103
|
<xsl:otherwise><xsl:value-of select="./@data-style"/></xsl:otherwise>
|
125
104
|
</xsl:choose>
|
126
105
|
</xsl:variable>
|
@@ -132,8 +111,13 @@
|
|
132
111
|
</w:r>
|
133
112
|
</xsl:template>
|
134
113
|
|
135
|
-
<xsl:template match="
|
136
|
-
<
|
114
|
+
<xsl:template match="div[contains(concat(' ', @class, ' '), ' -page-break ')]">
|
115
|
+
<w:p>
|
116
|
+
<w:r>
|
117
|
+
<w:br w:type="page" />
|
118
|
+
</w:r>
|
119
|
+
</w:p>
|
120
|
+
<xsl:apply-templates />
|
137
121
|
</xsl:template>
|
138
122
|
|
139
123
|
<xsl:template match="table">
|
@@ -155,74 +139,78 @@
|
|
155
139
|
<w:gridCol w:w="2310"/>
|
156
140
|
<w:gridCol w:w="2310"/>
|
157
141
|
</w:tblGrid>
|
158
|
-
<xsl:apply-templates
|
142
|
+
<xsl:apply-templates />
|
159
143
|
</w:tbl>
|
160
144
|
</xsl:template>
|
161
145
|
|
146
|
+
<xsl:template match="tbody">
|
147
|
+
<xsl:apply-templates />
|
148
|
+
</xsl:template>
|
149
|
+
|
162
150
|
<xsl:template match="tr">
|
163
151
|
<w:tr>
|
164
|
-
<xsl:apply-templates
|
152
|
+
<xsl:apply-templates />
|
165
153
|
</w:tr>
|
166
154
|
</xsl:template>
|
167
155
|
|
168
156
|
<xsl:template match="td">
|
169
157
|
<w:tc>
|
170
|
-
<
|
158
|
+
<w:p>
|
159
|
+
<w:r>
|
160
|
+
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
161
|
+
</w:r>
|
162
|
+
</w:p>
|
171
163
|
</w:tc>
|
172
164
|
</xsl:template>
|
173
165
|
|
174
|
-
<xsl:template match="a">
|
175
|
-
<xsl:apply-templates/>
|
176
|
-
</xsl:template>
|
177
|
-
|
178
|
-
<xsl:template match="h1|h2|h3|h4">
|
179
|
-
<w:r>
|
180
|
-
<w:rPr>
|
181
|
-
<w:rStyle w:val="{name(.)}"/>
|
182
|
-
</w:rPr>
|
183
|
-
<w:br/>
|
184
|
-
<xsl:apply-templates />
|
185
|
-
<w:br/>
|
186
|
-
</w:r>
|
187
|
-
</xsl:template>
|
188
|
-
|
189
166
|
<xsl:template match="text()">
|
190
|
-
<xsl:
|
191
|
-
<xsl:
|
192
|
-
<xsl:
|
193
|
-
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
194
|
-
</xsl:if>
|
195
|
-
</xsl:when>
|
196
|
-
<xsl:when test="name(..)='a' or name(..)='div' or name(..)='span' or name(..)='li' or name(..)='td' or name(..)='p'">
|
197
|
-
<xsl:if test="string-length(.) > 0">
|
167
|
+
<xsl:if test="string-length(.) > 0">
|
168
|
+
<xsl:choose>
|
169
|
+
<xsl:when test="parent::i or parent::em">
|
198
170
|
<w:r>
|
171
|
+
<w:rPr>
|
172
|
+
<w:i />
|
173
|
+
</w:rPr>
|
199
174
|
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
200
175
|
</w:r>
|
201
|
-
</xsl:
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
176
|
+
</xsl:when>
|
177
|
+
<xsl:when test="parent::b or parent::strong">
|
178
|
+
<w:r>
|
179
|
+
<w:rPr>
|
180
|
+
<w:b />
|
181
|
+
</w:rPr>
|
182
|
+
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
183
|
+
</w:r>
|
184
|
+
</xsl:when>
|
185
|
+
<xsl:when test="parent::div 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)">
|
186
|
+
<xsl:comment>
|
187
|
+
In the following situation:
|
188
|
+
|
189
|
+
div
|
190
|
+
h2
|
191
|
+
textnode
|
192
|
+
|
193
|
+
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.
|
194
|
+
</xsl:comment>
|
195
|
+
<w:p>
|
196
|
+
<w:r>
|
197
|
+
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
198
|
+
</w:r>
|
199
|
+
</w:p>
|
200
|
+
</xsl:when>
|
201
|
+
<xsl:otherwise>
|
202
|
+
<w:r>
|
203
|
+
<xsl:comment>text() fallback</xsl:comment>
|
204
|
+
<w:t xml:space="preserve"><xsl:value-of select="."/></w:t>
|
205
|
+
</w:r>
|
206
|
+
</xsl:otherwise>
|
207
|
+
</xsl:choose>
|
208
|
+
</xsl:if>
|
225
209
|
</xsl:template>
|
226
210
|
|
227
|
-
</xsl:stylesheet>
|
228
211
|
|
212
|
+
<xsl:template match="*">
|
213
|
+
<xsl:apply-templates/>
|
214
|
+
</xsl:template>
|
215
|
+
|
216
|
+
</xsl:stylesheet>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: htmltoword
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -48,17 +48,17 @@ dependencies:
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 1.0.0
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
none: false
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 1.0.0
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: rspec
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,21 +114,15 @@ executables: []
|
|
114
114
|
extensions: []
|
115
115
|
extra_rdoc_files: []
|
116
116
|
files:
|
117
|
-
- .
|
118
|
-
- .travis.yml
|
119
|
-
- Gemfile
|
120
|
-
- LICENSE.txt
|
121
|
-
- README.md
|
122
|
-
- Rakefile
|
123
|
-
- htmltoword.gemspec
|
124
|
-
- lib/htmltoword.rb
|
117
|
+
- lib/htmltoword/document.rb
|
125
118
|
- lib/htmltoword/htmltoword_helper.rb
|
126
119
|
- lib/htmltoword/version.rb
|
127
|
-
-
|
128
|
-
- spec/xslt_spec.rb
|
120
|
+
- lib/htmltoword.rb
|
129
121
|
- templates/default.docx
|
130
122
|
- xslt/html_to_wordml.xslt
|
131
123
|
- xslt/style2.xslt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
132
126
|
homepage: http://github.com/nickfrandsen/htmltoword
|
133
127
|
licenses:
|
134
128
|
- MIT
|
@@ -156,6 +150,4 @@ specification_version: 3
|
|
156
150
|
summary: This simple gem allows you to create MS Word docx documents from simple html
|
157
151
|
documents. This makes it easy to create dynamic reports and forms that can be downloaded
|
158
152
|
by your users as simple MS Word docx files.
|
159
|
-
test_files:
|
160
|
-
- spec/spec_helper.rb
|
161
|
-
- spec/xslt_spec.rb
|
153
|
+
test_files: []
|
data/.gitignore
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2013 Nicholas Frandsen
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/htmltoword.gemspec
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'htmltoword/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "htmltoword"
|
8
|
-
spec.version = Htmltoword::VERSION
|
9
|
-
spec.authors = ["Nicholas Frandsen"]
|
10
|
-
spec.email = ["nick.rowe.frandsen@gmail.com"]
|
11
|
-
spec.description = %q{Convert html to word docx document.}
|
12
|
-
spec.summary = %q{This simple gem allows you to create MS Word docx documents from simple html documents. This makes it easy to create dynamic reports and forms that can be downloaded by your users as simple MS Word docx files.}
|
13
|
-
spec.homepage = "http://github.com/nickfrandsen/htmltoword"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files`.split($/)
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_dependency "actionpack"
|
22
|
-
spec.add_dependency "nokogiri"
|
23
|
-
spec.add_dependency "rubyzip"
|
24
|
-
spec.add_development_dependency "rspec"
|
25
|
-
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
-
spec.add_development_dependency "rake"
|
27
|
-
end
|
data/spec/spec_helper.rb
DELETED
data/spec/xslt_spec.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "XSLT" do
|
4
|
-
|
5
|
-
it "transforms an empty html doc into an empty docx doc" do
|
6
|
-
html = '<html><head></head><body></body></html>'
|
7
|
-
compare_resulting_wordml_with_expected(html, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<w:document xmlns:wpc=\"http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas\" xmlns:mo=\"http://schemas.microsoft.com/office/mac/office/2008/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:mv=\"urn:schemas-microsoft-com:mac:vml\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\" xmlns:wp14=\"http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing\" xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" xmlns:w14=\"http://schemas.microsoft.com/office/word/2010/wordml\" xmlns:wpg=\"http://schemas.microsoft.com/office/word/2010/wordprocessingGroup\" xmlns:wpi=\"http://schemas.microsoft.com/office/word/2010/wordprocessingInk\" xmlns:wne=\"http://schemas.microsoft.com/office/word/2006/wordml\" xmlns:wps=\"http://schemas.microsoft.com/office/word/2010/wordprocessingShape\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:pkg=\"http://schemas.microsoft.com/office/2006/xmlPackage\" xmlns:str=\"http://exslt.org/common\" xmlns:fn=\"http://www.w3.org/2005/xpath-functions\" mc:Ignorable=\"w14 wp14\">\n <w:body>\n <w:p/> <w:sectPr>\n <w:pgSz w:w=\"11906\" w:h=\"16838\"/>\n <w:pgMar w:top=\"1440\" w:right=\"1440\" w:bottom=\"1440\" w:left=\"1440\" w:header=\"708\" w:footer=\"708\" w:gutter=\"0\"/>\n <w:cols w:space=\"708\"/>\n <w:docGrid w:linePitch=\"360\"/>\n </w:sectPr>\n </w:body>\n</w:document>\n")
|
8
|
-
end
|
9
|
-
|
10
|
-
it "transforms a div into a docx block element." do
|
11
|
-
html = '<html><head></head><body><div>Hello</div></body></html>'
|
12
|
-
compare_resulting_wordml_with_expected(html, "<w:p> <w:r> <w:t xml:space=\"preserve\">Hello</w:t> </w:r> </w:p>")
|
13
|
-
end
|
14
|
-
|
15
|
-
context "transform a span" do
|
16
|
-
|
17
|
-
it "into a docx block elmenet if child of body." do
|
18
|
-
html = '<html><head></head><body><span>Hello</span></body></html>'
|
19
|
-
compare_resulting_wordml_with_expected(html, "<w:p> <w:r> <w:t xml:space=\"preserve\">Hello</w:t> </w:r> </w:p>")
|
20
|
-
end
|
21
|
-
|
22
|
-
it "into a docx inline element if not child of body." do
|
23
|
-
html = '<html><head></head><body><div><span>Hello</span></div></body></html>'
|
24
|
-
compare_resulting_wordml_with_expected(html, "<w:p> <w:r> <w:t xml:space=\"preserve\">Hello</w:t> </w:r> </w:p>")
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
it "transforms a p into a docx block element." do
|
30
|
-
html = '<html><head></head><body><p>Hello</p></body></html>'
|
31
|
-
compare_resulting_wordml_with_expected(html, "<w:p> <w:r> <w:t xml:space=\"preserve\">Hello</w:t> </w:r> </w:p>")
|
32
|
-
end
|
33
|
-
|
34
|
-
protected
|
35
|
-
|
36
|
-
def compare_resulting_wordml_with_expected(html, resulting_wordml)
|
37
|
-
source = Nokogiri::HTML(html.gsub(/>\s+</, "><"))
|
38
|
-
xslt = Nokogiri::XSLT( File.read(Htmltoword::Document::XSLT_TEMPLATE))
|
39
|
-
result = xslt.transform(source)
|
40
|
-
if compare_content_of_body?(resulting_wordml)
|
41
|
-
result.at("//w:sectPr").remove
|
42
|
-
result = result.at("//w:body/*")
|
43
|
-
end
|
44
|
-
result.to_s.gsub(/\s+/, " ").should == resulting_wordml.gsub(/\s+/, " ")
|
45
|
-
end
|
46
|
-
|
47
|
-
def compare_content_of_body?(wordml)
|
48
|
-
wordml !~ /<?xml version/
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|