lib/write_xlsx/drawing.rb in write_xlsx-0.0.4 vs lib/write_xlsx/drawing.rb in write_xlsx-0.51.0

- old
+ new

@@ -1,12 +1,15 @@ # -*- coding: utf-8 -*- -require 'write_xlsx/package/xml_writer_simple.rb' +require 'write_xlsx/package/xml_writer_simple' +require 'write_xlsx/utility' module Writexlsx class Drawing - attr_writer :embedded + include Writexlsx::Utility + attr_writer :embedded, :orientation + def initialize @writer = Package::XMLWriterSimple.new @drawings = [] @embedded = false @orientation = false @@ -28,17 +31,17 @@ # Write the xdr:wsDr element. write_drawing_workspace if @embedded - index = 0 - @drawings.each do |dimensions| - # Write the xdr:twoCellAnchor element. - index += 1 + index = 0 + @drawings.each do |dimensions| + # Write the xdr:twoCellAnchor element. + index += 1 - write_two_cell_anchor(index, *(dimensions.flatten)) - end + write_two_cell_anchor(index, *(dimensions.flatten)) + end else index = 0 # Write the xdr:absoluteAnchor element. index += 1 @@ -49,29 +52,26 @@ @writer.crlf @writer.close end # - # Add a chart or image sub object to the drawing. + # Add a chart, image or shape sub object to the drawing. # def add_drawing_object(*args) - @drawings << args + @drawings << args.flatten end private # # Write the <xdr:wsDr> element. # def write_drawing_workspace schema = 'http://schemas.openxmlformats.org/drawingml/' - xmlns_xdr = "#{schema}2006/spreadsheetDrawing" - xmlns_a = "#{schema}2006/main" - attributes = [ - 'xmlns:xdr', xmlns_xdr, - 'xmlns:a', xmlns_a + 'xmlns:xdr', "#{schema}2006/spreadsheetDrawing", + 'xmlns:a', "#{schema}2006/main" ] @writer.start_tag('xdr:wsDr', attributes) end @@ -79,37 +79,76 @@ # Write the <xdr:twoCellAnchor> element. # def write_two_cell_anchor(*args) index, type, col_from, row_from, col_from_offset, row_from_offset, col_to, row_to, col_to_offset, row_to_offset, col_absolute, row_absolute, - width, height, description = args + width, height, description, shape = args attributes = [] # Add attribute for images. attributes << :editAs << 'oneCell' if type == 2 + # Add attribute for shapes. + attributes << :editAs << shape[:edit_as] if shape && !shape[:edit_as].nil? + @writer.tag_elements('xdr:twoCellAnchor', attributes) do # Write the xdr:from element. write_from(col_from, row_from, col_from_offset, row_from_offset) # Write the xdr:from element. write_to(col_to, row_to, col_to_offset, row_to_offset) if type == 1 + # Graphic frame. + # Write the xdr:graphicFrame element for charts. - write_graphic_frame(index) - else + write_graphic_frame(index, description) + elsif type == 2 # Write the xdr:pic element. write_pic(index, col_absolute, row_absolute, width, height, description) + else + # Write the xdr:sp element for shapes. + write_sp(index, col_absolute, row_absolute, width, height, shape) end # Write the xdr:clientData element. write_client_data end end # + # Write the <xdr:absoluteAnchor> element. + # + def write_absolute_anchor(index) + @writer.start_tag('xdr:absoluteAnchor') + + # Different co-ordinates for horizonatal (= 0) and vertical (= 1). + if !ptrue?(@orientation) + + # Write the xdr:pos element. + write_pos(0, 0) + + # Write the xdr:ext element. + write_ext(9308969, 6078325) + else + # Write the xdr:pos element. + write_pos(0, -47625) + + # Write the xdr:ext element. + write_ext(6162675, 6124575) + end + + # Write the xdr:graphicFrame element. + write_graphic_frame(index) + + # Write the xdr:clientData element. + write_client_data + + @writer.end_tag('xdr:absoluteAnchor') + end + + # # Write the <xdr:from> element. # def write_from(col, row, col_offset, row_offset) @writer.tag_elements('xdr:from') do # Write the xdr:col element. @@ -168,34 +207,60 @@ def write_row_off(data) @writer.data_element('xdr:rowOff', data) end # + # Write the <xdr:pos> element. + # + def write_pos(x, y) + attributes = [ + 'x', x, + 'y', y + ] + + @writer.empty_tag('xdr:pos', attributes) + end + + # + # Write the <xdr:ext> element. + # + def write_ext(cx, cy) + attributes = [ + 'cx', cx, + 'cy', cy + ] + + @writer.empty_tag('xdr:ext', attributes) + end + + # # Write the <xdr:graphicFrame> element. # - def write_graphic_frame(index) + def write_graphic_frame(index, name = nil) macro = '' attributes = ['macro', macro] @writer.tag_elements('xdr:graphicFrame', attributes) do # Write the xdr:nvGraphicFramePr element. - write_nv_graphic_frame_pr(index) + write_nv_graphic_frame_pr(index, name) # Write the xdr:xfrm element. write_xfrm # Write the a:graphic element. write_atag_graphic(index) end end # # Write the <xdr:nvGraphicFramePr> element. # - def write_nv_graphic_frame_pr(index) + def write_nv_graphic_frame_pr(index, name = nil) + name = "Chart #{index}" if name.nil? || name.empty? + @writer.tag_elements('xdr:nvGraphicFramePr') do # Write the xdr:cNvPr element. - write_c_nv_pr( index + 1, "Chart #{index}" ) + write_c_nv_pr( index + 1, name) # Write the xdr:cNvGraphicFramePr element. write_c_nv_graphic_frame_pr end end @@ -330,20 +395,102 @@ def write_client_data @writer.empty_tag('xdr:clientData') end # + # Write the <xdr:sp> element. + # + def write_sp(index, col_absolute, row_absolute, width, height, shape) + if shape[:connect] != 0 + attributes = [:macro, ''] + @writer.tag_elements('xdr:cxnSp', attributes) do + + # Write the xdr:nvCxnSpPr element. + write_nv_cxn_sp_pr(index, shape) + + # Write the xdr:spPr element. + write_xdr_sp_pr(index, col_absolute, row_absolute, width, height, shape) + end + else + # Add attribute for shapes. + attributes = [:macro, '', :textlink, ''] + @writer.tag_elements('xdr:sp', attributes) do + + # Write the xdr:nvSpPr element. + write_nv_sp_pr(index, shape) + + # Write the xdr:spPr element. + write_xdr_sp_pr(index, col_absolute, row_absolute, width, height, shape) + + # Write the xdr:txBody element. + if shape[:text] != 0 + write_txBody(col_absolute, row_absolute, width, height, shape) + end + end + end + end + + # + # Write the <xdr:nvCxnSpPr> element. + # + def write_nv_cxn_sp_pr(index, shape) + @writer.tag_elements('xdr:nvCxnSpPr') do + + shape[:name] = [shape[:type], index].join(' ') unless shape[:name] + write_c_nv_pr(shape[:id], shape[:name]) + + @writer.tag_elements('xdr:cNvCxnSpPr') do + + attributes = [:noChangeShapeType, '1'] + @writer.empty_tag('a:cxnSpLocks', attributes) + + if shape[:start] + attributes = ['id', shape[:start], 'idx', shape[:start_index]] + @writer.empty_tag('a:stCxn', attributes) + end + + if shape[:end] + attributes = ['id', shape[:end], 'idx', shape[:end_index]] + @writer.empty_tag('a:endCxn', attributes) + end + end + end + end + + # + # Write the <xdr:NvSpPr> element. + # + def write_nv_sp_pr(index, shape) + attributes = [] + + @writer.tag_elements('xdr:nvSpPr') do + shape_name = "#{shape[:type]} #{index}" + write_c_nv_pr(shape[:id], shape_name) + attributes = ['txBox', 1] if shape[:tx_box] != 0 + @writer.tag_elements('xdr:cNvSpPr', attributes) do + attributes = [:noChangeArrowheads, '1'] + @writer.empty_tag('a:spLocks', attributes) + end + end + end + + # # Write the <xdr:pic> element. # def write_pic(index, col_absolute, row_absolute, width, height, description) @writer.tag_elements('xdr:pic') do # Write the xdr:nvPicPr element. write_nv_pic_pr(index, description) # Write the xdr:blipFill element. write_blip_fill(index) + + # Pictures are rectangle shapes by default. + shape = Shape.new + shape[:type] = 'rect' + # Write the xdr:spPr element. - write_sp_pr(col_absolute, row_absolute, width, height) + write_sp_pr(col_absolute, row_absolute, width, height, shape) end end # # Write the <xdr:nvPicPr> element. @@ -422,26 +569,61 @@ def write_a_fill_rect @writer.empty_tag('a:fillRect') end # - # Write the <xdr:spPr> element. + # Write the <xdr:spPr> element, for charts. # - def write_sp_pr(col_absolute, row_absolute, width, height) + def write_sp_pr(col_absolute, row_absolute, width, height, shape = {}) @writer.tag_elements('xdr:spPr') do # Write the a:xfrm element. write_a_xfrm(col_absolute, row_absolute, width, height) # Write the a:prstGeom element. - write_a_prst_geom + write_a_prst_geom(shape) end end # + # Write the <xdr:spPr> element for shapes. + # + def write_xdr_sp_pr(index, col_absolute, row_absolute, width, height, shape) + attributes = ['bwMode', 'auto'] + + @writer.tag_elements('xdr:spPr', attributes) do + + # Write the a:xfrm element. + write_a_xfrm(col_absolute, row_absolute, width, height, shape) + + # Write the a:prstGeom element. + write_a_prst_geom(shape) + + if shape[:fill].to_s.bytesize > 1 + # Write the a:solidFill element. + write_a_solid_fill(shape[:fill]) + else + @writer.empty_tag('a:noFill') + end + + # Write the a:ln element. + write_a_ln(shape) + end + end + + # # Write the <a:xfrm> element. # - def write_a_xfrm(col_absolute, row_absolute, width, height) - @writer.tag_elements('a:xfrm') do + def write_a_xfrm(col_absolute, row_absolute, width, height, shape = {}) + attributes = [] + + rotation = shape[:rotation] || 0 + rotation *= 60000 + + attributes << 'rot' << rotation if rotation != 0 + attributes << 'flipH' << 1 if ptrue?(shape[:flip_h]) + attributes << 'flipV' << 1 if ptrue?(shape[:flip_v]) + + @writer.tag_elements('a:xfrm', attributes) do # Write the a:off element. write_a_off( col_absolute, row_absolute ) # Write the a:ext element. write_a_ext( width, height ) end @@ -473,24 +655,161 @@ end # # Write the <a:prstGeom> element. # - def write_a_prst_geom - prst = 'rect' + def write_a_prst_geom(shape = {}) + attributes = [] + attributes << 'prst' << shape[:type] if shape[:type] - attributes = ['prst', prst] - @writer.tag_elements('a:prstGeom', attributes) do # Write the a:avLst element. - write_a_av_lst + write_a_av_lst(shape) end end # # Write the <a:avLst> element. # - def write_a_av_lst - @writer.empty_tag('a:avLst') + def write_a_av_lst(shape = {}) + if shape[:adjustments].respond_to?(:empty?) + adjustments = shape[:adjustments] + elsif shape[:adjustments].respond_to?(:coerce) + adjustments = [shape[:adjustments]] + elsif !shape[:adjustments] + adjustments = [] + end + + if adjustments.respond_to?(:empty?) && !adjustments.empty? + @writer.tag_elements('a:avLst') do + i = 0 + adjustments.each do |adj| + i += 1 + # Only connectors have multiple adjustments. + suffix = shape[:connect] != 0 ? i : '' + + # Scale Adjustments: 100,000 = 100%. + adj_int = (adj * 1000).to_i + + attributes = [:name, "adj#{suffix}", :fmla, "val #{adj_int}"] + @writer.empty_tag('a:gd', attributes) + end + end + else + @writer.empty_tag('a:avLst') + end + end + + # + # Write the <a:solidFill> element. + # + def write_a_solid_fill(rgb = '000000') + attributes = ['val', rgb] + + @writer.tag_elements('a:solidFill') do + @writer.empty_tag('a:srgbClr', attributes) + end + end + + # + # Write the <a:ln> elements. + # + def write_a_ln(shape = {}) + weight = shape[:line_weight] || 0 + attributes = ['w', weight * 9525] + @writer.tag_elements('a:ln', attributes) do + line = shape[:line] || 0 + if line.to_s.bytesize > 1 + # Write the a:solidFill element. + write_a_solid_fill(line) + else + @writer.empty_tag('a:noFill') + end + + if shape[:line_type] != '' + attributes = ['val', shape[:line_type]] + @writer.empty_tag('a:prstDash', attributes) + end + + if shape[:connect] != 0 + @writer.empty_tag('a:round') + else + attributes = ['lim', 800000] + @writer.empty_tag('a:miter', attributes) + end + + @writer.empty_tag('a:headEnd') + @writer.empty_tag('a:tailEnd') + end + end + + # + # Write the <xdr:txBody> element. + # + def write_txBody(col_absolute, row_absolute, width, height, shape) + attributes = [ + :vertOverflow, "clip", + :wrap, "square", + :lIns, "27432", + :tIns, "22860", + :rIns, "27432", + :bIns, "22860", + :anchor, shape[:valign], + :upright, "1" + ] + @writer.tag_elements('xdr:txBody') do + @writer.empty_tag('a:bodyPr', attributes) + @writer.empty_tag('a:lstStyle') + + @writer.tag_elements('a:p') do + rotation = shape[:format][:rotation] || 0 + rotation *= 60000 + + attributes = [:algn, shape[:align], :rtl, rotation] + @writer.tag_elements('a:pPr', attributes) do + attributes = [:sz, "1000"] + @writer.empty_tag('a:defRPr', attributes) + end + + @writer.tag_elements('a:r') do + size = shape[:format][:size] || 8 + size *= 100 + + bold = shape[:format][:bold] || 0 + italic = shape[:format][:italic] || 0 + underline = ptrue?(shape[:format][:underline]) ? 'sng' : 'none' + strike = ptrue?(shape[:format][:font_strikeout]) ? 'Strike' : 'noStrike' + + attributes = [ + :lang, "en-US", + :sz, size, + :b, bold, + :i, italic, + :u, underline, + :strike, strike, + :baseline, 0 + ] + @writer.tag_elements('a:rPr', attributes) do + color = shape[:format][:color] + if color + color = shape.get_palette_color(color) + color = color.sub(/^FF/, '') # Remove leading FF from rgb for shape color. + else + color = '000000' + end + + write_a_solid_fill(color) + + font = shape[:format][:font] || 'Calibri' + attributes = [:typeface, font] + @writer.empty_tag('a:latin', attributes) + @writer.empty_tag('a:cs', attributes) + end + @writer.tag_elements('a:t') do + @writer.characters(shape[:text]) + end + end + end + end end end end