From 9f19b337645ff589153dcada0047540b66946167 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 2 May 2023 16:59:26 +0500 Subject: [PATCH] Support cells text alignment building schedule from ods #2885 Before - https://i.imgur.com/9QZ2YW2.png After - https://i.imgur.com/M7ZHSJq.png --- .../bim/module/drawing/scheduler.py | 85 +++++++++++++++---- .../bim/module/drawing/svgwriter.py | 25 +++--- 2 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py b/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py index 8c1af1c2db..5ffc563010 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from blenderbim.bim.module.drawing.svgwriter import SvgWriter import svgwrite from odf.opendocument import load @@ -25,6 +26,7 @@ from odf.style import Style FONT_SIZE = 4.13 + class Scheduler: def schedule(self, infile, outfile): self.svg = svgwrite.Drawing( @@ -41,7 +43,16 @@ class Scheduler: # looking for table-column-properties or table-row-properties if not style.firstChild: continue - styles[name] = {key[1]: value for key, value in style.firstChild.attributes.items()} + if style.firstChild.tagName in ["style:table-column-properties", "style:table-row-properties"]: + style_children = [style.firstChild] + else: + # for style:table-cell-properties we need to collect also text and paragraph properties + style_children = style.childNodes + + styles[name] = {} + for child in style_children: + child_params = {key[1]: value for key, value in child.attributes.items()} + styles[name].update(child_params) table = doc.getElementsByType(Table)[0] @@ -77,7 +88,7 @@ class Scheduler: x = self.margin height = row_heights[tri] tdi = 0 - + for td in tr.getElementsByType(TableCell): column_span = td.getAttribute("numbercolumnsspanned") column_span = int(column_span) if column_span else 1 @@ -85,8 +96,35 @@ class Scheduler: repeat = td.getAttribute("numbercolumnsrepeated") repeat = int(repeat) if repeat else 1 + # figuring text alignment + style_name = td.getAttribute("stylename") + if ( + style_name + and "vertical-align" in styles[style_name] + and styles[style_name]["vertical-align"] != "automatic" + ): + vertical_align = styles[style_name]["vertical-align"] + else: + vertical_align = "bottom" + + if ( + style_name + and "text-align" in styles[style_name] + and styles[style_name]["text-align"] != "automatic" + ): + horizontal_align = styles[style_name]["text-align"] + horizontal_align = "middle" if horizontal_align == "center" else horizontal_align + else: + horizontal_align = "left" + + if vertical_align == "middle" and horizontal_align == "middle": + box_alignment = "center" + else: + box_alignment = f"{vertical_align}-{horizontal_align}" + + # drawing cells and text for i in range(0, repeat): - width = sum(column_widths[tdi:tdi + int(column_span)]) + width = sum(column_widths[tdi : tdi + int(column_span)]) self.svg.add( self.svg.rect( insert=(x, y), @@ -96,7 +134,23 @@ class Scheduler: ) value = td.getElementsByType(P) if value: - self.add_text(value[0], x + self.padding, y + height - self.padding) + # figuring text position based on alignment + text_position = [0, 0] + if box_alignment.endswith("left"): + text_position[0] = x + self.padding + elif box_alignment.endswith("middle") or box_alignment == "center": + text_position[0] = x + width / 2 + elif box_alignment.endswith("right"): + text_position[0] = x + width - self.padding + + if box_alignment.startswith("top"): + text_position[1] = y + self.padding + elif box_alignment == "center": + text_position[1] = y + height / 2 + elif box_alignment.startswith("bottom"): + text_position[1] = y + height - self.padding + + self.add_text(value[0], *text_position, box_alignment=box_alignment) x += width tdi += column_span @@ -107,21 +161,16 @@ class Scheduler: self.svg["viewBox"] = "0 0 {} {}".format(total_width, y) self.svg.save(pretty=True) - def add_text(self, text, x, y): - bottom_left_alignment = { - "alignment-baseline": "baseline", - "dominant-baseline": "baseline", + def add_text(self, text, x, y, box_alignment="bottom-left"): + box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment) + text_params = { + "font-size": FONT_SIZE, + "font-family": "OpenGost Type B TT", + "text-anchor": "start", } - text_tag = self.svg.text( - str(text).upper(), - insert=tuple((x, y)), - **({ - "font-size": FONT_SIZE, - "font-family": "OpenGost Type B TT", - "text-anchor": "start", - } | bottom_left_alignment) - ) - self.svg.add(text_tag) + text_params.update(box_alignment_params) + text_tag = self.svg.text(str(text).upper(), insert=tuple((x, y)), **text_params) + self.svg.add(text_tag) def convert_to_mm(self, value): # XSL is what defines the units of measurements in ODF diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py index e41498cc76..779ed699b4 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py @@ -234,7 +234,7 @@ class SvgWriter: rl = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), rl) rl *= unit_scale rl = "{:.3f}m".format(rl) - text_style = self.get_box_alignment_parameters("bottom-left") + text_style = SvgWriter.get_box_alignment_parameters("bottom-left") self.svg.add(self.svg.text(f"RL +{rl}", insert=tuple(text_position), class_="SECTIONLEVEL", **text_style)) if tag: self.svg.add(self.svg.text(tag, insert=(text_position[0], text_position[1] - 5), **text_style)) @@ -263,7 +263,7 @@ class SvgWriter: "UP", insert=tuple(text_position), class_="STAIR", - **self.get_box_alignment_parameters("center"), + **SvgWriter.get_box_alignment_parameters("center"), ) ) @@ -287,7 +287,7 @@ class SvgWriter: ) line["stroke-dasharray"] = "12.5, 3, 3, 3" axis_tag = tool.Ifc.get_entity(obj).Name - text_style = self.get_box_alignment_parameters("center") + text_style = SvgWriter.get_box_alignment_parameters("center") self.svg.add( self.svg.text( axis_tag, @@ -557,7 +557,7 @@ class SvgWriter: reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj)) text_position = symbol_position_svg - text_style = self.get_box_alignment_parameters("center") + text_style = SvgWriter.get_box_alignment_parameters("center") self.svg.add( self.svg.text( reference_id, @@ -590,7 +590,7 @@ class SvgWriter: reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj)) text_position = symbol_position_svg - text_style = self.get_box_alignment_parameters("center") + text_style = SvgWriter.get_box_alignment_parameters("center") self.svg.add( self.svg.text( reference_id, insert=(text_position[0], text_position[1] - 2.5), class_="ELEVATION", **text_style @@ -617,7 +617,8 @@ class SvgWriter: return (reference_id, sheet_id) return ("-", "-") - def get_box_alignment_parameters(self, box_alignment): + @staticmethod + def get_box_alignment_parameters(box_alignment): """Convenience method to get svg parameters for text alignment in a readable way. @@ -721,7 +722,7 @@ class SvgWriter: text_tag = self.svg.text( "", **(attribs | {"filter": "url(#fill-background)"}) if add_fill_bg else attribs, - **self.get_box_alignment_parameters(text_literal.BoxAlignment), + **SvgWriter.get_box_alignment_parameters(text_literal.BoxAlignment), ) self.svg.add(text_tag) @@ -795,7 +796,7 @@ class SvgWriter: rl = "{:.3f}m".format(rl) box_alignment = "bottom-left" if projected_points[0].x <= projected_points[-1].x else "bottom-right" - text_style = self.get_box_alignment_parameters(box_alignment) + text_style = SvgWriter.get_box_alignment_parameters(box_alignment) self.svg.add( self.svg.text( "RL +{}".format(rl), @@ -917,7 +918,7 @@ class SvgWriter: text_offset = (text_position - center_position).xy.normalized() * 5 text_position += text_offset - text_style = self.get_box_alignment_parameters("center") + text_style = SvgWriter.get_box_alignment_parameters("center") angle_text = abs(round(math.degrees(angle), 3)) if is_reflex: angle_text = 360 - angle_text @@ -978,7 +979,7 @@ class SvgWriter: ) text_position += text_offset - text_style = self.get_box_alignment_parameters("center") + text_style = SvgWriter.get_box_alignment_parameters("center") radius = (points[-1].co - points[-2].co).length radius = helper.format_distance(radius, precision=self.precision, decimal_places=self.decimal_places) tag = element.Description or f"R{radius}" @@ -1051,7 +1052,7 @@ class SvgWriter: ) text_position += text_offset - text_style = self.get_box_alignment_parameters("center") + text_style = SvgWriter.get_box_alignment_parameters("center") self.svg.add(self.svg.text(tag, insert=tuple(text_position), class_="RADIUS", **text_style)) def draw_diameter_annotations(self, obj): @@ -1151,7 +1152,7 @@ class SvgWriter: text_format(text), insert=text_position, class_="DIMENSION", - **(text_kwargs | self.get_box_alignment_parameters(box_alignment)), + **(text_kwargs | SvgWriter.get_box_alignment_parameters(box_alignment)), ) if not show_description_only: