diff --git a/docs/source/configuration/outputs.rst b/docs/source/configuration/outputs.rst index c93b556e..408adaf7 100644 --- a/docs/source/configuration/outputs.rst +++ b/docs/source/configuration/outputs.rst @@ -921,7 +921,7 @@ the image aspect ratio. Creating a group in your PCB ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Variuos outputs and preflights uses a PCB group to specify the position and +Various outputs and preflights uses a PCB group to specify the position and size of the target object to be placed in your PCB. Here we explain how to create a group. @@ -938,3 +938,8 @@ create a group. - Now edit the group and change its name according to the name specified by the output or preflight. +.. note:: + + For the `Draw Fancy Stackup` and `Include Table` preflights, the font can be + set by adding to the group a textbox with the desired font. You can leave the + textbox empty and hide the borders. \ No newline at end of file diff --git a/kibot/kicad/pcb_draw_helpers.py b/kibot/kicad/pcb_draw_helpers.py index 5b5c5e64..df929826 100644 --- a/kibot/kicad/pcb_draw_helpers.py +++ b/kibot/kicad/pcb_draw_helpers.py @@ -6,6 +6,7 @@ # Helper functions to draw on a BOARD object from ..gs import GS import pcbnew +import re if GS.ki7: # Is this change really needed??!!! People doesn't have much to do ... GR_TEXT_HJUSTIFY_LEFT = pcbnew.GR_TEXT_H_ALIGN_LEFT @@ -64,10 +65,10 @@ def draw_line(g, x1, y1, x2, y2, layer, line_w=10000): GS.board.Add(nl) -def draw_text(g, x, y, text, h, w, layer, bold=False, alignment=GR_TEXT_HJUSTIFY_LEFT): +def draw_text(g, x, y, text, h, w, layer, bold=False, alignment=GR_TEXT_HJUSTIFY_LEFT, font=None): nt = pcbnew.PCB_TEXT(GS.board) nt.SetText(text) - nt.SetBold(bold) + nt.SetTextX(x) nt.SetTextY(y+h) nt.SetLayer(layer) @@ -75,11 +76,27 @@ def draw_text(g, x, y, text, h, w, layer, bold=False, alignment=GR_TEXT_HJUSTIFY nt.SetTextHeight(h) nt.SetHorizJustify(alignment) nt.SetVertJustify(GR_TEXT_VJUSTIFY_CENTER) + if font: + # segfault if overbars (~{text}) with custom fonts + remove_overbars(nt) + nt.SetFont(font) + if bold: + nt.SetBold(bold) + g.AddItem(nt) GS.board.Add(nt) + return nt, nt.GetTextBox().GetWidth() +def remove_overbars(txt): + current_text = txt.GetText() + + if current_text: + cleaned_text = re.sub(r'~\{(.*?)\}', r'\1', current_text) + txt.SetText(cleaned_text) + + def draw_poly(g, points, layer, filled=False, line_w=10000): assert not points or len(points) < 3, "A polygon requires at least 3 points" sps = pcbnew.SHAPE_POLY_SET() @@ -97,10 +114,12 @@ def draw_poly(g, points, layer, filled=False, line_w=10000): GS.board.Add(ps) -def get_text_width(text, w=10000, bold=False): +def get_text_width(text, w=10000, bold=False, font=None): nt = pcbnew.PCB_TEXT(GS.board) nt.SetText(text) nt.SetBold(bold) + if font: + nt.SetFont(font) nt.SetTextWidth(w) width = nt.GetTextBox().GetWidth() return width diff --git a/kibot/pre_draw_fancy_stackup.py b/kibot/pre_draw_fancy_stackup.py index c9bd70d9..c4a65783 100644 --- a/kibot/pre_draw_fancy_stackup.py +++ b/kibot/pre_draw_fancy_stackup.py @@ -385,15 +385,15 @@ def analyze_stackup(ops, gerber): return stackup -def meassure_table(ops, gerber, via_layer_pairs, stackup): +def measure_table(ops, gerber, via_layer_pairs, stackup, font=None): # Set the maximum length of each column to the column title for now for c in ops._columns: c.title = TITLES[c.type] - c.max_len = get_text_width(c.title) + c.max_len = get_text_width(c.title, font=font) if c.type == 'gerber' and gerber == {}: c.max_len = 0 - col_spacing_width = get_text_width('o')*ops.column_spacing + col_spacing_width = get_text_width('o', font=font)*ops.column_spacing # Compute maximum width of each column according to stackup data for c in ops._columns: @@ -434,12 +434,18 @@ def meassure_table(ops, gerber, via_layer_pairs, stackup): def update_drawing_group(g, pos_x, pos_y, width, tlayer, ops, gerber, via_layer_pairs): - # Purge all content + + font = None + for item in g.GetItems(): - GS.board.Delete(item) + if not isinstance(item, pcbnew.PCB_TEXTBOX): + GS.board.Delete(item) + else: + font = item.GetFont() + # Analyze the stackup stackup = analyze_stackup(ops, gerber) - meassure_table(ops, gerber, via_layer_pairs, stackup) + measure_table(ops, gerber, via_layer_pairs, stackup, font) # Draw the stackup total_char_w = sum(c.width_char for c in ops._columns) + ops.column_spacing @@ -480,17 +486,17 @@ def update_drawing_group(g, pos_x, pos_y, width, tlayer, ops, gerber, via_layer_ for c in ops._columns: bold = (layer.material == "Copper") if c.type == 'material': - draw_text(g, c.x, y, layer.material, font_w, font_w, tlayer, bold) + draw_text(g, c.x, y, layer.material, font_w, font_w, tlayer, bold, font=font) elif c.type == 'layer': - draw_text(g, c.x, y, layer.layer, font_w, font_w, tlayer, bold) + draw_text(g, c.x, y, layer.layer, font_w, font_w, tlayer, bold, font=font) elif c.type == 'thickness': - draw_text(g, c.x, y, layer.thickness, font_w, font_w, tlayer, bold) + draw_text(g, c.x, y, layer.thickness, font_w, font_w, tlayer, bold, font=font) elif c.type == 'dielectric': - draw_text(g, c.x, y, layer.dielectric, font_w, font_w, tlayer, bold) + draw_text(g, c.x, y, layer.dielectric, font_w, font_w, tlayer, bold, font=font) elif c.type == 'layer_type': - draw_text(g, c.x, y, layer.layer_type, font_w, font_w, tlayer, bold) + draw_text(g, c.x, y, layer.layer_type, font_w, font_w, tlayer, bold, font=font) elif c.type == 'gerber': - draw_text(g, c.x, y, layer.gerber, font_w, font_w, tlayer, bold) + draw_text(g, c.x, y, layer.gerber, font_w, font_w, tlayer, bold, font=font) y += row_h if layer.material == "Core": y += core_extra_padding @@ -508,16 +514,16 @@ def update_drawing_group(g, pos_x, pos_y, width, tlayer, ops, gerber, via_layer_ # Draw text titles for c in ops._columns: - draw_text(g, c.x, int(pos_y + font_w/2), c.title, font_w, font_w, tlayer) + draw_text(g, c.x, int(pos_y + font_w/2), c.title, font_w, font_w, tlayer, font=font) # Draw thickness ds = GS.board.GetDesignSettings() draw_text(g, table_x, int(pos_y + table_h + font_w/2), f"Total thickness: {GS.to_mm(ds.GetBoardThickness())}mm", - font_w, font_w, tlayer) + font_w, font_w, tlayer, font=font) # Draw note if ops.note != '': - draw_text(g, table_x, int(pos_y + table_h + font_w/2 + row_h), "Note: " + ops.note, font_w, font_w, tlayer) + draw_text(g, table_x, int(pos_y + table_h + font_w/2 + row_h), "Note: " + ops.note, font_w, font_w, tlayer, font=font) if not ops.draw_stackup: return True diff --git a/kibot/pre_include_table.py b/kibot/pre_include_table.py index a1c76f3f..5e1df02f 100644 --- a/kibot/pre_include_table.py +++ b/kibot/pre_include_table.py @@ -8,6 +8,7 @@ import os import csv import re +import pcbnew from .error import KiPlotConfigurationError from .gs import GS from .kicad.pcb_draw_helpers import (draw_rect, draw_line, draw_text, get_text_width, @@ -117,8 +118,13 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice if not os.path.isfile(csv_file): raise KiPlotConfigurationError(f'Missing `{csv_file}`, create it first using the `{out.name}` output') + font = None + for item in g.GetItems(): - GS.board.Delete(item) + if not isinstance(item, pcbnew.PCB_TEXTBOX): + GS.board.Delete(item) + else: + font = item.GetFont() cols = [] @@ -151,7 +157,7 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice if out.invert_columns_order: cols.reverse() - measure_table(cols, out) + measure_table(cols, out, font=font) total_char_w = sum(c.width_char for c in cols) total_rel_w = sum((c.width for c in cols)) @@ -179,8 +185,8 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice y += int(row_h) draw_line(g, pos_x, y, pos_x + width, y, tlayer, line_w=GS.from_mm(out.header_rule_width)) for c in cols: - draw_text(g, c.x + c.xoffset, int(pos_y + 0.5 * row_h - font_w), c.header, font_w, font_w, - tlayer, bold=out.bold_headers, alignment=out._text_alignment) + txt, _ = draw_text(g, c.x + c.xoffset, int(pos_y + 0.5 * row_h - font_w), c.header, font_w, font_w, + tlayer, bold=out.bold_headers, alignment=out._text_alignment, font=font) for i in range(max_row_data - 1): rule_y = int(y + (i + 1) * row_h) @@ -190,7 +196,8 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice for c in cols: row_y = int(y + row_h / 2) for d in c.data: - draw_text(g, c.x + c.xoffset, int(row_y - font_w), d, font_w, font_w, tlayer, alignment=out._text_alignment) + txt, _ = draw_text(g, c.x + c.xoffset, int(row_y - font_w), d, font_w, font_w, + tlayer, alignment=out._text_alignment, font=font) row_y += row_h table_h = int(max(table_h, row_y - pos_y) - row_h / 2) @@ -205,13 +212,13 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice draw_rect(g, pos_x, pos_y, width, table_h, tlayer, line_w=GS.from_mm(out.border_width)) -def measure_table(cols, out): - col_spacing_width = get_text_width('o')*out.column_spacing +def measure_table(cols, out, font=None): + col_spacing_width = get_text_width('o', font=font)*out.column_spacing for c in cols: - max_data_len = max(get_text_width(d) for d in c.data) if c.data else 0 + max_data_len = max(get_text_width(d, font=font) for d in c.data) if c.data else 0 max_data_width_char = max(len(d) for d in c.data) if c.data else 0 - c.max_len = max(get_text_width(c.header), max_data_len) + col_spacing_width + c.max_len = max(get_text_width(c.header, font=font), max_data_len) + col_spacing_width c.width_char = max(len(c.header), max_data_width_char) + out.column_spacing tot_len = sum(c.max_len for c in cols) @@ -306,6 +313,7 @@ def update_table(ops, parent): f' with name {g.GetName()}') update_table_group(g, x1, y1, x2 - x1, layer, ops, out, csv[index], slice_str) + updated = True if not group_found: diff --git a/tests/reference/8_0_0/include_table_1.svg b/tests/reference/8_0_0/include_table_1.svg index 59ade9ad..f2980679 100644 --- a/tests/reference/8_0_0/include_table_1.svg +++ b/tests/reference/8_0_0/include_table_1.svg @@ -7,7 +7,7 @@ xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="297.0022mm" height="210.0072mm" viewBox="0.0000 0.0000 297.0022 210.0072"> -SVG Image created as test_points-Dwgs_User.svg date 2024/10/28 12:04:07 +SVG Image created as test_points-Dwgs_User.svg date 2024/12/22 16:33:44 Image generated by PCBNEW Footprint Footprint - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Value Value - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Type Type - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Side Side - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Y-Loc Y-Loc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - X-Loc X-Loc - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designator Designator - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Center X Center X - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Value Value - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ref Ref - - - - - - - - - - - - - - - - - - - - - - - - - - - - Center X Center X - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Value Value - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ref Ref - - - - - - - - - - - - - - - - - - - - - - - - - - - -