├── .travis.yml
├── lib
├── htmltoword
│ ├── version.rb
│ ├── templates
│ │ └── default.docx
│ ├── configuration.rb
│ ├── htmltoword_helper.rb
│ ├── action_controller.rb
│ ├── document.rb
│ └── xslt
│ │ ├── style2.xslt
│ │ └── html_to_wordml.xslt
└── htmltoword.rb
├── Gemfile
├── Rakefile
├── .gitignore
├── spec
├── spec_helper.rb
├── xslt_spec.rb
├── xslt_alignment_spec.rb
├── xslt_tables_spec.rb
├── xslt_heading_spec.rb
└── xslt_simple_text_style_spec.rb
├── LICENSE.txt
├── htmltoword.gemspec
├── CHANGELOG.txt
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm: "1.9.3"
--------------------------------------------------------------------------------
/lib/htmltoword/version.rb:
--------------------------------------------------------------------------------
1 | module Htmltoword
2 | VERSION = "0.2.1"
3 | end
4 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in htmltoword.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require 'rspec/core/rake_task'
3 | task :default => :spec
4 | RSpec::Core::RakeTask.new
--------------------------------------------------------------------------------
/lib/htmltoword/templates/default.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nickfrandsen/htmltoword/HEAD/lib/htmltoword/templates/default.docx
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 | .idea/*
19 |
--------------------------------------------------------------------------------
/lib/htmltoword/configuration.rb:
--------------------------------------------------------------------------------
1 | module Htmltoword
2 | class Configuration
3 | attr_accessor :default_templates_path, :custom_templates_path, :default_xslt_path, :custom_xslt_path
4 |
5 | def initialize
6 | @default_templates_path = File.join(File.expand_path('../', __FILE__), 'templates')
7 | @custom_templates_path = File.join(File.expand_path('../', __FILE__), 'templates')
8 | @default_xslt_path = File.join(File.expand_path('../', __FILE__), 'xslt')
9 | @custom_xslt_path = File.join(File.expand_path('../', __FILE__), 'xslt')
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/htmltoword.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require 'action_controller'
3 | require 'action_view'
4 | require 'nokogiri'
5 | require 'zip'
6 | require 'htmltoword/configuration'
7 |
8 | module Htmltoword
9 | class << self
10 | def configure
11 | yield configuration
12 | end
13 |
14 | def configuration
15 | @configuration ||= Configuration.new
16 | end
17 |
18 | alias :config :configuration
19 | end
20 | end
21 |
22 |
23 | require 'htmltoword/version'
24 | require 'htmltoword/htmltoword_helper'
25 | require 'htmltoword/document'
26 | require 'htmltoword/action_controller'
27 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'bundler/setup'
3 | require 'htmltoword'
4 |
5 | def compare_resulting_wordml_with_expected(html, resulting_wordml)
6 | source = Nokogiri::HTML(html.gsub(/>\s+, "><"))
7 | xslt = Nokogiri::XSLT( File.read(Htmltoword::Document.default_xslt_template))
8 | result = xslt.transform(source)
9 | if compare_content_of_body?(resulting_wordml)
10 | result.at("//w:sectPr").remove
11 | result = result.at("//w:body")
12 | end
13 | result.xpath('//comment()').remove
14 | expect(remove_whitespace(result.to_s)).to eq(remove_whitespace(resulting_wordml))
15 | end
16 |
17 | def compare_content_of_body?(wordml)
18 | wordml !~ / file_name, :type => Mime::DOCX, :disposition => disposition
37 | end
38 |
39 | # For respond_with default
40 | begin
41 | ActionController::Responder
42 | rescue LoadError
43 | else
44 | class ActionController::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 |
--------------------------------------------------------------------------------
/CHANGELOG.txt:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 |
3 | Version 0.2
4 | * Relocation of templates and xslt files
5 | * Enable gem configuration to set the location of default and custom word templates and xslt.
6 | * Allow the Htmltoword::Document.create to receive custom templates
7 | * Add .docx as a renderer when used with rails.
8 |
9 | Version 0.1.8
10 | * Better alignment of text within a paragraph, div or table cell if it has a 'center', 'left', 'right', 'justify' class or by using the text-align property.
11 | * Support for table-bordered class on tables
12 | * Support for bold and italic text on table cells
13 | * Support for h1..h6 on table cells
14 | * Support for nested tables
15 | * Bugfix: thead without tr tag
16 | * Bugfix: nested bold and italic on paragraphs or div
17 | * Bugfix: allow
Some content inside a div | syntax
18 | * Bugfix: Headings in body tag are now correctly parsed
19 |
20 | Version 0.1.7
21 | * Allow centering of text within a paragraph if the paragraph has a 'center' class, e.g.
22 |
23 | Version 0.1.6:
24 | * Add tables support for borders and headers
25 |
26 | Version 0.1.5:
27 | * Bugfix: h5 & h6 also create w:p's so any wrapping divs dont need to.
28 |
29 | Version 0.1.4:
30 | * Details tag doesnt really work well for printing. By default its closed and not visible until you click on the summary so for now lets ignore it in the print out.
31 |
32 | Version 0.1.3:
33 | * Bugfix: Leaf i and b nodes with w:p creating siblings need to create their own w:p's.
34 |
35 | Version 0.1.2:
36 | * Bugfix: span.h (MS Word Highlights) with sibling block elements could result in invalid wordml.
37 |
38 | Version 0.1.1:
39 | * Nodes with neighbor nodes that create w:p's need to be wrapped in w:p themselves.
40 | * Fixed test suite.
41 |
42 | Version 0.1.0:
43 | * Rewrote xslt to support tables and updated rubyzip to v1.
44 |
45 | Version 0.0.1:
46 | * First basic implementation of htmltoword released.
47 |
--------------------------------------------------------------------------------
/lib/htmltoword/document.rb:
--------------------------------------------------------------------------------
1 | module Htmltoword
2 | class Document
3 | class << self
4 | include HtmltowordHelper
5 |
6 | def create content, file_name, template_name=nil
7 | file_name += extension unless file_name =~ /\.docx$/
8 | template_name += extension if template_name && !(template_name =~ /\.docx$/)
9 | word_file = new(template_file(template_name), file_name)
10 | word_file.replace_file content
11 | word_file.save
12 | end
13 |
14 | def create_with_content template, file_name, content, set=nil
15 | template += extension unless template =~ /\.docx$/
16 | word_file = new(template_file(template), file_name)
17 | content = replace_values(content, set) if set
18 | word_file.replace_file content
19 | word_file.save
20 | end
21 |
22 | def extension
23 | '.docx'
24 | end
25 |
26 | def doc_xml_file
27 | 'word/document.xml'
28 | end
29 |
30 | def default_xslt_template
31 | File.join(Htmltoword.config.default_xslt_path, 'html_to_wordml.xslt')
32 | end
33 | end
34 |
35 | def initialize(template_path, file_name)
36 | @file_name = file_name
37 | @replaceable_files = {}
38 | @template_path = template_path
39 | end
40 |
41 | def file_name
42 | @file_name
43 | end
44 |
45 | #
46 | # It creates missing folders if needed, creates a new zip/word file on the
47 | # specified location, copies all the files from the template word document
48 | # and replace the content of the ones to be replaced.
49 | # It will create a tempfile and return it. The rails app using the gem
50 | # should decide what to do with it.
51 | #
52 | #
53 | def save
54 | Tempfile.open([file_name, Document.extension], type: 'application/zip') do |output_file|
55 | Zip::File.open(@template_path) do |template_zip|
56 | Zip::OutputStream.open(output_file.path) do |out|
57 | template_zip.each do |entry|
58 | out.put_next_entry entry.name
59 | if @replaceable_files[entry.name]
60 | out.write(@replaceable_files[entry.name])
61 | else
62 | out.write(template_zip.read(entry.name))
63 | end
64 | end
65 | end
66 | end
67 | output_file
68 | end
69 | end
70 |
71 | def replace_file html, file_name=Document.doc_xml_file
72 | html = html.presence || '
'
73 | source = Nokogiri::HTML(html.gsub(/>\s+, "><"))
74 | xslt = Nokogiri::XSLT( File.read(Document.default_xslt_template) )
75 | source = xslt.transform( source ) unless (source/"/html").blank?
76 | @replaceable_files[file_name] = source.to_s
77 | end
78 |
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/lib/htmltoword/xslt/style2.xslt:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Im block
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/spec/xslt_spec.rb:
--------------------------------------------------------------------------------
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 = ''
7 | compare_resulting_wordml_with_expected(html, " ")
8 | end
9 |
10 | it "transforms a div into a docx block element." do
11 | html = 'Hello
'
12 | compare_resulting_wordml_with_expected(html, " Hello ")
13 | end
14 |
15 | context "transform a span" do
16 |
17 | it "into a docx block element if child of body." do
18 | html = 'Hello'
19 | compare_resulting_wordml_with_expected(html, " Hello ")
20 | end
21 |
22 | it "into a docx inline element if not child of body." do
23 | html = 'Hello
'
24 | compare_resulting_wordml_with_expected(html, " Hello ")
25 | end
26 |
27 | end
28 |
29 | it "transforms a p into a docx block element." do
30 | html = 'Hello
'
31 | compare_resulting_wordml_with_expected(html, " Hello ")
32 | end
33 |
34 | it "Should strip out details tags" do
35 | html = ''
36 | compare_resulting_wordml_with_expected(html, " Hello ")
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ruby Html to word Gem [](https://codeclimate.com/github/nickfrandsen/htmltoword) [](https://travis-ci.org/nickfrandsen/htmltoword)
2 |
3 | ## OBS: This repository is no longer being maintained. Please take a look at https://github.com/karnov/htmltoword
4 |
5 | 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.
6 |
7 | Add this line to your application's Gemfile:
8 |
9 | gem 'htmltoword'
10 |
11 | And then execute:
12 |
13 | $ bundle
14 |
15 | Or install it yourself as:
16 |
17 | $ gem install htmltoword
18 |
19 | ## Usage
20 |
21 | ### Standalone
22 |
23 | Using the default word file as template
24 | ```ruby
25 | require 'htmltoword'
26 |
27 | my_html = 'Hello
'
28 | file = Htmltoword::Document.create my_html, file_name
29 | ```
30 |
31 | Using your custom word file as a template, where you can setup your own style for normal text, h1,h2, etc.
32 | ```ruby
33 | require 'htmltoword'
34 |
35 | # Configure the location of your custom templates
36 | Htmltoword.config.custom_templates_path = 'some_path'
37 |
38 | my_html = 'Hello
'
39 | file = Htmltoword::Document.create my_html, file_name, word_template_file_name
40 | ```
41 |
42 | ### With Rails
43 | **For htmltoword version >= 0.2**
44 | An action controller renderer has been defined, so there's no need to declare the mime-type and you can just respond to .docx format. It will look then for views with the extension ```.docx.erb``` which will provide the HTML that will be rendered in the Word file.
45 |
46 | ```ruby
47 | # On your controller.
48 | respond_to :docx
49 |
50 | # filename and word_template are optional. By default it will name the file as your action and use the default template provided by the gem. The use of the .docx in the filename and word_template is optional.
51 | def my_action
52 | # ...
53 | respond_with(@object, filename: 'my_file.docx', word_template: 'my_template.docx')
54 | # Alternatively, if you don't want to create the .docx.erb template you could
55 | respond_with(@object, content: 'some html', filename: 'my_file.docx')
56 | end
57 |
58 | def my_action2
59 | # ...
60 | respond_to do |format|
61 | format.docx do
62 | render docx: 'my_view', filename: 'my_file.docx'
63 | # Alternatively, if you don't want to create the .docx.erb template you could
64 | render docx: 'my_file.docx', content: 'some html'
65 | end
66 | end
67 | end
68 | ```
69 |
70 | Example of my_view.docx.erb
71 | ```
72 | My custom template
73 | <%= render partial: 'my_partial', collection: @objects, as: :item %>
74 | ```
75 | Example of _my_partial.docx.erb
76 | ```
77 | <%= item.title %>
78 | My html for item <%= item.id %> goes here
79 | ```
80 |
81 | **For htmltoword version <= 0.1.8**
82 | ```ruby
83 | # Add mime-type in /config/initializers/mime_types.rb:
84 | Mime::Type.register "application/vnd.openxmlformats-officedocument.wordprocessingml.document", :docx
85 |
86 | # Add docx responder in your controller
87 | def show
88 | respond_to do |format|
89 | format.docx do
90 | file = Htmltoword::Document.create params[:docx_html_source], "file_name.docx"
91 | send_file file.path, :disposition => "attachment"
92 | end
93 | end
94 | end
95 | ```
96 |
97 | ```javascript
98 | // OPTIONAL: Use a jquery click handler to store the markup in a hidden form field before the form is submitted.
99 | // Using this strategy makes it easy to allow users to dynamically edit the document that will be turned
100 | // into a docx file, for example by toggling sections of a document.
101 | $('#download-as-docx').on('click', function () {
102 | $('input[name="docx_html_source"]').val('\n' + $('.delivery').html());
103 | });
104 | ```
105 |
106 | ### Configure templates and xslt paths
107 |
108 | From version 2.0 you can configure the location of default and custom templates and xslt files. By default templates are defined under ```lib/htmltoword/templates``` and xslt under ```lib/htmltoword/xslt```
109 |
110 | ```ruby
111 | Htmltoword.configure do |config|
112 | config.custom_templates_path = 'path_for_custom_templates'
113 | # If you modify this path, there should be a 'default.docx' file in there
114 | config.default_templates_path = 'path_for_default_template'
115 | # If you modify this path, there should be a 'html_to_wordml.xslt' file in there
116 | config.default_xslt_path = 'some_path'
117 | # The use of additional custom xslt will come soon
118 | config.custom_xslt_path = 'some_path'
119 | end
120 | ```
121 |
122 | ## Features
123 |
124 | All standard html elements are supported and will create the closest equivalent in wordml. For example spans will create inline elements and divs will create block like elements.
125 |
126 | ### Highlighting text
127 |
128 | 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:
129 |
130 | ```html
131 | This text will have a green highlight
132 | ```
133 |
134 | ### Page breaks
135 |
136 | To create page breaks simply add a div with class -page-break ie:
137 |
138 | ```html
139 |
140 | ````
141 |
142 | ## Contributing / Extending
143 |
144 | Word docx files are essentially just a zipped collection of xml files and resources.
145 | This gem contains a standard empty MS Word docx file and a stylesheet to transform arbitrary html into wordml.
146 | The basic functioning of this gem can be summarised as:
147 |
148 | 1. Transform inputed html to wordml.
149 | 2. Unzip empty word docx file bundled with gem and replace its document.xml content with the new transformed result of step 1.
150 | 3. Zip up contents again into a resulting .docx file.
151 |
152 | For more info about WordML: http://rep.oio.dk/microsoft.com/officeschemas/wordprocessingml_article.htm
153 |
154 | Contributions would be very much appreciated.
155 |
156 | 1. Fork it
157 | 2. Create your feature branch (`git checkout -b my-new-feature`)
158 | 3. Commit your changes (`git commit -am 'Add some feature'`)
159 | 4. Push to the branch (`git push origin my-new-feature`)
160 | 5. Create new Pull Request
161 |
162 | ## License
163 |
164 | (The MIT License)
165 |
166 | Copyright © 2013:
167 |
168 | * Cristina Matonte
169 |
170 | * Nicholas Frandsen
171 |
--------------------------------------------------------------------------------
/spec/xslt_alignment_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "XSLT to align div, p and td tags" do
4 |
5 | it "transforms a p element with the correct alignment." do
6 | html = <<-EOL
7 |
8 |
9 |
10 |
11 | p using text-aligned center
12 | p using text-aligned right
13 | p using text-aligned left
14 |
15 | Praesent commodo leo et tincidunt tincidunt. Aliquam vestibulum vehicula accumsan. In suscipit nunc vitae facilisis mattis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin fringilla, odio in rhoncus tincidunt, mauris lectus gravida nibh, ac consectetur est arcu a turpis. Proin sodales tellus imperdiet, auctor ante sed, pulvinar nisl. Aenean ultricies elementum leo, in mattis dolor dapibus feugiat. Nunc scelerisque nec purus ac tempus. Praesent at velit ac ipsum hendrerit auctor. Nam dui nunc, ultrices quis aliquet in, pellentesque quis diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
16 |
17 | p class center and strong
18 | p class right and strong
19 | p class left and strong
20 |
21 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ornare sem at sapien accumsan, in pellentesque elit consectetur. Mauris quis dui a magna scelerisque ornare. Vivamus scelerisque sollicitudin ante, auctor dictum nunc iaculis sed. Nullam lobortis ligula odio, a dapibus augue malesuada eget. Nam ac justo nunc. Vestibulum tristique diam sit amet ornare maximus. Duis sit amet libero elit. Proin massa nunc, rutrum nec odio et, hendrerit egestas dui. Donec sit amet aliquam libero.
22 |
23 | Just a p
24 |
25 |
26 | EOL
27 | expected_wordml = <<-EOL
28 |
29 |
30 |
31 |
32 |
33 |
34 | p using text-aligned center
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | p using text-aligned right
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | p using text-aligned left
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Praesent commodo leo et tincidunt tincidunt. Aliquam vestibulum vehicula accumsan. In suscipit nunc vitae facilisis mattis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin fringilla, odio in rhoncus tincidunt, mauris lectus gravida nibh, ac consectetur est arcu a turpis. Proin sodales tellus imperdiet, auctor ante sed, pulvinar nisl. Aenean ultricies elementum leo, in mattis dolor dapibus feugiat. Nunc scelerisque nec purus ac tempus. Praesent at velit ac ipsum hendrerit auctor. Nam dui nunc, ultrices quis aliquet in, pellentesque quis diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | p class center and strong
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | p class right and strong
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | p class left and strong
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ornare sem at sapien accumsan, in pellentesque elit consectetur. Mauris quis dui a magna scelerisque ornare. Vivamus scelerisque sollicitudin ante, auctor dictum nunc iaculis sed. Nullam lobortis ligula odio, a dapibus augue malesuada eget. Nam ac justo nunc. Vestibulum tristique diam sit amet ornare maximus. Duis sit amet libero elit. Proin massa nunc, rutrum nec odio et, hendrerit egestas dui. Donec sit amet aliquam libero.
103 |
104 |
105 |
106 |
107 |
108 | Just a p
109 |
110 |
111 |
112 | EOL
113 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
114 | end
115 |
116 | it "transforms a table having its cells properly aligned" do
117 | html = <<-EOL
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | | Left aligned text |
126 | Right aligned text |
127 | Center aligned text |
128 | Justify aligned text |
129 |
130 |
131 |
132 |
133 | | To the left |
134 | To the right |
135 | Centered |
136 | Justified |
137 |
138 |
139 | | To the left |
140 | To the right |
141 | Centered |
142 | Justified |
143 |
144 |
145 |
146 |
147 |
148 | EOL
149 | expected_wordml = <<-EOL
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 | Left aligned text
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 | Right aligned text
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | Center aligned text
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | Justify aligned text
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | To the left
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 | To the right
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | Centered
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 | Justified
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 | To the left
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 | To the right
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 | Centered
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 | Justified
291 |
292 |
293 |
294 |
295 |
296 |
297 | EOL
298 |
299 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
300 | end
301 |
302 | it "transforms div element with the correct alignment" do
303 | html = <<-EOL
304 |
305 |
306 |
307 |
308 | div using text-aligned center
309 | div using text-aligned right
310 | div using text-aligned left
311 |
312 | Praesent commodo leo et tincidunt tincidunt. Aliquam vestibulum vehicula accumsan. In suscipit nunc vitae facilisis mattis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin fringilla, odio in rhoncus tincidunt, mauris lectus gravida nibh, ac consectetur est arcu a turpis. Proin sodales tellus imperdiet, auctor ante sed, pulvinar nisl. Aenean ultricies elementum leo, in mattis dolor dapibus feugiat. Nunc scelerisque nec purus ac tempus. Praesent at velit ac ipsum hendrerit auctor. Nam dui nunc, ultrices quis aliquet in, pellentesque quis diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
313 |
314 | div class center and strong
315 | div class right and strong
316 | div class left and strong
317 |
318 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ornare sem at sapien accumsan, in pellentesque elit consectetur. Mauris quis dui a magna scelerisque ornare. Vivamus scelerisque sollicitudin ante, auctor dictum nunc iaculis sed. Nullam lobortis ligula odio, a dapibus augue malesuada eget. Nam ac justo nunc. Vestibulum tristique diam sit amet ornare maximus. Duis sit amet libero elit. Proin massa nunc, rutrum nec odio et, hendrerit egestas dui. Donec sit amet aliquam libero.
319 |
320 | Just a div
321 |
322 |
323 | EOL
324 | expected_wordml = <<-EOL
325 |
326 |
327 |
328 |
329 |
330 |
331 | div using text-aligned center
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 | div using text-aligned right
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 | div using text-aligned left
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 | Praesent commodo leo et tincidunt tincidunt. Aliquam vestibulum vehicula accumsan. In suscipit nunc vitae facilisis mattis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin fringilla, odio in rhoncus tincidunt, mauris lectus gravida nibh, ac consectetur est arcu a turpis. Proin sodales tellus imperdiet, auctor ante sed, pulvinar nisl. Aenean ultricies elementum leo, in mattis dolor dapibus feugiat. Nunc scelerisque nec purus ac tempus. Praesent at velit ac ipsum hendrerit auctor. Nam dui nunc, ultrices quis aliquet in, pellentesque quis diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 | div class center and strong
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 | div class right and strong
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 | div class left and strong
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ornare sem at sapien accumsan, in pellentesque elit consectetur. Mauris quis dui a magna scelerisque ornare. Vivamus scelerisque sollicitudin ante, auctor dictum nunc iaculis sed. Nullam lobortis ligula odio, a dapibus augue malesuada eget. Nam ac justo nunc. Vestibulum tristique diam sit amet ornare maximus. Duis sit amet libero elit. Proin massa nunc, rutrum nec odio et, hendrerit egestas dui. Donec sit amet aliquam libero.
400 |
401 |
402 |
403 |
404 |
405 | Just a div
406 |
407 |
408 |
409 | EOL
410 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
411 | end
412 |
413 | it "transforms nested divs with proper alignment" do
414 | # TODO: Known bug, not implemented yet.
415 | # -> else won’t be centered.
416 | end
417 | end
418 |
--------------------------------------------------------------------------------
/lib/htmltoword/xslt/html_to_wordml.xslt:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | KNOWN BUGS:
31 | div
32 | h2
33 | div
34 | textnode (WONT BE WRAPPED IN A W:P)
35 | div
36 | table
37 | span
38 | text
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Divs should create a p if nothing above them has and nothing below them will
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
108 |
109 | In the following situation:
110 |
111 | div
112 | h2
113 | span
114 | textnode
115 | span
116 | textnode
117 | p
118 |
119 | The div template will not create a w:p because the div contains a h2. Therefore we need to wrap the inline elements span|a|small in a p here.
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | In the following situation:
129 |
130 | div
131 | h2
132 | textnode
133 | p
134 |
135 | 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.
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | This template adds MS Word highlighting ability.
147 |
148 |
149 |
150 | magenta
151 | cyan
152 | darkYellow
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | 6
193 | 0
194 |
195 |
196 |
197 |
198 |
199 | none
200 | single
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 | center
342 | right
343 | left
344 | both
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
--------------------------------------------------------------------------------
/spec/xslt_tables_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "XSLT for tables" do
4 |
5 | it "transforms a table into a tbl element" do
6 | html = <<-EOL
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | | Hello |
15 |
16 |
17 |
18 |
19 |
20 | EOL
21 | expected_wordml = <<-EOL
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Hello
46 |
47 |
48 |
49 |
50 |
51 |
52 | EOL
53 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
54 | end
55 |
56 | it "transforms a nested table" do
57 | html = <<-EOL
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | | Cell 1,1 |
66 |
67 |
68 |
69 |
70 | | Nested |
71 | Table |
72 |
73 |
74 |
75 | |
76 | Cell 1,3 |
77 |
78 |
79 |
80 |
81 |
82 | EOL
83 | expected_wordml = <<-EOL
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | Cell 1,1
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | Nested
135 |
136 |
137 |
138 |
139 |
140 |
141 | Table
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | Cell 1,3
153 |
154 |
155 |
156 |
157 |
158 |
159 | EOL
160 |
161 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
162 | end
163 |
164 | it "transforms tables with empty cells" do
165 | html = <<-EOL
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | | Cell 1,1 |
174 | |
175 |
176 |
177 |
178 |
179 |
180 | EOL
181 | expected_wordml = <<-EOL
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 | Cell 1,1
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | EOL
219 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
220 | end
221 |
222 | it "transform tables with empty headers" do
223 | html = <<-EOL
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 | |
232 | Header 2 |
233 |
234 |
235 |
236 |
237 | | Cell 1,1 |
238 | Cell 1,2 |
239 |
240 |
241 |
242 |
243 |
244 | EOL
245 | expected_wordml = <<-EOL
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 | Header 2
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 | Cell 1,1
295 |
296 |
297 |
298 |
299 |
300 |
301 | Cell 1,2
302 |
303 |
304 |
305 |
306 |
307 |
308 | EOL
309 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
310 | end
311 |
312 | it "transforms tables without tag on " do
313 | html = <<-EOL
314 |
315 |
316 |
317 |
318 |
319 |
320 | | Header 1 |
321 | |
322 |
323 |
324 |
325 | | Cell 1,1 |
326 | Cell 1,2 |
327 |
328 |
329 |
330 |
331 |
332 | EOL
333 | expected_wordml = <<-EOL
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 | Header 1
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 | Cell 1,1
383 |
384 |
385 |
386 |
387 |
388 |
389 | Cell 1,2
390 |
391 |
392 |
393 |
394 |
395 |
396 | EOL
397 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
398 | end
399 |
400 | it "transforms tables with border attribute and table-bordered class" do
401 | html = <<-EOL
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 | | Hello |
410 | World |
411 |
412 |
413 |
414 | Using table-bordered class
415 |
416 |
417 |
418 | | Hello world |
419 | Part 2 |
420 |
421 |
422 |
423 |
424 |
425 | EOL
426 |
427 | expected_wordml = <<-EOL
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 | Hello
452 |
453 |
454 |
455 |
456 |
457 |
458 | World
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 | Using table-bordered class
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 | Hello world
492 |
493 |
494 |
495 |
496 |
497 |
498 | Part 2
499 |
500 |
501 |
502 |
503 |
504 |
505 | EOL
506 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
507 | end
508 |
509 | it "transforms nested elements inside table cells" do
510 | html = <<-EOL
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 | Pre H1 This is a H1 Post H1 |
519 | Text A paragraph with Strong text More text |
520 |
521 |
522 | Some content inside a div |
523 | |
524 |
525 |
526 | | Something Inside a p strong and strong em |
527 | Text inside div |
528 |
529 |
530 |
531 |
532 |
533 | EOL
534 | expected_wordml = <<-EOL
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 | Pre H1
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 | This is a H1
567 |
568 |
569 |
570 |
571 | Post H1
572 |
573 |
574 |
575 |
576 |
577 |
578 | Text
579 |
580 |
581 |
582 |
583 | A paragraph with
584 |
585 |
586 |
587 |
588 |
589 | Strong
590 |
591 |
592 | text
593 |
594 |
595 |
596 |
597 | More text
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 | Some content
607 |
608 |
609 |
610 |
611 |
612 | inside
613 |
614 |
615 | a
616 |
617 |
618 |
619 |
620 |
621 | div
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 | Something
634 |
635 |
636 |
637 |
638 | Inside a p
639 |
640 |
641 |
642 |
643 |
644 | strong
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 | and strong em
654 |
655 |
656 |
657 |
658 |
659 |
660 | Text inside div
661 |
662 |
663 |
664 |
665 |
666 |
667 | EOL
668 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
669 | end
670 | end
671 |
--------------------------------------------------------------------------------
/spec/xslt_heading_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "XSLT for Headings" do
4 |
5 | it "transforms heading tags in the body" do
6 | html = <<-EOL
7 |
8 |
9 |
10 |
11 | Heading 1
12 | Heading 2
13 | Heading 3
14 | Heading 4
15 | Heading 5
16 | Heading 6
17 |
18 |
19 | EOL
20 | expected_wordml = <<-EOL
21 |
22 |
23 |
24 |
25 |
26 |
27 | Heading 1
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Heading 2
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Heading 3
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | Heading 4
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Heading 5
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Heading 6
68 |
69 |
70 |
71 | EOL
72 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
73 | end
74 |
75 | it "transforms heading tags in a div" do
76 | html = <<-EOL
77 |
78 |
79 |
80 |
81 |
82 |
Heading 1
83 | Heading 2
84 | Heading 3
85 | Heading 4
86 | Heading 5
87 | Heading 6
88 |
89 |
90 |
91 | EOL
92 | expected_wordml = <<-EOL
93 |
94 |
95 |
96 |
97 |
98 |
99 | Heading 1
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | Heading 2
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | Heading 3
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | Heading 4
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | Heading 5
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | Heading 6
140 |
141 |
142 |
143 | EOL
144 |
145 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
146 | end
147 |
148 | it "transforms heading tags in a table" do
149 | html = <<-EOL
150 |
151 |
152 |
153 |
154 |
155 |
156 | Heading 1 normal text |
157 | Heading 2 normal text |
158 |
159 |
160 | Heading 3 normal text |
161 | Heading 4 normal text |
162 |
163 |
164 | Heading 5 normal text |
165 | Heading 6 normal text |
166 |
167 |
168 |
169 |
170 | EOL
171 | expected_wordml = <<-EOL
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 | Heading 1
199 |
200 |
201 |
202 |
203 | normal text
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | Heading 2
214 |
215 |
216 |
217 |
218 | normal text
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | Heading 3
231 |
232 |
233 |
234 |
235 | normal text
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 | Heading 4
246 |
247 |
248 |
249 |
250 | normal text
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 | Heading 5
263 |
264 |
265 |
266 |
267 | normal text
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 | Heading 6
278 |
279 |
280 |
281 |
282 | normal text
283 |
284 |
285 |
286 |
287 |
288 |
289 | EOL
290 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
291 | end
292 |
293 | it "transform tables with empty headers" do
294 | html = <<-EOL
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 | |
303 | Header 2 |
304 |
305 |
306 |
307 |
308 | | Cell 1,1 |
309 | Cell 1,2 |
310 |
311 |
312 |
313 |
314 |
315 | EOL
316 | expected_wordml = <<-EOL
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 | Header 2
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 | Cell 1,1
366 |
367 |
368 |
369 |
370 |
371 |
372 | Cell 1,2
373 |
374 |
375 |
376 |
377 |
378 |
379 | EOL
380 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
381 | end
382 |
383 | it "transforms tables without tag on " do
384 | html = <<-EOL
385 |
386 |
387 |
388 |
389 |
390 |
391 | | Header 1 |
392 | |
393 |
394 |
395 |
396 | | Cell 1,1 |
397 | Cell 1,2 |
398 |
399 |
400 |
401 |
402 |
403 | EOL
404 | expected_wordml = <<-EOL
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 | Header 1
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 | Cell 1,1
454 |
455 |
456 |
457 |
458 |
459 |
460 | Cell 1,2
461 |
462 |
463 |
464 |
465 |
466 |
467 | EOL
468 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
469 | end
470 |
471 | it "transforms tables with border attribute and table-bordered class" do
472 | html = <<-EOL
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 | | Hello |
481 | World |
482 |
483 |
484 |
485 | Using table-bordered class
486 |
487 |
488 |
489 | | Hello world |
490 | Part 2 |
491 |
492 |
493 |
494 |
495 |
496 | EOL
497 |
498 | expected_wordml = <<-EOL
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 | Hello
523 |
524 |
525 |
526 |
527 |
528 |
529 | World
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 | Using table-bordered class
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 | Hello world
563 |
564 |
565 |
566 |
567 |
568 |
569 | Part 2
570 |
571 |
572 |
573 |
574 |
575 |
576 | EOL
577 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
578 | end
579 |
580 | it "transforms nested elements inside table cells" do
581 | html = <<-EOL
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 | Pre H1 This is a H1 Post H1 |
590 | Text A paragraph with Strong text More text |
591 |
592 |
593 | Some content inside a div |
594 | |
595 |
596 |
597 | | Something Inside a p strong and strong em |
598 | Text inside div |
599 |
600 |
601 |
602 |
603 |
604 | EOL
605 | expected_wordml = <<-EOL
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 | Pre H1
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 | This is a H1
638 |
639 |
640 |
641 |
642 | Post H1
643 |
644 |
645 |
646 |
647 |
648 |
649 | Text
650 |
651 |
652 |
653 |
654 | A paragraph with
655 |
656 |
657 |
658 |
659 |
660 | Strong
661 |
662 |
663 | text
664 |
665 |
666 |
667 |
668 | More text
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 | Some content
678 |
679 |
680 |
681 |
682 |
683 | inside
684 |
685 |
686 | a
687 |
688 |
689 |
690 |
691 |
692 | div
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 | Something
705 |
706 |
707 |
708 |
709 | Inside a p
710 |
711 |
712 |
713 |
714 |
715 | strong
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 | and strong em
725 |
726 |
727 |
728 |
729 |
730 |
731 | Text inside div
732 |
733 |
734 |
735 |
736 |
737 |
738 | EOL
739 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
740 | end
741 | end
742 |
--------------------------------------------------------------------------------
/spec/xslt_simple_text_style_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "XSLT to make text bold or italic" do
4 |
5 | it "transforms simple b, strong, em and italic tags" do
6 | html = <<-EOL
7 |
8 |
9 |
10 |
11 | Testing bold, italic, strong, em text within a div.
12 | Testing bold, italic, strong, em text within a p.
13 | Testing bold, italic, strong, em text within a span.
14 |
15 |
16 | EOL
17 | expected_wordml = <<-EOL
18 |
19 |
20 |
21 | Testing
22 |
23 |
24 |
25 |
26 |
27 | bold
28 |
29 |
30 | ,
31 |
32 |
33 |
34 |
35 |
36 | italic
37 |
38 |
39 | ,
40 |
41 |
42 |
43 |
44 |
45 | strong
46 |
47 |
48 | ,
49 |
50 |
51 |
52 |
53 |
54 | em
55 |
56 |
57 | text within a div.
58 |
59 |
60 |
61 |
62 | Testing
63 |
64 |
65 |
66 |
67 |
68 | bold
69 |
70 |
71 | ,
72 |
73 |
74 |
75 |
76 |
77 | italic
78 |
79 |
80 | ,
81 |
82 |
83 |
84 |
85 |
86 | strong
87 |
88 |
89 | ,
90 |
91 |
92 |
93 |
94 |
95 | em
96 |
97 |
98 | text within a p.
99 |
100 |
101 |
102 |
103 | Testing
104 |
105 |
106 |
107 |
108 |
109 | bold
110 |
111 |
112 | ,
113 |
114 |
115 |
116 |
117 |
118 | italic
119 |
120 |
121 | ,
122 |
123 |
124 |
125 |
126 |
127 | strong
128 |
129 |
130 | ,
131 |
132 |
133 |
134 |
135 |
136 | em
137 |
138 |
139 | text within a span.
140 |
141 |
142 |
143 | EOL
144 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
145 | end
146 |
147 | it "transforms all combinations of b, strong, em and italic within div and p" do
148 | html = <<-EOL
149 |
150 |
151 |
152 |
153 |
154 | Combinations in p tag: Bold italic, Italic bold, Bold em,
155 | Em bold, Strong italic, Italic strong,
156 | Strong em, Em strong. Should be ok
157 |
158 |
159 | More on combinations : Just bold. Bold italic. Again just bold,
160 | Just italic. Italic bold. Again just italic,
161 | Just bold. Bold em Again just bold,
162 | Just em. Em bold. Again just em,
163 | Just Strong. Strong italic. Again just strong,
164 | Just italic. Italic strong. Again just italic ,
165 | Just Strong. Strong em. Again just strong,
166 | Just em. Em strong. Again just em. Should be ok
167 |
168 |
169 |
170 | EOL
171 | expected_wordml = <<-EOL
172 |
173 |
174 |
175 |
176 | Combinations in p tag:
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | Bold italic
186 |
187 |
188 | ,
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 | Italic bold
198 |
199 |
200 | ,
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | Bold em
210 |
211 |
212 | ,
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 | Em bold
223 |
224 |
225 | ,
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | Strong italic
235 |
236 |
237 | ,
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 | Italic strong
247 |
248 |
249 | ,
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 | Strong em
260 |
261 |
262 | ,
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 | Em strong
272 |
273 |
274 | . Should be ok
275 |
276 |
277 |
278 |
279 |
280 |
281 | More on combinations :
282 |
283 |
284 |
285 |
286 |
287 | Just bold.
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 | Bold italic.
297 |
298 |
299 |
300 |
301 |
302 | Again just bold
303 |
304 |
305 | ,
306 |
307 |
308 |
309 |
310 |
311 |
312 | Just italic.
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 | Italic bold.
322 |
323 |
324 |
325 |
326 |
327 | Again just italic
328 |
329 |
330 | ,
331 |
332 |
333 |
334 |
335 |
336 |
337 | Just bold.
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 | Bold em
347 |
348 |
349 |
350 |
351 |
352 | Again just bold
353 |
354 |
355 | ,
356 |
357 |
358 |
359 |
360 |
361 |
362 | Just em.
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 | Em bold.
372 |
373 |
374 |
375 |
376 |
377 | Again just em
378 |
379 |
380 | ,
381 |
382 |
383 |
384 |
385 |
386 |
387 | Just Strong.
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 | Strong italic.
397 |
398 |
399 |
400 |
401 |
402 | Again just strong
403 |
404 |
405 | ,
406 |
407 |
408 |
409 |
410 |
411 |
412 | Just italic.
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 | Italic strong.
422 |
423 |
424 |
425 |
426 |
427 | Again just italic
428 |
429 |
430 | ,
431 |
432 |
433 |
434 |
435 |
436 |
437 | Just Strong.
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 | Strong em.
447 |
448 |
449 |
450 |
451 |
452 | Again just strong
453 |
454 |
455 | ,
456 |
457 |
458 |
459 |
460 |
461 |
462 | Just em.
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 | Em strong.
472 |
473 |
474 |
475 |
476 |
477 | Again just em
478 |
479 |
480 | . Should be ok
481 |
482 |
483 |
484 |
485 | EOL
486 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
487 | end
488 |
489 | it "transforms b, strong, em and italic within table cells" do
490 | html = <<-EOL
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 | | Column 1 |
499 | Column 2 |
500 | Column 3 |
501 |
502 |
503 |
504 |
505 | | Row 1 |
506 | Em text |
507 | I text |
508 |
509 |
510 | | Row 2 |
511 | Text: Strong Strong em Strong Bold tag Bold italic More bold. End |
512 | Text: Just em Em strong text More em text Italic tag Italic bold More italic. End |
513 |
514 |
515 | Row 2 |
516 | Text: Strong Strong em Strong Bold tag Bold italic More bold. End |
517 | Td Div Strong italic Bold Em |
518 |
519 |
520 | | Td Span A div Strong em |
521 | Td A div Span em |
522 | Td A div Italic strong |
523 |
524 |
525 |
526 |
527 |
528 | EOL
529 | expected_wordml = <<-EOL
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 | Column 1
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 | Column 2
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 | Column 3
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 | Row 1
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 | Em text
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 | I text
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 | Row 2
621 |
622 |
623 |
624 |
625 |
626 |
627 | Text:
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 | Strong
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 | Strong em
645 |
646 |
647 |
648 |
649 |
650 | Strong
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 | Bold tag
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 | Bold italic
668 |
669 |
670 |
671 |
672 |
673 | More bold.
674 |
675 |
676 |
677 |
678 | End
679 |
680 |
681 |
682 |
683 |
684 |
685 | Text:
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 | Just em
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 | Em strong text
703 |
704 |
705 |
706 |
707 |
708 | More em text
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 | Italic tag
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 | Italic bold
726 |
727 |
728 |
729 |
730 |
731 | More italic.
732 |
733 |
734 |
735 |
736 | End
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 | Row 2
749 |
750 |
751 |
752 |
753 |
754 |
755 | Text:
756 |
757 |
758 |
759 |
760 |
761 | Strong
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 | Strong em
771 |
772 |
773 |
774 |
775 |
776 | Strong
777 |
778 |
779 |
780 |
781 |
782 | Bold tag
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 | Bold italic
792 |
793 |
794 |
795 |
796 |
797 | More bold.
798 |
799 |
800 | End
801 |
802 |
803 |
804 |
805 |
806 |
807 | Td
808 |
809 |
810 |
811 |
812 | Div
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 | Strong italic
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 | Bold Em
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 | Td
842 |
843 |
844 |
845 |
846 | Span
847 |
848 |
849 |
850 |
851 | A div
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 | Strong em
863 |
864 |
865 |
866 |
867 |
868 |
869 | Td
870 |
871 |
872 |
873 |
874 | A div
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 | Span em
883 |
884 |
885 |
886 |
887 |
888 |
889 | Td
890 |
891 |
892 |
893 |
894 | A div
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 | Italic strong
906 |
907 |
908 |
909 |
910 |
911 |
912 | EOL
913 | compare_resulting_wordml_with_expected(html, expected_wordml.strip)
914 | end
915 |
916 | end
917 |
--------------------------------------------------------------------------------