├── .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 = '

Hello

Title

Second

' 36 | compare_resulting_wordml_with_expected(html, " Hello ") 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby Html to word Gem [![Code Climate](https://codeclimate.com/github/nickfrandsen/htmltoword.png)](https://codeclimate.com/github/nickfrandsen/htmltoword) [![Build Status](https://travis-ci.org/nickfrandsen/htmltoword.png)](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 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
Left aligned textRight aligned textCenter aligned textJustify aligned text
To the leftTo the rightCenteredJustified
To the leftTo the rightCenteredJustified
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 | #
Something
else
-> 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 | 15 | 16 | 17 |
Hello
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 | 66 | 76 | 77 | 78 | 79 |
Cell 1,1 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
NestedTable
75 |
Cell 1,3
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 | 174 | 176 | 177 |
Cell 1,1 175 |
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 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 |
232 | Header 2
Cell 1,1Cell 1,2
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 | 321 | 323 | 324 | 325 | 326 | 327 | 328 | 329 |
Header 1 322 |
Cell 1,1Cell 1,2
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 | 410 | 411 | 412 | 413 |
HelloWorld
414 | Using table-bordered class 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 |
Hello worldPart 2
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 | 519 | 520 | 521 | 522 | 523 | 525 | 526 | 527 | 528 | 529 | 530 |
Pre H1

This is a H1

Post H1
Text

A paragraph with Strong text

More text
Some content inside a div
524 |
Something

Inside a p strong and strong em

Text inside div
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 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 |

Heading 1

normal text

Heading 2

normal text

Heading 3

normal text

Heading 4

normal text
Heading 5
normal text
Heading 6
normal text
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 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 |
303 | Header 2
Cell 1,1Cell 1,2
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 | 392 | 394 | 395 | 396 | 397 | 398 | 399 | 400 |
Header 1 393 |
Cell 1,1Cell 1,2
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 | 481 | 482 | 483 | 484 |
HelloWorld
485 | Using table-bordered class 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 |
Hello worldPart 2
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 | 590 | 591 | 592 | 593 | 594 | 596 | 597 | 598 | 599 | 600 | 601 |
Pre H1

This is a H1

Post H1
Text

A paragraph with Strong text

More text
Some content inside a div
595 |
Something

Inside a p strong and strong em

Text inside div
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 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 |
Column 1Column 2Column 3
Row 1Em textI text
Row 2Text: Strong Strong em Strong Bold tag Bold italic More bold. End Text: Just em Em strong text More em text Italic tag Italic bold More italic. End
Row 2

Text: Strong Strong em Strong Bold tag Bold italic More bold. End

Td
Div Strong italic
Bold Em
Td Span
A div
Strong em
Td
A div
Span em
Td
A div
Italic strong
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 | --------------------------------------------------------------------------------