Support cells text alignment building schedule from ods #2885

Before - https://i.imgur.com/9QZ2YW2.png
After - https://i.imgur.com/M7ZHSJq.png
This commit is contained in:
Andrej730
2023-05-02 16:59:26 +05:00
parent f858dd14d9
commit 9f19b33764
2 changed files with 80 additions and 30 deletions
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from blenderbim.bim.module.drawing.svgwriter import SvgWriter
import svgwrite import svgwrite
from odf.opendocument import load from odf.opendocument import load
@@ -25,6 +26,7 @@ from odf.style import Style
FONT_SIZE = 4.13 FONT_SIZE = 4.13
class Scheduler: class Scheduler:
def schedule(self, infile, outfile): def schedule(self, infile, outfile):
self.svg = svgwrite.Drawing( self.svg = svgwrite.Drawing(
@@ -41,7 +43,16 @@ class Scheduler:
# looking for table-column-properties or table-row-properties # looking for table-column-properties or table-row-properties
if not style.firstChild: if not style.firstChild:
continue 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] table = doc.getElementsByType(Table)[0]
@@ -85,8 +96,35 @@ class Scheduler:
repeat = td.getAttribute("numbercolumnsrepeated") repeat = td.getAttribute("numbercolumnsrepeated")
repeat = int(repeat) if repeat else 1 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): 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.add(
self.svg.rect( self.svg.rect(
insert=(x, y), insert=(x, y),
@@ -96,7 +134,23 @@ class Scheduler:
) )
value = td.getElementsByType(P) value = td.getElementsByType(P)
if value: 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 x += width
tdi += column_span tdi += column_span
@@ -107,20 +161,15 @@ class Scheduler:
self.svg["viewBox"] = "0 0 {} {}".format(total_width, y) self.svg["viewBox"] = "0 0 {} {}".format(total_width, y)
self.svg.save(pretty=True) self.svg.save(pretty=True)
def add_text(self, text, x, y): def add_text(self, text, x, y, box_alignment="bottom-left"):
bottom_left_alignment = { box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment)
"alignment-baseline": "baseline", text_params = {
"dominant-baseline": "baseline", "font-size": FONT_SIZE,
"font-family": "OpenGost Type B TT",
"text-anchor": "start",
} }
text_tag = self.svg.text( text_params.update(box_alignment_params)
str(text).upper(), text_tag = self.svg.text(str(text).upper(), insert=tuple((x, y)), **text_params)
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) self.svg.add(text_tag)
def convert_to_mm(self, value): def convert_to_mm(self, value):
@@ -234,7 +234,7 @@ class SvgWriter:
rl = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), rl) rl = ifcopenshell.util.geolocation.auto_z2e(tool.Ifc.get(), rl)
rl *= unit_scale rl *= unit_scale
rl = "{:.3f}m".format(rl) 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)) self.svg.add(self.svg.text(f"RL +{rl}", insert=tuple(text_position), class_="SECTIONLEVEL", **text_style))
if tag: if tag:
self.svg.add(self.svg.text(tag, insert=(text_position[0], text_position[1] - 5), **text_style)) self.svg.add(self.svg.text(tag, insert=(text_position[0], text_position[1] - 5), **text_style))
@@ -263,7 +263,7 @@ class SvgWriter:
"UP", "UP",
insert=tuple(text_position), insert=tuple(text_position),
class_="STAIR", 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" line["stroke-dasharray"] = "12.5, 3, 3, 3"
axis_tag = tool.Ifc.get_entity(obj).Name 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.add(
self.svg.text( self.svg.text(
axis_tag, 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)) reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
text_position = symbol_position_svg 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.add(
self.svg.text( self.svg.text(
reference_id, 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)) reference_id, sheet_id = self.get_reference_and_sheet_id_from_annotation(tool.Ifc.get_entity(obj))
text_position = symbol_position_svg 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.add(
self.svg.text( self.svg.text(
reference_id, insert=(text_position[0], text_position[1] - 2.5), class_="ELEVATION", **text_style 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 (reference_id, sheet_id)
return ("-", "-") 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 """Convenience method to get svg parameters for text alignment
in a readable way. in a readable way.
@@ -721,7 +722,7 @@ class SvgWriter:
text_tag = self.svg.text( text_tag = self.svg.text(
"", "",
**(attribs | {"filter": "url(#fill-background)"}) if add_fill_bg else attribs, **(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) self.svg.add(text_tag)
@@ -795,7 +796,7 @@ class SvgWriter:
rl = "{:.3f}m".format(rl) rl = "{:.3f}m".format(rl)
box_alignment = "bottom-left" if projected_points[0].x <= projected_points[-1].x else "bottom-right" 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.add(
self.svg.text( self.svg.text(
"RL +{}".format(rl), "RL +{}".format(rl),
@@ -917,7 +918,7 @@ class SvgWriter:
text_offset = (text_position - center_position).xy.normalized() * 5 text_offset = (text_position - center_position).xy.normalized() * 5
text_position += text_offset 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)) angle_text = abs(round(math.degrees(angle), 3))
if is_reflex: if is_reflex:
angle_text = 360 - angle_text angle_text = 360 - angle_text
@@ -978,7 +979,7 @@ class SvgWriter:
) )
text_position += text_offset 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 = (points[-1].co - points[-2].co).length
radius = helper.format_distance(radius, precision=self.precision, decimal_places=self.decimal_places) radius = helper.format_distance(radius, precision=self.precision, decimal_places=self.decimal_places)
tag = element.Description or f"R{radius}" tag = element.Description or f"R{radius}"
@@ -1051,7 +1052,7 @@ class SvgWriter:
) )
text_position += text_offset 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)) self.svg.add(self.svg.text(tag, insert=tuple(text_position), class_="RADIUS", **text_style))
def draw_diameter_annotations(self, obj): def draw_diameter_annotations(self, obj):
@@ -1151,7 +1152,7 @@ class SvgWriter:
text_format(text), text_format(text),
insert=text_position, insert=text_position,
class_="DIMENSION", 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: if not show_description_only: