From 0cda8decf9cb3785a21fba4f817c1b039a5bb0bf Mon Sep 17 00:00:00 2001 From: MuhammetDilmac Date: Mon, 10 Jul 2017 16:16:10 +0300 Subject: [PATCH 1/4] ImageSupport * Add image support * Add picture sample docx * Add jpeg content type --- Html2Docx.gemspec | 1 + lib/Html2Docx.rb | 2 + lib/Html2Docx/document.rb | 7 +- lib/Html2Docx/document_objects/heading.rb | 6 +- lib/Html2Docx/document_objects/image.rb | 220 ++++++++++++++++++++ lib/Html2Docx/document_objects/paragraph.rb | 12 +- lib/Html2Docx/relation.rb | 83 +++++++- samples/Picture.docx | Bin 0 -> 42877 bytes skell/[Content_Types].xml | 2 +- skell/word/document2.xml | 2 +- 10 files changed, 319 insertions(+), 16 deletions(-) create mode 100644 lib/Html2Docx/document_objects/image.rb create mode 100644 samples/Picture.docx diff --git a/Html2Docx.gemspec b/Html2Docx.gemspec index 27c57e8..9b87bbe 100644 --- a/Html2Docx.gemspec +++ b/Html2Docx.gemspec @@ -25,4 +25,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'rspec', '~> 3.0' spec.add_runtime_dependency 'nokogiri', '~> 1.6', '>= 1.6.8' spec.add_runtime_dependency 'rubyzip', '~> 1.2', '>= 1.2.0' + spec.add_runtime_dependency 'typhoeus', '~> 1.0', '>= 1.0.1' end diff --git a/lib/Html2Docx.rb b/lib/Html2Docx.rb index f683130..f128227 100644 --- a/lib/Html2Docx.rb +++ b/lib/Html2Docx.rb @@ -1,6 +1,7 @@ require 'fileutils' require 'nokogiri' require 'zip' +require 'typhoeus' require 'Html2Docx/helpers/document_helper' require 'Html2Docx/helpers/nokogiri_helper' @@ -14,6 +15,7 @@ require 'Html2Docx/document_objects/paragraph' require 'Html2Docx/document_objects/heading' +require 'Html2Docx/document_objects/image' module Html2Docx ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), '../')) diff --git a/lib/Html2Docx/document.rb b/lib/Html2Docx/document.rb index aae42f1..705ec7c 100644 --- a/lib/Html2Docx/document.rb +++ b/lib/Html2Docx/document.rb @@ -1,7 +1,8 @@ module Html2Docx class Document def initialize(options = {}) - @document_file = File.join(options.fetch(:temp), 'word', 'document2.xml') + @tmp_path = options[:temp] + @document_file = File.join(@tmp_path, 'word', 'document2.xml') @document = File.open(@document_file) { |f| Nokogiri::XML(f) } @body = @document.at_xpath('//w:body') @contents = [] @@ -26,11 +27,11 @@ def add_html(html) case element.name when 'p' # Add paragraph - paragraph = DocumentObjects::Paragraph.new(@document, @relation) + paragraph = DocumentObjects::Paragraph.new(@document, @relation, @tmp_path) paragraph.add_paragraph(element) @contents.push paragraph.render when /h[1-9]/ - heading = DocumentObjects::Heading.new(@document, @relation) + heading = DocumentObjects::Heading.new(@document, @relation, @tmp_path) heading.add_heading(element) @contents.push heading.render when 'table' diff --git a/lib/Html2Docx/document_objects/heading.rb b/lib/Html2Docx/document_objects/heading.rb index 7a4d1f9..4374bd3 100644 --- a/lib/Html2Docx/document_objects/heading.rb +++ b/lib/Html2Docx/document_objects/heading.rb @@ -1,9 +1,11 @@ module Html2Docx module DocumentObjects class Heading - def initialize(document, relation) + def initialize(document, relation, tmp_path) @document = document @relation = relation + @tmp_path = tmp_path + @heading = nil end @@ -11,7 +13,7 @@ def add_heading(heading_object) heading_object['class'] = "Heading#{heading_object.name.scan(/[0-9]/).first}" heading_object.name = 'p' - paragraph = Paragraph.new(@document, @relation) + paragraph = Paragraph.new(@document, @relation, @tmp_path) paragraph.add_paragraph(heading_object) @heading = paragraph.render diff --git a/lib/Html2Docx/document_objects/image.rb b/lib/Html2Docx/document_objects/image.rb new file mode 100644 index 0000000..7b287f5 --- /dev/null +++ b/lib/Html2Docx/document_objects/image.rb @@ -0,0 +1,220 @@ +module Html2Docx + module DocumentObjects + class Image + def initialize(document, relation, tmp_path) + @document = document + @relation = relation + @tmp_path = tmp_path + + @media_path = nil + @image = nil + + check_and_create_media_directory + end + + def add_image(image_object) + image = get_image_information(image_object) + drawing_tag = create_drawing_tag + anchor_tag = create_anchor_tag + doc_pr_tag = create_doc_pr_tag(image) + graphic_tag = create_graphic_tag(image) + + anchor_tag.add_child(doc_pr_tag) + anchor_tag.add_child(graphic_tag) + drawing_tag.add_child(anchor_tag) + + drawing_tag + end + + private + + def get_image_information(image_object) + id = @relation.get_latest_image_id + 1 + path = image_object.attr('src') + name = path.split('/').last + title = image_object.attr('alt') || "Picture-#{id}" + height = image_object.attr('height').to_i + width = image_object.attr('width').to_i + + { name: name, title: title, path: path, height: height, width: width } + end + + def create_drawing_tag + Nokogiri::XML::Node.new('w:drawing', @document) + end + + def create_anchor_tag + anchor_tag = Nokogiri::XML::Node.new('wp:anchor', @document) + anchor_tag['allowOverlap'] = 0 + anchor_tag['behindDoc'] = 0 + anchor_tag['simplePos'] = 0 + + anchor_tag + end + + def create_doc_pr_tag(image) + doc_pr_tag = Nokogiri::XML::Node.new('w:docPr', @document) + doc_pr_tag['id'] = @relation.get_uniq_image_id + doc_pr_tag['name'] = image[:name] + doc_pr_tag['title'] = image[:title] + + doc_pr_tag + end + + def check_and_create_media_directory + @media_path = File.join(@tmp_path, 'media') + + Dir.mkdir @media_path unless Dir.exist? @media_path + end + + def create_graphic_tag(image) + graphic_tag = Nokogiri::XML::Node.new('a:graphic', @document) + graphic_tag['xmlns:a'] = 'http://schemas.openxmlformats.org/drawingml/2006/main' + + graphic_data_tag = create_graphic_data_tag(image) + graphic_tag.add_child(graphic_data_tag) + + graphic_tag + end + + def create_graphic_data_tag(image) + graphic_data_tag = Nokogiri::XML::Node.new('a:graphicData', @document) + graphic_data_tag['uri'] = 'http://schemas.openxmlformats.org/drawingml/2006/picture' + + pic_tag = create_pic_tag(image) + graphic_data_tag.add_child(pic_tag) + + graphic_data_tag + end + + def create_pic_tag(image) + pic_tag = Nokogiri::XML::Node.new('pic:pic', @document) + pic_tag['xmlns:pic'] = 'http://schemas.openxmlformats.org/drawingml/2006/picture' + + nv_pic_pr_tag = create_nv_pic_pr_tag(image) + pic_tag.add_child(nv_pic_pr_tag) + + blip_fill_tag = create_blip_fill_tag(image) + pic_tag.add_child(blip_fill_tag) + + sp_pr_tag = create_sp_pr_tag(image) + pic_tag.add_child(sp_pr_tag) + + pic_tag + end + + def create_nv_pic_pr_tag(image) + nv_pic_pr_tag = Nokogiri::XML::Node.new('pic:nvPicPr', @document) + + c_nv_pr_tag = create_c_nv_pr_tag(image) + nv_pic_pr_tag.add_child(c_nv_pr_tag) + + c_nv_pic_pr = create_c_nv_pic_pr(image) + nv_pic_pr_tag.add_child(c_nv_pic_pr) + + nv_pic_pr_tag + end + + def create_c_nv_pr_tag(image) + c_nv_pr_tag = Nokogiri::XML::Node.new('pic:cNvPr', @document) + c_nv_pr_tag['id'] = @relation.get_uniq_image_id + c_nv_pr_tag['name'] = image[:name] + c_nv_pr_tag['title'] = image[:title] + + c_nv_pr_tag + end + + def create_c_nv_pic_pr(image) + c_nv_pic_pr_tag = Nokogiri::XML::Node.new('pic:cNvPicPr', @document) + + c_nv_pic_pr_tag + end + + def create_blip_fill_tag(image) + blip_fill_tag = Nokogiri::XML::Node.new('pic:blipFill', @document) + + blip_tag = create_blip_tag(image) + blip_fill_tag.add_child(blip_tag) + + stretch_tag = create_stretch_tag + blip_fill_tag.add_child(stretch_tag) + + blip_fill_tag + end + + def create_blip_tag(image) + blip_tag = Nokogiri::XML::Node.new('a:blip', @document) + blip_tag['r:embed'] = @relation.add_image(image, @media_path) + + blip_tag + end + + def create_stretch_tag + stretch_tag = Nokogiri::XML::Node.new('a:stretch', @document) + + fill_rect_tag = create_fill_rect_tag + stretch_tag.add_child(fill_rect_tag) + + stretch_tag + end + + def create_fill_rect_tag + Nokogiri::XML::Node.new('a:fillRect', @document) + end + + def create_sp_pr_tag(image) + sp_pr_tag = Nokogiri::XML::Node.new('pic:spPr', @document) + + xfrm_tag = create_xfrm_tag(image) + sp_pr_tag.add_child(xfrm_tag) + + prst_geom_tag = create_prst_geom_tag(image) + sp_pr_tag.add_child(prst_geom_tag) + + sp_pr_tag + end + + def create_xfrm_tag(image) + xfrm_tag = Nokogiri::XML::Node.new('a:xfrm', @document) + + off_tag = create_off_tag(image) + xfrm_tag.add_child(off_tag) + + ext_tag = create_ext_tag(image) + xfrm_tag.add_child(ext_tag) + + xfrm_tag + end + + def create_off_tag(image) + off_tag = Nokogiri::XML::Node.new('a:off', @document) + off_tag['x'] = '0' + off_tag['y'] = '0' + + off_tag + end + + def create_ext_tag(image) + ext_tag = Nokogiri::XML::Node.new('a:ext', @document) + ext_tag['cx'] = image[:width] * 9525 + ext_tag['cy'] = image[:height] * 9525 + + ext_tag + end + + def create_prst_geom_tag(image) + prst_geom_tag = Nokogiri::XML::Node.new('a:prstGeom', @document) + prst_geom_tag['prst'] = 'rect' + + av_lst_tag = create_av_lst_tag(image) + prst_geom_tag.add_child(av_lst_tag) + + prst_geom_tag + end + + def create_av_lst_tag(image) + Nokogiri::XML::Node.new('a:avLst', @document) + end + end + end +end \ No newline at end of file diff --git a/lib/Html2Docx/document_objects/paragraph.rb b/lib/Html2Docx/document_objects/paragraph.rb index fbc8756..103d820 100644 --- a/lib/Html2Docx/document_objects/paragraph.rb +++ b/lib/Html2Docx/document_objects/paragraph.rb @@ -1,9 +1,10 @@ module Html2Docx module DocumentObjects class Paragraph - def initialize(document, relation) + def initialize(document, relation, tmp_path) @document = document @relation = relation + @tmp_path = tmp_path @paragraph = nil end @@ -135,6 +136,10 @@ def add_paragraph_child(children) hyperlink_tag.add_child text_field @paragraph.add_child hyperlink_tag next + when 'img' + text_field.add_child add_image(child) + @paragraph.add_child(text_field) + next end paragraph_id = child.attr('id') @@ -220,6 +225,11 @@ def add_stroke_text(text_style) text_style end + def add_image(image) + image_object_helper = DocumentObjects::Image.new(@document, @relation, @tmp_path) + image_object_helper.add_image(image) + end + def render @paragraph end diff --git a/lib/Html2Docx/relation.rb b/lib/Html2Docx/relation.rb index b2d737d..1dd7ab7 100644 --- a/lib/Html2Docx/relation.rb +++ b/lib/Html2Docx/relation.rb @@ -7,6 +7,8 @@ def initialize(options = {}) @last_relation_id = 1 @internal_links = {} @external_links = {} + @images = {} + @unique_image_id = 0 if options[:main_relation] @relation_file = File.join(options.fetch(:temp), 'word', '_rels', 'document2.xml.rels') @@ -59,6 +61,14 @@ def create_internal_link_id(name) end end + def get_latest_internal_link_id + @internal_links.keys.max || 0 + end + + def find_internal_link_id(name) + @internal_links.find{ |key, value| value == name } + end + def create_external_link_id(destination) id, value = find_external_link_id(destination) @@ -67,7 +77,7 @@ def create_external_link_id(destination) else id = get_latest_external_link_id.delete('rId').to_i + 1 @external_links["rId#{id}"] = destination - "rId#{id}" + "elId#{id}" end end @@ -76,28 +86,85 @@ def find_external_link_id(destination) end def get_latest_external_link_id - @external_links.keys.max || "rId0" + @external_links.keys.max || 'rId0' end - def get_latest_internal_link_id - @internal_links.keys.max || 0 + def get_uniq_image_id + @unique_image_id = @unique_image_id + 1 end - def find_internal_link_id(name) - @internal_links.find{ |key, value| value == name } + def add_image(image, media_path) + real_path = image[:path] + image_name = image[:path].split('/').last + current_path = File.join(media_path, image_name) + + if real_path.start_with? 'http' + request = Typhoeus::Request.new(path) + image_file = File.open(current_path, 'wb+') + + request.on_headers do |response| + if response.code != 200 + raise "Image not found! Image Path: #{real_path}" + end + end + + request.on_body do |data| + image_file.write(data) + end + + request.on_complete do |response| + image_file.close + end + + request.run + else + if File.exist? real_path + FileUtils.cp real_path, current_path + else + raise "Image not found! Image Path: #{real_path}" + end + end + + relation_image_path = File.join('/', 'media', image_name) + + add_image_relation(relation_image_path) + end + + def add_image_relation(relation_image_path) + image_id = "iId#{get_uniq_image_id}" + + @images[image_id] = { + type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', + target: relation_image_path + } + + image_id + end + + def get_latest_image_id + @images.keys.max.to_i end def render @external_links.each do |key, value| external_link_relation = Nokogiri::XML::Node.new('Relationship', @relation) - external_link_relation['Id'] = key - external_link_relation['Type'] = 'http://. . ./hyperlink' + external_link_relation['Type'] = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink' external_link_relation['Target'] = value external_link_relation['TargetMode'] = 'External' + external_link_relation['Id'] = key @relation.root.add_child(external_link_relation) end + @images.each do |key, value| + image_relation = Nokogiri::XML::Node.new('Relationship', @relation) + image_relation['Type'] = value[:type] + image_relation['Target'] = value[:target] + image_relation['Id'] = key + + @relation.root.add_child(image_relation) + end + File.open(@relation_file, 'w') { |f| f.write(Helpers::NokogiriHelper.to_xml(@relation)) } end end diff --git a/samples/Picture.docx b/samples/Picture.docx new file mode 100644 index 0000000000000000000000000000000000000000..651871ce466af4ef2258c3344af285b697257fe6 GIT binary patch literal 42877 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 \ No newline at end of file + \ No newline at end of file diff --git a/skell/word/document2.xml b/skell/word/document2.xml index ad5106b..50bd863 100644 --- a/skell/word/document2.xml +++ b/skell/word/document2.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 916557843d78a26f683fe6f4a48280b6d7413c47 Mon Sep 17 00:00:00 2001 From: MuhammetDilmac Date: Tue, 11 Jul 2017 16:53:42 +0300 Subject: [PATCH 2/4] Add image support with external link. --- lib/Html2Docx/document_objects/image.rb | 47 ++++++++++++++++++------- lib/Html2Docx/relation.rb | 9 ++--- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/lib/Html2Docx/document_objects/image.rb b/lib/Html2Docx/document_objects/image.rb index 7b287f5..655651d 100644 --- a/lib/Html2Docx/document_objects/image.rb +++ b/lib/Html2Docx/document_objects/image.rb @@ -15,13 +15,17 @@ def initialize(document, relation, tmp_path) def add_image(image_object) image = get_image_information(image_object) drawing_tag = create_drawing_tag - anchor_tag = create_anchor_tag + inline_tag = create_inline_tag doc_pr_tag = create_doc_pr_tag(image) graphic_tag = create_graphic_tag(image) + extent_tag = create_extent_tag(image) + c_nv_graphic_frame_pr = create_c_nv_graphic_frame_pr(image) - anchor_tag.add_child(doc_pr_tag) - anchor_tag.add_child(graphic_tag) - drawing_tag.add_child(anchor_tag) + inline_tag.add_child(extent_tag) + inline_tag.add_child(doc_pr_tag) + inline_tag.add_child(c_nv_graphic_frame_pr) + inline_tag.add_child(graphic_tag) + drawing_tag.add_child(inline_tag) drawing_tag end @@ -43,17 +47,14 @@ def create_drawing_tag Nokogiri::XML::Node.new('w:drawing', @document) end - def create_anchor_tag - anchor_tag = Nokogiri::XML::Node.new('wp:anchor', @document) - anchor_tag['allowOverlap'] = 0 - anchor_tag['behindDoc'] = 0 - anchor_tag['simplePos'] = 0 + def create_inline_tag + anchor_tag = Nokogiri::XML::Node.new('wp:inline', @document) anchor_tag end def create_doc_pr_tag(image) - doc_pr_tag = Nokogiri::XML::Node.new('w:docPr', @document) + doc_pr_tag = Nokogiri::XML::Node.new('wp:docPr', @document) doc_pr_tag['id'] = @relation.get_uniq_image_id doc_pr_tag['name'] = image[:name] doc_pr_tag['title'] = image[:title] @@ -69,7 +70,6 @@ def check_and_create_media_directory def create_graphic_tag(image) graphic_tag = Nokogiri::XML::Node.new('a:graphic', @document) - graphic_tag['xmlns:a'] = 'http://schemas.openxmlformats.org/drawingml/2006/main' graphic_data_tag = create_graphic_data_tag(image) graphic_tag.add_child(graphic_data_tag) @@ -89,7 +89,6 @@ def create_graphic_data_tag(image) def create_pic_tag(image) pic_tag = Nokogiri::XML::Node.new('pic:pic', @document) - pic_tag['xmlns:pic'] = 'http://schemas.openxmlformats.org/drawingml/2006/picture' nv_pic_pr_tag = create_nv_pic_pr_tag(image) pic_tag.add_child(nv_pic_pr_tag) @@ -215,6 +214,30 @@ def create_prst_geom_tag(image) def create_av_lst_tag(image) Nokogiri::XML::Node.new('a:avLst', @document) end + + def create_extent_tag(image) + ext_tag = Nokogiri::XML::Node.new('wp:extent', @document) + ext_tag['cx'] = image[:width] * 9525 + ext_tag['cy'] = image[:height] * 9525 + + ext_tag + end + + def create_c_nv_graphic_frame_pr(image) + c_nv_graphic_frame_pr_tag = Nokogiri::XML::Node.new('wp:cNvGraphicFramePr', @document) + + graphic_frame_locks_tag = create_graphic_frame_locks_tag(image) + c_nv_graphic_frame_pr_tag.add_child(graphic_frame_locks_tag) + + c_nv_graphic_frame_pr_tag + end + + def create_graphic_frame_locks_tag(image) + graphic_frame_locks_tag = Nokogiri::XML::Node.new('a:graphicFrameLocks', @document) + graphic_frame_locks_tag['noChangeAspect'] = 1 + + graphic_frame_locks_tag + end end end end \ No newline at end of file diff --git a/lib/Html2Docx/relation.rb b/lib/Html2Docx/relation.rb index 1dd7ab7..69a210b 100644 --- a/lib/Html2Docx/relation.rb +++ b/lib/Html2Docx/relation.rb @@ -75,8 +75,9 @@ def create_external_link_id(destination) if id id else - id = get_latest_external_link_id.delete('rId').to_i + 1 - @external_links["rId#{id}"] = destination + id = get_latest_external_link_id.delete('elId').to_i + 1 + @external_links["elId#{id}"] = destination + "elId#{id}" end end @@ -86,7 +87,7 @@ def find_external_link_id(destination) end def get_latest_external_link_id - @external_links.keys.max || 'rId0' + @external_links.keys.max || 'elId0' end def get_uniq_image_id @@ -99,7 +100,7 @@ def add_image(image, media_path) current_path = File.join(media_path, image_name) if real_path.start_with? 'http' - request = Typhoeus::Request.new(path) + request = Typhoeus::Request.new(real_path) image_file = File.open(current_path, 'wb+') request.on_headers do |response| From 766bdf43fbb5a74ee810d1a16847463ea86f6375 Mon Sep 17 00:00:00 2001 From: MuhammetDilmac Date: Tue, 11 Jul 2017 17:11:24 +0300 Subject: [PATCH 3/4] Version bump --- lib/Html2Docx/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Html2Docx/version.rb b/lib/Html2Docx/version.rb index dbd7992..314a909 100644 --- a/lib/Html2Docx/version.rb +++ b/lib/Html2Docx/version.rb @@ -1,3 +1,3 @@ module Html2Docx - VERSION = '0.4.1' + VERSION = '0.5.0' end \ No newline at end of file From c5665675500e157258287c17bc1e742c24595f3f Mon Sep 17 00:00:00 2001 From: MuhammetDilmac Date: Tue, 11 Jul 2017 17:11:34 +0300 Subject: [PATCH 4/4] Add Image wiki --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 76b1e6e..fbb17a8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Html2Docx has very easy usage. * Supporting heading. [Wiki - Heading Usage](https://github.com/MuhammetDilmac/Html2Docx/wiki/Heading-Usage) * Supporting internal links. [Wiki - Internal Links Usage](https://github.com/MuhammetDilmac/Html2Docx/wiki/Internal-Links-Usage) * Supporting extarnal links. [Wiki - Extarnal Links Usage](https://github.com/MuhammetDilmac/Html2Docx/wiki/External-Links-Usage) +* Supporting image(internal & external). [Wiki - Image Usage](https://github.com/MuhammetDilmac/Html2Docx/wiki/Image-Usage) ## Contributing