def add_ObjectN(N, metainf_manifest_xml_obj): """Add Object manifest:file-entry object to manifest.xml in META-INF of ods file""" NS = metainf_manifest_xml_obj.NS M = metainf_manifest_xml_obj.getroot() # just to reduce line lengths # put subdirectory object into manifest obj_name = 'Object %i' % N attribD = { NS('manifest:full-path'): obj_name + '/', NS('manifest:media-type'): "application/vnd.oasis.opendocument.chart" } mfe = ET.Element(NS('manifest:file-entry'), attrib=attribD) M.insert(N, mfe) # place in order # put xml files into subdirectory attribD = { NS('manifest:full-path'): obj_name + '/content.xml', NS('manifest:media-type'): "text/xml" } mfe = ET.Element(NS('manifest:file-entry'), attrib=attribD) M.insert(N * 3, mfe) # place in order attribD = { NS('manifest:full-path'): obj_name + '/styles.xml', NS('manifest:media-type'): "text/xml" } mfe = ET.Element(NS('manifest:file-entry'), attrib=attribD) M.insert(N * 3, mfe) # place in order
def new_elem(self, name, attribOD=None): """ Create a new Element object. name format can be 'table:table' OR '{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table' (i.e. can be path format OR tab format) attribOD is an OrderedDict in order to preserve attribute order. """ tag = self.NS(name) #print( 'tag =',tag ) if attribOD: OD = self.NS_attrib(attribOD) my_new_elem = ET.Element(tag, attrib=OD) else: my_new_elem = ET.Element(tag) sL = my_new_elem.tag.split('}') if len(sL) == 2: name = sL[1] uri = sL[0][1:] self.qnameOD[my_new_elem.tag] = '%s:%s' % (self.nsOD[uri], name) for qname, v in list(my_new_elem.attrib.items()): sL = qname.split('}') if len(sL) == 2: name = sL[1] uri = sL[0][1:] self.qnameOD[qname] = '%s:%s' % (self.nsOD[uri], name) return my_new_elem
def tostring(self): class dummy: pass xml_dataL = [] if self.xml_header: xml_dataL = [self.xml_header + '\n'] dummy_file = dummy() dummy_file.write = xml_dataL.append # There are differences between the python2 and python3 serialize routines if sys.version_info < (3,): ET._serialize_xml(dummy_file.write, self.root, "utf-8", self.qnameOD, self.nsOD) else: short_empty_elements = True # use short format for empty elements ET._serialize_xml(dummy_file.write, self.root, self.qnameOD, self.nsOD, short_empty_elements) return "".join(xml_dataL)
def tostring(self): class dummy: pass xml_dataL = [] if self.xml_header: xml_dataL = [self.xml_header + '\n'] dummy_file = dummy() dummy_file.write = xml_dataL.append # There are differences between the python2 and python3 serialize routines if sys.version_info < (3, ): ET._serialize_xml(dummy_file.write, self.root, "utf-8", self.qnameOD, self.nsOD) else: short_empty_elements = True # use short format for empty elements ET._serialize_xml(dummy_file.write, self.root, self.qnameOD, self.nsOD, short_empty_elements) return "".join(xml_dataL)
def make_subelem(ndot1=1, ldot1='0.25in', ndot2=0, ldot2='0in', dist='0.03125in', style='rect'): """ style can be "rect" or "round" (hard to tell the difference) """ new_elem_2 = ET.SubElement(parent,"{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke-dash", attrib=OrderedDict([('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}name', '%s'%a_name), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}display-name', '%s'%display_name), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}style', '%s'%style), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}dots1', '%i'%ndot1), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}dots1-length', '%s'%ldot1), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}dots2', '%i'%ndot2), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}dots2-length', '%s'%ldot2), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}distance', '%s'%dist)]))
def __init__(self, plot_sheetname, num_chart, parent_obj, excel_colors=True): """Inits SpreadSheet with filename and blank content. NS: cleans up the namespace callouts of the xml Element Attributes:: plot_sheetname: name of data sheet """ self.i_color = 0 # index into color chart for next curve self.i_prim_color = 0 # index into colorL self.i_sec_color = 0 # index into color2L self.i_symbol_type = 0 # index into SYMBOL_TYPE_LIST if excel_colors: self.color_list = EXCEL_COLOR_LIST else: self.color_list = BIG_COLOR_HEXSTR_LIST NS = parent_obj.NS # Start building new xml Element to be new Sheet in spreadsheet attribD = {NS('table:name'): plot_sheetname} newsheet = ET.Element(NS('table:table'), attrib=attribD) table_shapes = ET.Element(NS('table:shapes')) #print( 'table_shapes.text =',table_shapes.text ) def NS_attrib(attD): D = {} for key, val in list(attD.items()): D[NS(key)] = val return D attribD = NS_attrib({ 'draw:z-index': "1", 'draw:id': "id0", 'draw:style-name': "a0", 'draw:name': "Chart 1", 'svg:x': "0in", 'svg:y': "0in", 'svg:width': "9.47551in", 'svg:height': "6.88048in", 'style:rel-width': "scale", 'style:rel-height': "scale" }) draw_frame = ET.Element(NS('draw:frame'), attrib=attribD) attribD = NS_attrib({ 'xlink:href': "Object %i/" % num_chart, 'xlink:type': "simple", 'xlink:show': "embed", 'xlink:actuate': "onLoad" }) draw_obj = ET.Element(NS('draw:object'), attrib=attribD) svg_title = ET.Element(NS('svg:title')) svg_desc = ET.Element(NS('svg:desc')) draw_frame.append(draw_obj) draw_frame.append(svg_title) draw_frame.append(svg_desc) table_shapes.append(draw_frame) tab_col = ET.Element( NS('table:table-column'), attrib={NS('table:number-columns-repeated'): "16384"}) tab_row = ET.Element( NS('table:table-row'), attrib={NS('table:number-rows-repeated'): "1048576"}) tab_cell = ET.Element( NS('table:table-cell'), attrib={NS('table:number-columns-repeated'): "16384"}) tab_row.append(tab_cell) newsheet.append(table_shapes) newsheet.append(tab_col) newsheet.append(tab_row) # make sure any added Element objects are in nsOD, rev_nsOD and qnameOD of parent_obj def add_tag(tag): sL = tag.split('}') uri = sL[0][1:] name = sL[1] parent_obj.qnameOD[tag] = parent_obj.nsOD[uri] + ':' + name def add_tags(obj): if hasattr(obj, 'tag'): add_tag(obj.tag) if hasattr(obj, 'attrib'): for q, v in list(obj.attrib.items()): add_tag(q) for parent in newsheet.iter(): add_tags(parent) for child in parent.getchildren(): add_tags(child) self.xmlSheetObj = newsheet self.plot_sheetname = '' self.data_sheetname = '' self.title = '' self.xlabel = '' self.ylabel = '' self.y2label = '' self.ycolL = None self.ycol2L = None self.logx = False self.logy = False self.log2y = False self.xcolL = [] self.ycolDataSheetNameL = [] self.xcol2L = [] self.ycol2_DataSheetNameL = [] self.showMarkerL = None self.showMarker2L = None self.markerTypeL = None self.markerType2L = None self.showLineL = None self.showLine2L = None self.lineThkL = None self.lineThk2L = None self.lineStyleL = None self.lineStyle2L = None self.set_of_line_styles = set() self.markerHtWdL = None self.markerHtWd2L = None self.showUnits = True self.colorL = None self.color2L = None self.labelL = None self.label2L = None
def build_chart_object_content(chart_obj, plotSheetObj): """When chart_obj is input, it still holds values from the original template""" chart = chart_obj.find('office:body/office:chart/chart:chart') plotSheetObj.chart_obj = chart_obj nsOD = chart_obj.rev_nsOD doc = plotSheetObj.document def set_new_tag(elem, shortname, value): """ Adds shortname to qnames and sets attrib variable on elem shortname format is "chart:symbol-type" """ doc.add_tag(NS(shortname, nsOD), plotSheetObj.chart_obj) elem.set(NS(shortname, nsOD), value) title = chart.find('chart:title/text:p', nsOD) #print( 'title.text = ',title.text) title.text = plotSheetObj.title plot_area = chart.find('chart:plot-area', nsOD) #print( 'plot_area =',plot_area ) axisL = plot_area.findall('chart:axis', nsOD) #print( 'axisL =',axisL ) xaxis = None yaxis = None x2axis = None y2axis = None for axis in axisL: dim_xy = axis.get( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}dimension') dim_name = axis.get( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}name') #print( 'dim_xy =',dim_xy ) if dim_xy == 'x' and dim_name == 'primary-x': xaxis = axis if dim_xy == 'y' and dim_name == 'primary-y': yaxis = axis # ODDLY... Excel uses capital X in "secondary-X" label #if dim_xy == 'x' and dim_name.startswith('secondary'): # x2axis = axis if dim_xy == 'y' and dim_name.startswith('secondary'): y2axis = axis xtitle = xaxis.find('chart:title/text:p', nsOD) ytitle = yaxis.find('chart:title/text:p', nsOD) xUnitsStr, yUnitsStr, y2UnitsStr = get_all_units_on_chart(plotSheetObj) if plotSheetObj.showUnits: xtitle.text = plotSheetObj.xlabel + xUnitsStr else: xtitle.text = plotSheetObj.xlabel if plotSheetObj.showUnits and plotSheetObj.ycolL: ytitle.text = plotSheetObj.ylabel + yUnitsStr else: ytitle.text = plotSheetObj.ylabel # Look for secondary y axis if plotSheetObj.ycol2L: y2title = y2axis.find('chart:title/text:p', nsOD) if plotSheetObj.showUnits: y2title.text = plotSheetObj.y2label + y2UnitsStr else: y2title.text = plotSheetObj.y2label chart_seriesL = plot_area.findall('chart:series', nsOD) y_series = None y2_series = None for c_series in chart_seriesL: axis_name = c_series.get( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}attached-axis') if axis_name == 'primary-y': y_series = c_series if axis_name == 'secondary-y': y2_series = c_series #print( 'y2_series =',y2_series ) # =============== Primary Y Axis ========================= series_label_cell_address = y_series.get( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}label-cell-address') #print( 'series_label_cell_address =',series_label_cell_address) series_value_cell_range = y_series.get( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}values-cell-range-address' ) #print( 'series_value_cell_range =',series_value_cell_range) ycol = plotSheetObj.ycolL[0] col_letter = get_col_letters_from_number(ycol) sht_name = plotSheetObj.ycolDataSheetNameL[0] dataTableObj = doc.data_table_objD[sht_name] val_cell_range = '%s.$%s$3:.$%s$%i' % (sht_name, col_letter, col_letter, dataTableObj.nrows) #print( 'val_cell_range =',val_cell_range) lab_cell = '%s.$%s$1' % (sht_name, col_letter) y_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}label-cell-address', lab_cell) y_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}values-cell-range-address', val_cell_range) chart_domain = y_series.find('chart:domain', nsOD) chart_data_point = y_series.find('chart:data-point', nsOD) #print( chart_domain.items()) #print( chart_data_point.items()) xcol = plotSheetObj.xcolL[0] xcol_letter = get_col_letters_from_number(xcol) xval_cell_range = '%s.$%s$3:.$%s$%i' % (sht_name, xcol_letter, xcol_letter, dataTableObj.nrows) chart_domain.set( '{urn:oasis:names:tc:opendocument:xmlns:table:1.0}cell-range-address', xval_cell_range) chart_data_point.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}repeated', '%i' % (dataTableObj.nrows - 2, )) # Look for logarithmic X Axis if plotSheetObj.logx: auto_styles = chart_obj.find('office:automatic-styles') # Find "Axs0" logx_style, ipos_logx_style = find_elem_w_attrib( 'style:style', auto_styles, nsOD, attrib={'style:name': 'Axs0'}, nth_match=0) #print( 'FOUND: ipos_logx_style = ', ipos_logx_style ) chart_prop = logx_style.find(NS('style:chart-properties', nsOD)) chart_prop.attrib = NS_attrib( { "chart:display-label": "true", "chart:link-data-style-to-source": "true", "chart:logarithmic": "true", "chart:tick-marks-major-inner": "false", "chart:tick-marks-major-outer": "true", "chart:tick-marks-minor-inner": "true", "chart:tick-marks-minor-outer": "true", "chart:visible": "true" }, nsOD) # Find "GMa0" logx_style, ipos_logx_style = find_elem_w_attrib( 'style:style', auto_styles, nsOD, attrib={'style:name': 'GMa0'}, nth_match=0) #print( 'FOUND: ipos_logx_style = ', ipos_logx_style ) stroke_style = logx_style.find(NS('style:graphic-properties', nsOD)) stroke_style.set(NS('draw:stroke', nsOD), 'solid') del stroke_style.attrib[NS('draw:stroke-dash', nsOD)] # Find "G0S0" logx_style, ipos_logx_style = find_elem_w_attrib( 'style:style', auto_styles, nsOD, attrib={'style:name': 'G0S0'}, nth_match=0) #print( 'FOUND: ipos_logx_style = ', ipos_logx_style ) new_elem_1 = ET.SubElement( auto_styles, "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}style", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}family', 'chart'), ('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name', 'GMi0') ])) new_elem_2 = ET.SubElement( new_elem_1, "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}graphic-properties", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}fill', 'none'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke', 'dash'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke-dash', 'a4'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-width', '0.01042in'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-color', '#000000'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-opacity', '100%'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke-linejoin', 'round') ])) # Add minor grid to xaxis new_elem_3 = ET.SubElement( xaxis, "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}grid", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}class', 'minor'), ('{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}style-name', 'GMi0') ])) if plotSheetObj.ycolL and len(plotSheetObj.ycolL) >= 1: auto_styles = chart_obj.find('office:automatic-styles') autostyleL = auto_styles.findall('style:style', nsOD) ref_series_style = None istyle_loc = 0 for iloc, astyle in enumerate(autostyleL): if astyle.get( '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name' ) == 'G0S0': ref_series_style = astyle istyle_loc = iloc + 2 # graphic-properties elem = ref_series_style.find("style:graphic-properties", nsOD) c = plotSheetObj.colorL[0] if not c is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-color", c) elem.set( "{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}fill-color", c) w = plotSheetObj.lineThkL[0] if not w is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-width", w) if not plotSheetObj.showLineL[0]: #elem.attrib.clear() elem.set(NS("draw:fill", nsOD), "none") elem.set(NS("draw:stroke", nsOD), "none") else: # we're showing the line, should it have a style??? i_style = plotSheetObj.lineStyleL[0] #print('i_style =', i_style) if i_style > 0: elem.set(NS("draw:stroke-dash", nsOD), get_dash_a_name(i_style)) elem.set(NS("draw:stroke", nsOD), 'dash') else: elem.set(NS("draw:stroke", nsOD), "solid") # .............. Failed Experiment ................... #doc.add_tag(NS("draw:symbol-color", nsOD), plotSheetObj.chart_obj) #elem.set(NS("draw:symbol-color", nsOD), "#999999") #doc.add_tag(NS("draw:fill-color", nsOD), plotSheetObj.chart_obj) #elem.set(NS("draw:fill-color", nsOD), "#999999") #elem.set(NS("draw:fill", nsOD), "solid") # chart-properties elem = ref_series_style.find("style:chart-properties", nsOD) if plotSheetObj.showMarkerL[ 0]: # showMarkerL is guaranteed to have same dimension as ycolL elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "automatic") else: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "none") for at in [ NS("chart:symbol-name", nsOD), NS("chart:symbol-width", nsOD), NS("chart:symbol-height", nsOD) ]: if elem.get(at, None) is not None: del elem.attrib[at] # elem is chart-properties if not plotSheetObj.showLineL[0]: set_new_tag(elem, "chart:symbol-type", "named-symbol") set_new_tag(elem, "chart:symbol-name", plotSheetObj.markerTypeL[0]) hw = plotSheetObj.markerHtWdL[0] set_new_tag(elem, "chart:symbol-width", hw) set_new_tag(elem, "chart:symbol-height", hw) # Look for logarithmic scale on primary y if plotSheetObj.logy: #print( 'Got log y' ) # Find "Axs1" logy_style, ipos_logy_style = find_elem_w_attrib( 'style:style', auto_styles, nsOD, attrib={'style:name': 'Axs1'}, nth_match=0) #print( 'FOUND: ipos_logy_style = ', ipos_logy_style ) chart_prop = logy_style.find(NS('style:chart-properties', nsOD)) chart_prop.set(NS("chart:logarithmic", nsOD), "true") chart_prop.set(NS("chart:tick-marks-minor-inner", nsOD), "true") chart_prop.set(NS("chart:tick-marks-minor-outer", nsOD), "true") # Find "GMa1" logy_style, ipos_logy_style = find_elem_w_attrib( 'style:style', auto_styles, nsOD, attrib={'style:name': 'GMa1'}, nth_match=0) #print( 'FOUND: ipos_logy_style = ', ipos_logy_style ) logy_style.set(NS("draw:stroke", nsOD), "dash") # Find "G0S1" logy_style, ipos_logy_style = find_elem_w_attrib( 'style:style', auto_styles, nsOD, attrib={'style:name': 'G0S1'}, nth_match=0) #print( 'FOUND: ipos_logy_style = ', ipos_logy_style ) new_elem_1 = ET.SubElement( auto_styles, "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}style", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}family', 'chart'), ('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name', 'GMi1') ])) new_elem_2 = ET.SubElement( new_elem_1, "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}graphic-properties", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}fill', 'none'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke', 'dash'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke-dash', 'a4'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-width', '0.01042in'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-color', '#000000'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-opacity', '100%'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke-linejoin', 'round') ])) # Add minor grid to yaxis new_elem_3 = ET.SubElement( yaxis, "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}grid", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}class', 'minor'), ('{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}style-name', 'GMi1') ])) #print( ref_series_style.items()) nG0S = 1 for ycol in plotSheetObj.ycolL[1:]: new_style = deepcopy(ref_series_style) new_style.set( '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name', 'G0S%i' % nG0S) elem = new_style.find("style:graphic-properties", nsOD) c = plotSheetObj.colorL[nG0S] if not c is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-color", c) elem.set( "{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}fill-color", c) w = plotSheetObj.lineThkL[nG0S] if not w is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-width", w) if not plotSheetObj.showLineL[nG0S]: #elem.attrib.clear() elem.set(NS("draw:fill", nsOD), "none") elem.set(NS("draw:stroke", nsOD), "none") else: i_style = plotSheetObj.lineStyleL[nG0S] #print('i_style =', i_style) if i_style > 0: elem.set(NS("draw:stroke-dash", nsOD), get_dash_a_name(i_style)) elem.set(NS("draw:stroke", nsOD), 'dash') else: elem.set(NS("draw:stroke", nsOD), "solid") elem = new_style.find("style:chart-properties", nsOD) if plotSheetObj.showMarkerL[ nG0S]: # showMarkerL is guaranteed to have same dimension as ycolL elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "automatic") else: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "none") for at in [ NS("chart:symbol-name", nsOD), NS("chart:symbol-width", nsOD), NS("chart:symbol-height", nsOD) ]: if elem.get(at, None) is not None: del elem.attrib[at] # elem is chart-properties if not plotSheetObj.showLineL[nG0S]: set_new_tag(elem, "chart:symbol-type", "named-symbol") set_new_tag(elem, "chart:symbol-name", plotSheetObj.markerTypeL[nG0S]) hw = plotSheetObj.markerHtWdL[nG0S] set_new_tag(elem, "chart:symbol-width", hw) set_new_tag(elem, "chart:symbol-height", hw) auto_styles.insert(istyle_loc, new_style) istyle_loc += 1 new_chart_series = deepcopy(y_series) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}style-name', 'G0S%i' % nG0S) col_letter = get_col_letters_from_number(ycol) sht_name = plotSheetObj.ycolDataSheetNameL[nG0S] dataTableObj = doc.data_table_objD[sht_name] val_cell_range = '%s.$%s$3:.$%s$%i' % ( sht_name, col_letter, col_letter, dataTableObj.nrows) #print( 'val_cell_range =',val_cell_range) lab_cell = '%s.$%s$1' % (sht_name, col_letter) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}label-cell-address', lab_cell) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}values-cell-range-address', val_cell_range) xcol = plotSheetObj.xcolL[nG0S] xcol_letter = get_col_letters_from_number(xcol) xval_cell_range = '%s.$%s$3:.$%s$%i' % ( sht_name, xcol_letter, xcol_letter, dataTableObj.nrows) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:table:1.0}cell-range-address', xval_cell_range) #ipos = len(plot_area)-1 #plot_area.insert(ipos, new_chart_series ) insert_below_series_named('G0S%i' % (nG0S - 1, ), new_chart_series, plot_area) nG0S += 1 # =============== Secondary Y Axis ========================= # Look for secondary y axis if (plotSheetObj.ycol2L is not None) and (len(plotSheetObj.ycol2L) > 0): series_label_cell_address = y2_series.get( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}label-cell-address' ) #print( 'Y2 series_label_cell_address =',series_label_cell_address) series_value_cell_range = y2_series.get( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}values-cell-range-address' ) #print( 'Y2 series_value_cell_range =',series_value_cell_range) ycol = plotSheetObj.ycol2L[0] col_letter = get_col_letters_from_number(ycol) sht_name = plotSheetObj.ycol2_DataSheetNameL[0] dataTableObj = doc.data_table_objD[sht_name] val_cell_range = '%s.$%s$3:.$%s$%i' % (sht_name, col_letter, col_letter, dataTableObj.nrows) #print( 'Y2 val_cell_range =',val_cell_range) lab_cell = '%s.$%s$1' % (sht_name, col_letter) y2_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}label-cell-address', lab_cell) y2_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}values-cell-range-address', val_cell_range) chart_domain = y2_series.find('chart:domain', nsOD) chart_data_point = y2_series.find('chart:data-point', nsOD) #print( chart_domain.items()) #print( chart_data_point.items()) xcol = plotSheetObj.xcol2L[0] xcol_letter = get_col_letters_from_number(xcol) xval_cell_range = '%s.$%s$3:.$%s$%i' % ( sht_name, xcol_letter, xcol_letter, dataTableObj.nrows) chart_domain.set( '{urn:oasis:names:tc:opendocument:xmlns:table:1.0}cell-range-address', xval_cell_range) chart_data_point.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}repeated', '%i' % (dataTableObj.nrows - 2, )) # Look for logarithmic scale on primary y if plotSheetObj.log2y: #print( 'Got log on secondary y' ) # Find "Axs2" logy_style, ipos_logy_style = find_elem_w_attrib( 'style:style', auto_styles, nsOD, attrib={'style:name': 'Axs2'}, nth_match=0) #print( 'FOUND: ipos_logy_style = ', ipos_logy_style ) chart_prop = logy_style.find(NS('style:chart-properties', nsOD)) chart_prop.set(NS("chart:logarithmic", nsOD), "true") chart_prop.set(NS("chart:tick-marks-minor-inner", nsOD), "true") chart_prop.set(NS("chart:tick-marks-minor-outer", nsOD), "true") # Make "GMi2" new_elem_1 = ET.SubElement( auto_styles, "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}style", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}family', 'chart'), ('{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name', 'GMi2') ])) # Looks better with only one set of y grids... Seems obvious now if 0: #plotSheetObj.ycolL and (not plotSheetObj.logy): new_elem_2 = ET.SubElement( new_elem_1, "{urn:oasis:names:tc:opendocument:xmlns:style:1.0}graphic-properties", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}fill', 'none'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke', 'dash'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke-dash', 'a4'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-width', '0.01042in'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-color', '#000000'), ('{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-opacity', '100%'), ('{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}stroke-linejoin', 'round') ])) # Add minor grid to yaxis new_elem_3 = ET.SubElement( y2axis, "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}grid", attrib=OrderedDict([ ('{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}class', 'minor'), ('{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}style-name', 'GMi2') ])) # If more than one curve on secondary y... if len(plotSheetObj.ycol2L) >= 1: auto_styles = chart_obj.find('office:automatic-styles') autostyleL = auto_styles.findall('style:style', nsOD) ref_series_style = None istyle_loc = 0 for iloc, astyle in enumerate(autostyleL): if astyle.get( '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name' ) == 'G1S0': ref_series_style = astyle istyle_loc = iloc + 2 elem = ref_series_style.find("style:graphic-properties", nsOD) c = plotSheetObj.color2L[0] if not c is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-color", c) elem.set( "{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}fill-color", c) w = plotSheetObj.lineThk2L[0] if not w is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-width", w) if not plotSheetObj.showLine2L[0]: elem.set(NS("draw:fill", nsOD), "none") elem.set(NS("draw:stroke", nsOD), "none") else: i_style = plotSheetObj.lineStyle2L[0] #print('i_style =', i_style) if i_style > 0: elem.set(NS("draw:stroke-dash", nsOD), get_dash_a_name(i_style)) elem.set(NS("draw:stroke", nsOD), 'dash') else: elem.set(NS("draw:stroke", nsOD), "solid") elem = ref_series_style.find("style:chart-properties", nsOD) if plotSheetObj.showMarkerL[ 0]: # showMarkerL is guaranteed to have same dimension as ycol2L elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "automatic") else: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "none") for at in [ NS("chart:symbol-name", nsOD), NS("chart:symbol-width", nsOD), NS("chart:symbol-height", nsOD) ]: if elem.get(at, None) is not None: del elem.attrib[at] # elem is chart-properties if not plotSheetObj.showLine2L[0]: set_new_tag(elem, "chart:symbol-type", "named-symbol") set_new_tag(elem, "chart:symbol-name", plotSheetObj.markerType2L[0]) hw = plotSheetObj.markerHtWd2L[0] set_new_tag(elem, "chart:symbol-width", hw) set_new_tag(elem, "chart:symbol-height", hw) plotSheetObj.ref_series_style2 = ref_series_style #print( ref_series_style.items()) nG1S = 1 for ycol in plotSheetObj.ycol2L[1:]: new_style = deepcopy(ref_series_style) new_style.set( '{urn:oasis:names:tc:opendocument:xmlns:style:1.0}name', 'G1S%i' % nG1S) elem = new_style.find("style:graphic-properties", nsOD) c = plotSheetObj.color2L[nG1S] if not c is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-color", c) elem.set( "{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}fill-color", c) w = plotSheetObj.lineThk2L[nG1S] if not w is None: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}stroke-width", w) if not plotSheetObj.showLine2L[nG1S]: elem.set(NS("draw:fill", nsOD), "none") elem.set(NS("draw:stroke", nsOD), "none") else: i_style = plotSheetObj.lineStyle2L[nG1S] #print('i_style =', i_style) if i_style > 0: elem.set(NS("draw:stroke-dash", nsOD), get_dash_a_name(i_style)) elem.set(NS("draw:stroke", nsOD), 'dash') else: elem.set(NS("draw:stroke", nsOD), "solid") elem = new_style.find("style:chart-properties", nsOD) if plotSheetObj.showMarker2L[ nG1S]: # showMarkerL is guaranteed to have same dimension as ycol2L elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "automatic") else: elem.set( "{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}symbol-type", "none") for at in [ NS("chart:symbol-name", nsOD), NS("chart:symbol-width", nsOD), NS("chart:symbol-height", nsOD) ]: if elem.get(at, None) is not None: del elem.attrib[at] # elem is chart-properties if not plotSheetObj.showLine2L[nG1S]: set_new_tag(elem, "chart:symbol-type", "named-symbol") set_new_tag(elem, "chart:symbol-name", plotSheetObj.markerType2L[nG1S]) hw = plotSheetObj.markerHtWd2L[nG1S] set_new_tag(elem, "chart:symbol-width", hw) set_new_tag(elem, "chart:symbol-height", hw) auto_styles.insert(istyle_loc, new_style) istyle_loc += 1 new_chart_series = deepcopy(y2_series) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}style-name', 'G1S%i' % nG1S) col_letter = get_col_letters_from_number(ycol) sht_name = plotSheetObj.ycol2_DataSheetNameL[nG1S] dataTableObj = doc.data_table_objD[sht_name] val_cell_range = '%s.$%s$3:.$%s$%i' % ( sht_name, col_letter, col_letter, dataTableObj.nrows) #print( 'val_cell_range =',val_cell_range) lab_cell = '%s.$%s$1' % (sht_name, col_letter) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}label-cell-address', lab_cell) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}values-cell-range-address', val_cell_range) xcol = plotSheetObj.xcol2L[nG1S] xcol_letter = get_col_letters_from_number(xcol) xval_cell_range = '%s.$%s$3:.$%s$%i' % ( sht_name, xcol_letter, xcol_letter, dataTableObj.nrows) new_chart_series.set( '{urn:oasis:names:tc:opendocument:xmlns:table:1.0}cell-range-address', xval_cell_range) ipos = len(plot_area) - 1 plot_area.insert(ipos, new_chart_series) nG1S += 1 #plot_area._children.sort( key=lambda child: child.get('{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}style-name', '0') ) plotSheetObj.istyle_loc = istyle_loc plotSheetObj.y_series = y_series plotSheetObj.chart_obj = chart_obj
def __init__(self, xml_file_name_or_src): """ Read and parse xml file using modified version of standard python xml.etree.ElementTree. xml_file_name_or_src can be a file name like: "content.xml" OR can be xml source. """ #xml_file_name_or_src = xml_file_name_or_src.decode('utf-8') if xml_file_name_or_src.endswith('.xml') and len(xml_file_name_or_src)<256: self.xml_file_name_or_src = xml_file_name_or_src fInp = io.open(xml_file_name_or_src, 'rt', encoding='utf-8') xml_src = fInp.read() fInp.close() else: self.xml_file_name_or_src = None xml_src = xml_file_name_or_src self.xml_header = '' # Assume no header unless found at head of file match = header_re.match(xml_src) if match: #print( 'Found XML Header: ' + match.group(0) ) self.xml_header = match.group(0) # will need \n when serialized # ns entries like: ('urn:oasis:names:tc:opendocument:xmlns:table:1.0', u'table') self.nsOD = OrderedDict() # rev_ns entries like: (u'table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0') self.rev_nsOD = OrderedDict() # qname entries like: ('{urn:oasis:names:tc:opendocument:xmlns:office:1.0}document-content', u'office:document-content') self.qnameOD = OrderedDict() events = ("start", "end", "start-ns", "end-ns") context = ET.iterparse(StringIO(xml_src), events=events) for event, elem in context: if event=="start": #print('type elem.tag =', type(elem.tag)) #print(' type("}") =',type("}")) sL = elem.tag.split('}') if len(sL) == 2: name = sL[1] uri = sL[0][1:] self.qnameOD[elem.tag] = '%s:%s'%(self.nsOD[uri], name) for qname,v in elem.attrib.items(): sL = qname.split('}') if len(sL) == 2: name = sL[1] uri = sL[0][1:] self.qnameOD[qname] = '%s:%s'%(self.nsOD[uri], name) if event=="start-ns": self.nsOD[elem[1]] = elem[0] # like: ('urn:oasis:names:tc:opendocument:xmlns:table:1.0', u'table') self.rev_nsOD[elem[0]] = elem[1] # like: (u'table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0') self.context = context #self.root = ET.ElementTree( context.root ) self.root = context.root self.parentD = {} # index=child Element object, value=parent Element object self.depthD = {} # index=Element object, value = depth in xml tree self.original_posD = {} # index=Element object, value=tuple of child position (e.g. (0,3,1)) self.get_elem_from_orig_posD = {} # reverse lookup of "original_posD" self.max_depth = 0 self.short_pathD = {} # index=Element, value = short name (like: "ns0:name1/ns1:xyz/ns3:abc") # After building tree, create self.parentD for all Elements self.parentD[self.root] = None self.depthD[self.root] = 0 self.short_pathD[self.root] = self.qnameOD[ self.root.tag ] # no calc req'd... just = qname self.original_posD[self.root] = (0,) # tuple of position temp_short_path_counterD = {} # just used here to help count occurances of short path self.short_path_counterD = {} # index=Element, value=short path counter value self.short_path_parent_counterD = {} # index=Element, value=parent's short path counter value self.short_path_counterD[self.root] = 1 # 1st (and only) occurance self.short_path_parent_counterD[self.root] = 1 # 1st (and only) occurance for parent in self.root.iter(): try: for ichild, child in enumerate(parent.getchildren()): self.parentD[child] = parent self.depthD[child] = self.depthD[parent] + 1 self.max_depth = max(self.max_depth, self.depthD[child]) L = list(self.original_posD[parent]) L.append( ichild ) self.original_posD[child] = tuple( L ) short_path = self.get_short_path( child ) self.short_pathD[child] = short_path temp_short_path_counterD[(parent,short_path)] = temp_short_path_counterD.get((parent,short_path), 0) + 1 self.short_path_counterD[child] = '%s,%s'%(self.short_path_counterD[parent], temp_short_path_counterD[(parent,short_path)]) self.short_path_parent_counterD[child] = '%s'%self.short_path_counterD[parent] except: print( 'NOTICE: No children for:', parent ) for key,item in self.original_posD.items(): self.get_elem_from_orig_posD[item] = key # get elem from original_posD
def __init__(self, data_sheetname, list_of_rows, parent_obj): """Inits SpreadSheet with filename and blank content. NS: cleans up the namespace callouts of the xml Element Attributes:: data_sheetname: name of data sheet nrows: number of rows including label and units rows ncols: max number of cols labelL: list of labels unitsL: list of units """ NS = parent_obj.NS self.data_sheetname = data_sheetname self.list_of_rows = list_of_rows # calc number of rows and columns in data self.nrows = len(list_of_rows) self.ncols = 0 for row in list_of_rows: self.ncols = max(self.ncols, len(row)) # make sure that row1 and row2 contain labels and units self.labelL = [] self.unitsL = [] for n in range(self.ncols): try: s = str(list_of_rows[0][n]).strip() except: s = 'Column %i' % (n + 1, ) self.labelL.append(s) try: s = str(list_of_rows[1][n]).strip() except: s = '' self.unitsL.append(s) # Start building new xml Element to be new Sheet in spreadsheet #print( nsmap ) attribD = { NS('table:name'): data_sheetname, NS('table:style-name'): "ta1" } newsheet = ET.Element(NS('table:table'), attrib=attribD) colD = { NS('table:style-name'): "co1", NS('table:number-columns-repeated'): "16384", NS('table:default-cell-style-name'): "ce1" } col_elm = ET.Element(NS('table:table-column'), attrib=colD) newsheet.append(col_elm) str_cellD = { NS('office:value-type'): "string", NS('table:style-name'): "ce1" } float_cellD = { NS('office:value-type'): "float", NS('table:style-name'): "ce1" } row_rep = 1048576 # max number of rows for row in list_of_rows: row_obj = ET.Element(NS('table:table-row'), attrib={NS('table:style-name'): "ro1"}) col_rep = 16384 # max number of columns for value in row: try: fval = float(value) float_cellD[NS('office:value')] = str(fval) D = float_cellD text_p = "%g" % fval except: D = str_cellD text_p = "%s" % value cell_obj = ET.Element(NS('table:table-cell'), attrib=D) text_obj = ET.Element(NS('text:p')) text_obj.text = text_p cell_obj.append(text_obj) row_obj.append(cell_obj) col_rep -= 1 # decrement max available columns remaining last_cell_obj = ET.Element( NS('table:table-cell'), attrib={NS('table:number-columns-repeated'): "%i" % col_rep}) row_obj.append(last_cell_obj) newsheet.append(row_obj) row_rep -= 1 # decrement max remaining number of rows last_row_obj = ET.Element(NS('table:table-row'), attrib={ NS('table:style-name'): "ro1", NS('table:number-rows-repeated'): "%i" % row_rep }) last_row_obj.append( ET.Element(NS('table:table-cell'), attrib={NS('table:number-columns-repeated'): "16384"})) newsheet.append(last_row_obj) # make sure any added Element objects are in nsOD, rev_nsOD and qnameOD of parent_obj # make sure any added Element objects are in nsOD, rev_nsOD and qnameOD of parent_obj def add_tag(tag): sL = tag.split('}') uri = sL[0][1:] name = sL[1] parent_obj.qnameOD[tag] = parent_obj.nsOD[uri] + ':' + name def add_tags(obj): if hasattr(obj, 'tag'): add_tag(obj.tag) if hasattr(obj, 'attrib'): for q, v in list(obj.attrib.items()): add_tag(q) for parent in newsheet.iter(): add_tags(parent) for child in parent.getchildren(): add_tags(child) self.xmlSheetObj = newsheet
def __init__(self, xml_file_name_or_src): """ Read and parse xml file using modified version of standard python xml.etree.ElementTree. xml_file_name_or_src can be a file name like: "content.xml" OR can be xml source. """ #xml_file_name_or_src = xml_file_name_or_src.decode('utf-8') if xml_file_name_or_src.endswith( '.xml') and len(xml_file_name_or_src) < 256: self.xml_file_name_or_src = xml_file_name_or_src fInp = io.open(xml_file_name_or_src, 'rt', encoding='utf-8') xml_src = fInp.read() fInp.close() else: self.xml_file_name_or_src = None xml_src = xml_file_name_or_src self.xml_header = '' # Assume no header unless found at head of file match = header_re.match(xml_src) if match: #print( 'Found XML Header: ' + match.group(0) ) self.xml_header = match.group(0) # will need \n when serialized # ns entries like: ('urn:oasis:names:tc:opendocument:xmlns:table:1.0', u'table') self.nsOD = OrderedDict() # rev_ns entries like: (u'table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0') self.rev_nsOD = OrderedDict() # qname entries like: ('{urn:oasis:names:tc:opendocument:xmlns:office:1.0}document-content', u'office:document-content') self.qnameOD = OrderedDict() events = ("start", "end", "start-ns", "end-ns") context = ET.iterparse(StringIO(xml_src), events=events) for event, elem in context: if event == "start": #print('type elem.tag =', type(elem.tag)) #print(' type("}") =',type("}")) sL = elem.tag.split('}') if len(sL) == 2: name = sL[1] uri = sL[0][1:] self.qnameOD[elem.tag] = '%s:%s' % (self.nsOD[uri], name) for qname, v in list(elem.attrib.items()): sL = qname.split('}') if len(sL) == 2: name = sL[1] uri = sL[0][1:] self.qnameOD[qname] = '%s:%s' % (self.nsOD[uri], name) if event == "start-ns": self.nsOD[elem[1]] = elem[ 0] # like: ('urn:oasis:names:tc:opendocument:xmlns:table:1.0', u'table') self.rev_nsOD[elem[0]] = elem[ 1] # like: (u'table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0') self.context = context #self.root = ET.ElementTree( context.root ) self.root = context.root self.parentD = { } # index=child Element object, value=parent Element object self.depthD = {} # index=Element object, value = depth in xml tree self.original_posD = { } # index=Element object, value=tuple of child position (e.g. (0,3,1)) self.get_elem_from_orig_posD = {} # reverse lookup of "original_posD" self.max_depth = 0 self.short_pathD = { } # index=Element, value = short name (like: "ns0:name1/ns1:xyz/ns3:abc") # After building tree, create self.parentD for all Elements self.parentD[self.root] = None self.depthD[self.root] = 0 self.short_pathD[self.root] = self.qnameOD[ self.root.tag] # no calc req'd... just = qname self.original_posD[self.root] = (0, ) # tuple of position temp_short_path_counterD = { } # just used here to help count occurances of short path self.short_path_counterD = { } # index=Element, value=short path counter value self.short_path_parent_counterD = { } # index=Element, value=parent's short path counter value self.short_path_counterD[self.root] = 1 # 1st (and only) occurance self.short_path_parent_counterD[ self.root] = 1 # 1st (and only) occurance for parent in self.root.iter(): try: for ichild, child in enumerate(parent.getchildren()): self.parentD[child] = parent self.depthD[child] = self.depthD[parent] + 1 self.max_depth = max(self.max_depth, self.depthD[child]) L = list(self.original_posD[parent]) L.append(ichild) self.original_posD[child] = tuple(L) short_path = self.get_short_path(child) self.short_pathD[child] = short_path temp_short_path_counterD[( parent, short_path)] = temp_short_path_counterD.get( (parent, short_path), 0) + 1 self.short_path_counterD[child] = '%s,%s' % ( self.short_path_counterD[parent], temp_short_path_counterD[(parent, short_path)]) self.short_path_parent_counterD[ child] = '%s' % self.short_path_counterD[parent] except: print('NOTICE: No children for:', parent) for key, item in list(self.original_posD.items()): self.get_elem_from_orig_posD[ item] = key # get elem from original_posD