From 2d0b3562030c09de27d4d6aeeae8b9673e3db0e4 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 20 May 2024 12:00:59 +1000 Subject: [PATCH] See #4690. User can now configure font and font-scaling in schedules. --- .../blenderbim/bim/data/assets/schedule.css | 45 ++++++++++++++++ .../bim/module/drawing/scheduler.py | 52 ++++++++++++------- 2 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 src/blenderbim/blenderbim/bim/data/assets/schedule.css diff --git a/src/blenderbim/blenderbim/bim/data/assets/schedule.css b/src/blenderbim/blenderbim/bim/data/assets/schedule.css new file mode 100644 index 0000000000..151d7d4f78 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/data/assets/schedule.css @@ -0,0 +1,45 @@ +/* + * BlenderBIM Add-on - OpenBIM Blender Add-on + * Copyright (C) 2020, 2021 Dion Moult + * + * This file is part of BlenderBIM Add-on. + * + * BlenderBIM Add-on is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BlenderBIM Add-on is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with BlenderBIM Add-on. If not, see . + */ + +/** + * You may copy this `schedule.css` template alongside your input schedule + * document with the same filename. For example if you have a schedule called + * `door_types.ods`, you can create a `door_types.css` in the same folder to + * style that schedule. + */ + +/** + * If you specify a font size in CSS, such as text { font-size: 5; }, all fonts + * will be overriden to match that size. + * + * If your CSS, ODS, XLSX does not specify a font size, the variables below + * will specify the default font size. + * + * If your ODS, XLSX does specify a font size, they will scale linearly based + * on the variables below. + */ + +:root { + --font-size-pt: 12; + --font-size-px: 4.13; + --font-width: 0.45; +} + +text, tspan { /* 2.5mm */ fill: black; stroke: none; font-family: 'OpenGost Type B TT', 'DejaVu Sans Condensed', 'Liberation Sans', 'Arial Narrow', 'Arial'; } diff --git a/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py b/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py index 0bdad578e0..72da3bbfa7 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/scheduler.py @@ -16,22 +16,21 @@ # 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 os +import re +import bpy +import string import svgwrite import openpyxl +from blenderbim.bim.module.drawing.svgwriter import SvgWriter from odf.opendocument import load as load_ods from odf.table import Table, TableRow, TableColumn, TableCell from odf.text import P from odf.style import Style from textwrap import wrap from pathlib import Path -import string -FONT_SIZE = 4.13 -FONT_WIDTH = lambda size: size * 0.45 -FONT_SIZE_PT = 12 -FONT_FAMILY = "OpenGost Type B TT" DEBUG = False @@ -63,11 +62,29 @@ class Scheduler: ) self.padding = 1 self.margin = 1 + + self.parse_css(infile) if infile.endswith("ods"): self.schedule_ods(infile, outfile) elif infile.endswith("xlsx"): self.schedule_xlsx(infile, outfile) + def parse_css(self, infile): + stylesheet_path = os.path.splitext(infile)[0] + ".css" + if not os.path.exists(stylesheet_path): + stylesheet_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "assets", "schedule.css") + with open(stylesheet_path, "r") as stylesheet: + css = stylesheet.read() + + matches = re.search("--font-size-pt:\s*([0-9.]+);", css) + self.font_size_pt = float(matches.groups()[0]) if matches else 12 # Default to 12pt + matches = re.search("--font-size-px:\s*([0-9.]+);", css) + self.font_size_px = float(matches.groups()[0]) if matches else 4.13 # Magic number 4.13px ~= 12pt + matches = re.search("--font-width:\s*([0-9.]+);", css) + self.font_width = float(matches.groups()[0]) if matches else 0.45 # A magic number for OpenGost + + self.svg.defs.add(self.svg.style(css)) + def schedule_xlsx(self, infile, outfile): workbook = openpyxl.open(infile, data_only=True) sheet = workbook.active @@ -144,8 +161,8 @@ class Scheduler: x += unmerged_width continue - font_size = cell.font.size or 11 # 11pt default - font_size = font_size / FONT_SIZE_PT * FONT_SIZE # Magic? + font_size = cell.font.size or self.font_size_pt # 12pt default + font_size = font_size / self.font_size_pt * self.font_size_px # Magic? text_position = [0.0, 0.0] if cell.alignment.horizontal == "left": @@ -424,9 +441,9 @@ class Scheduler: italic_text = final_cell_style.get("font-style", None) == "italic" # NOTE: very naive since we're scaling text proportionally font_size = ( - float(final_cell_style.get("font-size", f"{FONT_SIZE_PT}pt")[:-2]) - / FONT_SIZE_PT - * FONT_SIZE + float(final_cell_style.get("font-size", f"{self.font_size_pt}pt")[:-2]) + / self.font_size_pt + * self.font_size_px ) if p_tags: @@ -523,10 +540,7 @@ class Scheduler: """ text_lines = [str(p) for p in p_tags] box_alignment_params = SvgWriter.get_box_alignment_parameters(box_alignment) - text_params = { - "font-size": font_size, - "font-family": FONT_FAMILY, - } + text_params = {"font-size": font_size} if bold: text_params["font-weight"] = "bold" if italic: @@ -546,13 +560,13 @@ class Scheduler: text_tag = self.svg.text("", **(text_params | {"font-size": "0"} | box_alignment_params)) - # TODO: should be done in less naive way - # without using magic number for FONT_WIDTH - # currently it might not work for all fonts and font sizes + # TODO: Should be done without using magic number for self.font_width if wrap_text: wrapped_lines = [] for line in text_lines: - wrapped_line = wrap(line, width=int(cell_width // FONT_WIDTH(font_size)), break_long_words=False) + wrapped_line = wrap( + line, width=int(cell_width // (font_size * self.font_width)), break_long_words=False + ) wrapped_lines.extend(wrapped_line) else: wrapped_lines = text_lines