TechDraw TemplateGenerator/pl: Difference between revisions

From FreeCAD Documentation
(Created page with "{{TutorialInfo/pl |Topic=Template generation with Python macro |Topic/pl=Generator szablonów - makrodefinicja Python |Level=Podstawowa znajomość środowiska Python i strukt...")
(Created page with "Kolorowanie edytowalnych tekstów na niebiesko jest tylko osobistym wyborem, aby łatwiej odróżnić statyczne i edytowalne teksty na gotowym rysunku.")
 
(106 intermediate revisions by 3 users not shown)
Line 2: Line 2:


{{TutorialInfo/pl
{{TutorialInfo/pl
|Topic=Tworzenie szablonu Rysunku Technicznego - makrodefinicja Python
|Topic=Template generation with Python macro
|Topic/pl=Generator szablonów - makrodefinicja Python
|Level=Podstawowa znajomość środowiska Python i struktur svg jest przydatna
|Level=Podstawowa znajomość środowiska Python i struktur svg jest przydatna
|FCVersion=0.19.1 i nowszy
|FCVersion=0.21 i nowszy
|Time=''(Jeszcze nie wiem)''
|Time=''(Jeszcze nie wiem)''
|Author=[[User:FBXL5|FBXL5]]
|Author=[[User:FBXL5|FBXL5]]
|SeeAlso=[[Macro_TemplateHelper/pl|Makro TemplateHelper]]
}}
}}


<span id="Introduction"></span>
==Wprowadzenie==
==Wprowadzenie==


Ten poradnik opisuje jak z kilku linijek kodu Pythona wygenerować prosty szablon do wykorzystania w środowisku pracy Rysunek Techniczny.
This tutorial describes how to generate a simple template to use with the TechDraw workbench out of some lines of Python code.


Do kodowania można użyć dowolnego edytora tekstu. Mój wybór to Atom, ale wbudowany edytor FreeCAD też działa dobrze.
Any text editor can be used to code. My choice is Atom, but FreeCAD's built-in editor works well, too.


Poniższe przykłady kodu można skopiować i wkleić do pustego pliku tekstowego, a następnie zapisać pod wybraną nazwą jako plik typu {{FileName|*.py}} lub {{FileName|*.FCMacro}}.
The following code examples can be copied and pasted into an empty text file and then saved under a name of your choice as a *.py or *.FCMacro file.


Szablon stanowi tło dla zadań rysunkowych, a jego wymiary są wykorzystywane przez sterowniki drukarki do prawidłowego skalowania rysunku.
A template provides a background for the drawing tasks and its dimensions are used by the printer drivers to scale the drawing correctly.


Szablony są plikami svg, więc makrodefinicja musi skomponować kilka linii kodu svg ''(który jest podzbiorem kodu xml)''.
The templates are svg-files and so a macro has to compose some lines of svg code (which is a subset of xml code).


'''Uwaga:''' Kiedy FreeCAD został przeniesiony z '''freecadweb.org''' do '''freecad.org''', ta strona została odpowiednio zaktualizowana, a wynikowy kod SVG nie jest już kompatybilny z wersjami FreeCAD starszymi niż v0.21. W przypadku tych wersji należy ręcznie zmienić {{Incode|freecad.org}} na {{Incode|freecadweb.org}} w wierszu deklaracji przestrzeni nazw w wynikowym kodzie SVG, w przeciwnym razie edytowalne teksty nie zostaną rozpoznane.
== Structure of a simple blank page ==


<span id="Structure_of_a_simple_blank_page"></span>
The SVG format is a subset of the XML format.
==Struktura zwykłej pustej strony==
That is why an SVG file, like any XML file, consists of two parts:
* Head, a format declaration
* Body, containing the information what to show and where to place it
: (I don't know why there should be a headline, the svg-file is still a valid template file without it...)


Format SVG jest podzbiorem formatu XML.
=== Head ===
Dlatego plik SVG, jak każdy plik XML, składa się z dwóch części:
* Nagłówka, czyli deklaracji formatu.
* Zawartości, zawierającej informacje co pokazać i gdzie to umieścić
: ''(nie wiem po co ma być nagłówek, plik svg jest nadal poprawnym plikiem szablonu bez niego ...)''


<span id="Head"></span>
The head is just one line to declare which version of the XML language an interpreter should use to handle the instructions in the body.
===Nagłówek===

Nagłówek to tylko jedna linia deklarująca, której wersji języka XML powinien użyć interpreter do obsługi instrukcji w treści.


{{Code|lang=xml|code=
{{Code|lang=xml|code=
Line 38: Line 43:
}}
}}


<span id="Body"></span>
=== Body ===
===Zawartość===


Struktura zaczyna się od znacznika otwierającego, który zawiera informacje o przestrzeniach nazw oraz o wielkości szablonu i miejscu jego umieszczenia. A kończy się znacznikiem zamykającym.
The Body starts with an opening tag which contains information about name spaces and about the size of the template and where to place it. And it finishes with a closing tag.


{{Code|lang=xml|code=
{{Code|lang=xml|code=
<svg
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace"
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
width="420mm"
width="420mm"
height="297mm"
height="297mm"
Line 52: Line 58:
}}
}}


: '''xmlns='''"http://www.w3.org/2000/svg": External link to the xml name space to look up standard xml commands
: '''xmlns='''http://www.w3.org/2000/svg''': Zewnętrzny link do przestrzeni nazw xml, aby wyszukać standardowe polecenia xml.
: '''version='''"1.1": Used xml version is 1.1
: '''version='''"1.1": Używana wersja xml to 1.1
: '''xmlns:freecad='''...=Svg_Namespace": Zewnętrzny link do strony Wiki FreeCAD [[Svg_Namespace/pl|Svg Namespace]]. Link nie jest używany do pobierania informacji lub wartości w czasie wykonywania, ale jest kluczem do aktywacji niestandardowych atrybutów, np. tych dla edytowalnych tekstów.
: '''xmlns:freecad='''"[https://wiki.freecadweb.org/index.php?title=Svg_Namespace Svg Namespace]": External link to FreeCAD's name space extension
: "freecad:" zostaną poprzedzone nazwami atrybutów niestandardowych.
:: to look up special commands that are only used inside a FreeCAD environment e.g. for editable texts
: '''width='''"420mm": Width of the drawing area
: '''width=''' "420mm": Szerokość obszaru rysowania.
: '''height='''"297mm": Height of the drawing area
: '''height=''' "297mm": Wysokość obszaru rysowania.
: '''viewBox='''"0 0 420 297": Position of the upper left corner (0;0) and the lower right corner (420;297) in the svg construction space (in svg units).
: '''viewBox=''' "0 0 420 297": Położenie lewego górnego rogu ''(0;0)'' i prawego dolnego rogu ''(420;297)'' w przestrzeni konstrukcyjnej svg ''(w jednostkach svg)''.
: Width, height, and viewBox in this combination set 1 svg-unit to 1 mm for the whole document. A dimensional unit can be omitted from now on.
: Szerokość, wysokość i viewBox w tej kombinacji ustawiają 1 jednostkę svg na 1 mm dla całego dokumentu. Jednostka wymiarowa może być od tej pory pomijana.
: In this case 420 and 297 give an A3 page. Customise these values to generate other page sizes
: W tym przypadku 420 i 297 dają stronę A3. Dostosuj te wartości, aby wygenerować inne rozmiary stron.


For a blank page size DIN A3 in landscape orientation that's all.
Dla pustej strony rozmiar DIN A3 w orientacji poziomej to wszystko.


{{Code|lang=xml|code=
{{Code|lang=xml|code=
Line 68: Line 74:
<svg
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace"
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
width="420mm"
width="420mm"
height="297mm"
height="297mm"
Line 75: Line 81:
}}
}}


<span id="Python_code..."></span>
== Python code... ==
==Kod Python ...==


Kodowanie rozpoczyna się od frameworka zawierającego oddzielną funkcję główną:
Before any code is generated a folder is needed to store the new template file.


{{Code| |code=
The user should have selected a template folder. Its path is then stored in the TechDraw preferences. </br>
#! python
It is not necessary to know where the preferences are store, because FreeCAD has commands to address needed parameters directly.
# -*- coding: utf-8 -*-
# (c) 2024 Your name LGPL


def main():
"""Here is where the magic happens"""
return

if __name__ == '__main__':
# This will be true only if the file is "executed"
# but not if imported as module
main()
}}

<span id="..._to_create_a_blank_page"></span>
=== ... aby stworzyć pustą stronę ===

Proces tworzenia szablonu składa się z następujących etapów
* Zlokalizowanie folderu szablonów.
* Otwarcie pliku do zapisu w celu utworzenia pliku svg od podstaw, napisanie linii nagłówka i zamknięcie pliku w pierwszym kroku.
* A następnie wielokrotne otwieranie pliku w celu dodania kolejnych segmentów, zamykając go ponownie za każdym razem.

Makrodefinicja składa się z kilku funkcji, które są wywoływane z sekcji głównej.<br>
Dodatkowe funkcje można wstawić przed funkcją EndSvg, a potrzebne wywołania przed wywołaniem EndSvg().<br>
Aby wcięcie było prawidłowe, musimy zwracać uwagę na liczbę spacji używanych podczas operacji zapisu.

<span id="pathToTemplate()"></span>
====pathToTemplate()====

Przed wygenerowaniem jakiegokolwiek kodu potrzebny jest folder do przechowywania nowego pliku szablonu i należy ustawić nazwę pliku.

Użytkownik powinien wybrać folder szablonu. Jego ścieżka jest następnie zapisywana w preferencjach środowiska pracy Rysunek Techniczny. </br>
Nie jest konieczne, aby wiedzieć, gdzie przechowywane są preferencje, ponieważ FreeCAD posiada polecenia pozwalające na bezpośrednie adresowanie potrzebnych parametrów.


{{Code| |code=
{{Code| |code=
def pathToTemplate(template_name):
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
"""Link a given template name to the path of the template folder"""
template_path = parameter_path.GetString("TemplateDir")
#- Get the path to the template folder that is set in the FreeCAD parameters
template_name = "MyTemplate.svg"
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_file = os.path.join(template_path, template_name)
template_path = parameter_path.GetString("TemplateDir")
#- Link template_path and template_name for any OS
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral
return path_to_file
}}
}}


'''parameter_path''' receives the path to the "folder" within the configuration file where "TemplateDir" parameter can be found. </br>
'''parameter_path''' przyjmuje ścieżkę do "folderu" w ramach pliku konfiguracyjnego, w którym znajduje się parametr "TemplateDir". </br>
'''template_path''' receives the content of "TemplateDir" which is the path to the template directory. </br>
'''template_path''' przyjmuje zawartość "TemplateDir", która jest ścieżką do katalogu z szablonem. <br>
'''template_name''' recieves the name of the template to be created.
'''template_name''' przyjmuje nazwę szablonu, który ma zostać utworzony.


Teraz nazwa szablonu musi być połączona ze ścieżką szablonu w sposób, który jest kompatybilny z systemami operacyjnymi opartymi na Uniksie i Windows.</br>
Now the template name needs to be linked to the template path in a way that is compatible to unix based OSs and Windows.</br>
Robi się to za pomocą polecenia "os.path.join" i przechowuje w '''pliku szablonu'''. Aby włączyć to polecenie, wymagana jest instrukcja "import os".
This is done with the "os.path.join" command and stored into the '''template_file'''.


<span id="createSvgFile()"></span>
=== ... to create a blank page ===
====createSvgFile()====

Spowoduje to utworzenie nowego pliku szablonu i zapisanie nagłówka xml.

{{Code| |code=
def createSvgFile(file_path):
# Create a file and insert a header line (with t as the space saving variant of template)
t = open(file_path, "w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
}}

<span id="startSvg()"></span>
====startSvg()====

Powoduje to dołączenie pliku szablonu i utworzenie znacznika otwierającego svg wraz z jego atrybutami.<br>
Każda instrukcja zapisu zawiera jedną linię kodu svg zakończoną "\n", znacznikiem CR/LF.

{{Code| |code=
def startSvg(file_path, sheet_width, sheet_height):
# Create svg-tag including namespace and format definitions
t = open(file_path, "a", encoding="utf-8")
# a = append, new lines are added at the end of an existing file
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
t.write("\n" + "\n")
t.write("<svg\n")
#- Namespace declarations
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
#- Format definition
t.write(" width =\"" + sheet_width + "mm\"\n")
t.write(" height=\"" + sheet_height + "mm\"\n")
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
# identical values for width and height and Viewbox' width and height
# will synchronise mm and svg-units
t.close
}}

<span id="endSvg()"></span>
====endSvg()====

To dalej dołącza plik szablonu i tworzy znacznik zamykający svg; To ostatecznie kończy kod szablonu.

{{Code| |code=
def endSvg(file_path):
# Create closing svg-tag
t = open(file_path, "a", encoding="utf-8")
t.write("</svg>")
t.close
}}

<span id="main()"></span>
====main()====

Funkcja main() wywołuje funkcje i przekazuje pewne parametry.

{{Code| |code=
def main():
"""This one creates an empty A3 template"""
#- Set the name of the template file and get its location
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here
#- Here starts the compiling of the svg file
createSvgFile(template_file) # overwrites existing File
#- Set sheet format (DIN A3)
format_width = "420"
format_height = "297"
startSvg(template_file, format_width, format_height) # adds svg start tag
endSvg(template_file) # adds svg end tag
# At this point a new SVG-file is generated and saved
return
}}

W tym przykładzie {{Incode|format_width}} i {{Incode|format_height}} są twardo zakodowanymi wymiarami, obie niepotrzebne linie oznaczają punkty, w których inne sposoby pobierania danych formatu mogłyby umieścić swoją zawartość.


<div class="mw-collapsible mw-collapsed toccolours">
<div class="mw-collapsible mw-collapsed toccolours">
====Kompletna makrodefinicja pustej strony====
This macro shows the principle how an svg-file can be put together. (Format is A3)

To makro składa się z powyższych segmentów kodu, gotowych do uruchomienia.
<div class="mw-collapsible-content">
<div class="mw-collapsible-content">


Line 105: Line 224:
#! python
#! python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
# (c) 2024 Your name LGPL
#
#
#- Get the path to the template folder that is set in the FreeCAD parameters
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
template_name = "MyTemplate.svg"
#- Link template_path and template_name for any OS
template_file = os.path.join(template_path, template_name)


import os # to enable the use of os.path.join()
# - SVG creation -



#- Create a file and insert a header line
# - SVG creation -
# (with t as the space saving variant of template)
def CreateSvgFile(filePath):
def createSvgFile(file_path):
# Create a file and insert a header line (with t as the space saving variant of template)
t=open(filePath,"w") # w = write, overwrites existing files
t = open(file_path, "w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
t.close


def startSvg(file_path, sheet_width, sheet_height):
#- Create opening svg-tag
# Create svg-tag including namespace and format definitions
# Namespace section
t = open(file_path, "a", encoding="utf-8")
def StartSvg(filePath):
t=open(filePath,"a") # a = append, new lines are added at the end of an existing file
# a = append, new lines are added at the end of an existing file
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
t.write("\n"+"\n")
t.write("\n" + "\n")
t.write("<svg\n")
t.write("<svg\n")
#- Namespace declarations
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace\"\n")
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
#- Format definition
t.close
t.write(" width =\"" + sheet_width + "mm\"\n")
# Sheet size section
t.write(" height=\"" + sheet_height + "mm\"\n")
def CreateSheet(filePath,shWidth,shHeight):
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
t=open(filePath,"a")
# identical values for width and height and Viewbox' width and height
t.write(" width =\""+shWidth+"mm\"\n")
# will synchronise mm and svg-units
t.write(" height=\""+shHeight+"mm\"\n")
t.write(" viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n")
t.close
t.close
# identical values for width and height and Viewbox' width and height will synchronise mm and svg-units


def endSvg(file_path):
#- Create closing svg-tag
# Create closing svg-tag
def EndSvg(filePath):
t=open(filePath,"a")
t = open(file_path, "a", encoding="utf-8")
t.write("</svg>")
t.write("</svg>")
t.close
t.close


def pathToTemplate(template_name):
# --- Main section ---
"""Link a given template name to the path of the template folder"""
#- Get the path to the template folder that is set in the FreeCAD parameters
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
#- Link template_path and template_name for any OS
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral
return path_to_file


def main():
CreateSvgFile(template_file) # overwrites existing File
"""This one creates an empty A3 template"""
#- Set the name of the template file and get its location
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here
#- Here starts the compiling of the svg file
createSvgFile(template_file) # overwrites existing File
#- Set sheet format (DIN A3)
format_width = "420"
format_height = "297"
startSvg(template_file, format_width, format_height) # adds svg start tag
endSvg(template_file) # adds svg end tag
# At this point a new SVG-file is generated and saved
return


if __name__ == '__main__':
# Set sheet format (DIN A3)
# This will be true only if the file is "executed"
formatWidth = "420"
# but not if imported as module
formatHeight = "297"
main()

StartSvg(template_file) # adds Start tag and namespaces
CreateSheet(template_file, formatWidth, formatHeight)
EndSvg(template_file)
# At this point a new SVG-file is generated and saved
}}
}}


</div> <!-- End of collapsible element for ... section. Do not remove! -->
</div> <!-- Koniec elementu zwijanego dla sekcji treści. Nie usuwaj! -->
</div> <!-- End of collapsible element for ... section. Do not remove! -->
</div> <!-- Koniec elementu zwijanego dla ... sekcji. Nie usuwaj! -->


: The main principle is:
:* to open a file for writing and so start an svg-file from scratch, write the header line and close the file as a first step.
:* and then to repeatedly open the file to append further segments and then close it again after appending.
:
:The macro is made of several functions which are called from the main section.
:Additional functions could be inserted before the EndSvg function and the needed calls are put before the EndSvg() call.
:We need to have an eye on the number of spaces used with the write operations for a correct indentation.


<span id="..._to_create_a_page_with_some_ink"></span>
=== ... to create a page with some ink ===
=== ... aby stworzyć stronę w której jest trochę tuszu ===


Aby stworzyć rysunek z pustej kartki potrzebujemy:
To make a drawing out of a blank page we need:
:* Frames i.e. rectangles created with the '''rect''' instruction
* ramki czyli prostokąty utworzone za pomocą instrukcji '''rect''',
bloku tytułowego i więcej linii utworzonych za pomocą instrukcji '''path''',
:* a title block and more made of lines created with the '''path''' instruction
* proste teksty do indeksów i etykietowania komórek bloku tytułowego,
:* simple texts for indexes and labeling title block cells
* teksty edytowalne, takie jak numer lub nazwa części,
:* editable texts like part number or part name


Zwykle te elementy graficzne są używane wielokrotnie, więc kod generujący jest umieszczony w czterech funkcjach:
Normally these graphical elements are used repeatedly and so the generating code is put into functions.
* svgRect() dla prostokątnych elementów ramki.
* svgPath() dla elementów prostych.
* svgText() dla tekstów statycznych.
* ediText() dla tekstów edytowalnych.


Wszystkie funkcje są umieszczane wewnątrz powyższej makrodefinicji przed funkcją main(), a powiązane z nimi wywołania funkcji są wstawiane do funkcji main() między {{Incode|startSvg(...)}} i {{Incode|endSvg(...)}}.
=== svgrect function ===


<span id="svgRect()"></span>
To draw a rectangle we just need to call the '''svgrect''' function and hand over the values for width, height, and position of the upper left corner. That is better readable than the content of the svgline which had to be used instead.
====funkcja svgRect()====

Aby narysować prostokąt musimy tylko wywołać funkcję '''svgRect''' i przekazać wartości szerokości, wysokości i położenia lewego górnego rogu. Jest to lepiej czytelne w tym miejscu, niż gdyby svg_line zawierał całą linię kodu svg.


{{Code| |code=
{{Code| |code=
def svgRect(width, height, x, y):
#- Function to generate an svg-instruction to draw a rectangle with the given values
# Generates an svg-instruction to draw a rectangle with the given values
def svgrect(width,height,x,y):
svg_line = (
svgLine=("<rect width=\""+width+"\" height=\""+height+"\" x=\""+x+"\" y=\""+y+"\" />")
"<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x
return svgLine
+ "\" y=\"" + y + "\" />"
)
return svg_line
}}
}}


Instrukcja svg jest podzielona, aby pozostać w zalecanej długości linii Pythona, mimo to spowoduje to utworzenie pojedynczej linii svg.
=== svgpath function ===

<span id="svgPath()"></span>
====funkcja svgPath()====


Aby narysować linię wystarczy wywołać funkcję '''svgPath''' i przekazać współrzędne punktu początkowego i końcowego linii.
To draw a line we just need to call the '''svgpath''' function and hand over the coordinates of the start point and the endpoint of a line.


Zamiast współrzędnych punktu końcowego możemy przekazać znacznik do rysowania linii poziomej ''(h)'' lub pionowej ''(v)'', a następnie długość linii lub znacznik do rysowania linii poziomej ''(H)'' lub pionowej ''(V)'', a następnie rzędną x lub y punktu końcowego.
Instead of the end point coordinates we can hand over a tag to draw a horizontal (h) or vertical (v) line followed by the length of the line or the tag to draw a horizontal (H) or vertical (V) line followed by the x or y ordinate of the end point.


Każda ścieżka zaczyna się w punkcie początkowym i pierwszą czynnością jest ruch z "podniesionym piórem" ''(bez rysowania)'' do punktu początkowego. W tym przypadku ruch względny i ruch bezwzględny są takie same i dlatego nie ma znaczenia, czy znacznik m jest pisany wielką czy małą literą.
Each path starts at the origin and the first action is a movement with "raised pen" (not drawing) to the start point. In this case the relative movement and the absolute movement are the same and so it is irrelevant whether the m-tag is upper or lower case.


{{Code| |code=
{{Code| |code=
def svgPath(x1, y1, x2, y2):
#- Function to generate an svg-instruction to draw a path element (line) with the given values
# Generates an svg-instruction to draw a path element (line) with the given values
def svgpath(x1,y1,x2,y2):
if x2=="v" or x2=="V" or x2=="h" or x2=="H":
if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H":
svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
else:
else:
svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
return svgLine
return svg_line
}}
}}


<span id="svgText()"></span>
=== svgtext function ===
====funkcja svgText()====


Aby narysować fragment tekstu wystarczy wywołać funkcję '''svgText''' i przekazać współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu.
To draw a piece of text we just need to call the '''svgtext''' function and hand over the coordinates of the text's anchor point and the text string itself.

''(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej)''.


(The text alignment is controlled by the surrounding group tag or left-aligned as default).
{{Code| |code=
{{Code| |code=
def svgText(x, y, str_value, str_angle="0"):
#- Function to generate an svg-instruction to place a text element with the given values
"""
def svgtext(posX,posY,strValue):
Generates an svg-instruction to place a text element with the given values.
svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>")
Optional str_angle enables vertical and arbitrarily rotated texts
return svgLine
"""
if str_angle == "0":
svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>")
else:
svg_line = (
"<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle
+ "," + x + "," + y + ")\">" + str_value + "</text>"
)
return svg_line
}}
}}


<span id="ediText()"></span>
=== FCeditext function ===
====funkcja ediText()====


Aby narysować fragment edytowalnego tekstu wystarczy wywołać funkcję '''ediText''' i przekazać jej nazwę, współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu.
To draw a piece of editable text we just need to call the '''FCeditext''' function and hand over a name, the coordinates of the text's anchor point, and the text string itself.


FreeCAD tworzy obiekt słownika z każdym wstawionym szablonem, a każdy wpis ma nazwę ''(klucz)'' i wartość.
FreeCAD creates a dictionary object with every inserted template, and each entry has a name (key) and a value.


''(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej)''.
(The text alignment is controlled by the surrounding group tag or left-aligned as default).


{{Code| |code=
{{Code| |code=
def ediText(entry_name, x, y, str_value, str_angle="0"):
#- Function to generate an svg-instruction to place an editable text element with the given values
"""
def FCeditext(entryName,posX,posY,strValue):
Generates an svg-instruction to place an editable text element with the given values.
svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \
Optional str_angle enables vertical and arbitrarily rotated editable texts
+"\"> <tspan>"+strValue+"</tspan> </text>")
return svgLine
"""
if str_angle == "0":
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\"> <tspan>" + str_value + "</tspan> </text>"
)
else:
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\"> <tspan>"
+ str_value + "</tspan> </text>"
)
return svg_line
}}
}}


<span id="createXxxx_functions"></span>
=== CreateXxxx functions ===
===Funkcja createXxxx===


Funkcje te rozpoczynają się od kodu otwierającego plik w trybie dołączania i zapisującego znacznik otwierający grupę.
These functions start with code to open a file in append mode and to write the group opening tag.


Następnie widzimy sekcję do ustawiania i obliczania wartości oraz z instrukcjami zapisu, które wywołują powyższe funkcje w celu wygenerowania kodu svg.
Then follows a section to set and calculate values and with write instructions that call the above functions to generate svg-code.


I wreszcie znacznik zamykający grupę, po którym następuje instrukcja zamknięcia pliku.
And finally the group closing tag followed by an instruction to close the file.


{{Code| |code=
{{Code| |code=
def createFrame(file_path, sheet_width, sheet_height):
#- Frame creation
# Creates rectangles for sheet frame and drawing area
def CreateFrame(filePath,shWidth,shHeight):
t=open(filePath,"a")
t = open(file_path, "a", encoding="utf-8")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;\
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n")
#- upper left corner of inner Frame, drawing area
stroke-linecap:round\">\n")
frame_x = str(20)
# inner Frame, drawing area
#- upper left corner
frame_y = str(10)
drawingX=str(20)
drawingY=str(10)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20-10)
frame_width = str(int(sheet_width) - 20 - 10)
drawingHeight=str(int(shHeight)-10-10)
frame_height = str(int(sheet_height) - 10 - 10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
# outer frame
#- upper left corner outer frame, sheet frame
#- upper left corner
frame_x = str(15)
drawingX=str(15)
frame_y = str(5)
drawingY=str(5)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20)
frame_width = str(int(sheet_width)-20)
drawingHeight=str(int(shHeight)-10)
frame_height = str(int(sheet_height)-10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
t.write(" </g>\n\n")
t.write(" </g>\n\n")
t.close
t.close
}}
}}


<span id="Resulting_macro"></span>
=== Resulting macro ===
===Rezultat===


<div class="mw-collapsible mw-collapsed toccolours">
<div class="mw-collapsible mw-collapsed toccolours">
Makro to dodaje kilka podstawowych elementów graficznych potrzebnych do prawidłowego działania szablonów tj. elementy linii, etykiety oraz teksty do edycji.
This macro adds some basic graphical elements needed for proper templates i.e. line elements, texts, and editable texts.
<div class="mw-collapsible-content">
<div class="mw-collapsible-content">


Line 287: Line 443:
#! python
#! python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
# (c) 2024 Your name LGPL
#
#
#- Get the path to the template folder that is set in the FreeCAD parameters
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
template_name = "MyTemplate.svg"
#- Link template_path and template_name for any OS
template_file = os.path.join(template_path, template_name)


import os # to enable the use of os.path.join()
# - SVG creation -



#- Create a file and insert a header line
# - SVG creation -
# (with t as the space saving variant of template)
def CreateSvgFile(filePath):
def createSvgFile(file_path):
# Create a file and insert a header line (with t as the space saving variant of template)
t=open(filePath,"w") # w = write, overwrites existing files
t = open(file_path, "w") # w = write, overwrites existing files
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
t.close
t.close


def startSvg(file_path, sheet_width, sheet_height):
#- Create opening svg-tag
# Create svg-tag including namespace and format definitions
# Namespace section
t = open(file_path, "a", encoding="utf-8")
def StartSvg(filePath):
t=open(filePath,"a") # a = append, new lines are added at the end of an existing file
# a = append, new lines are added at the end of an existing file
# encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
t.write("\n"+"\n")
t.write("\n" + "\n")
t.write("<svg\n")
t.write("<svg\n")
#- Namespace declarations
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
t.write(" xmlns:freecad=\"http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace\"\n")
t.write(" xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
#- Format definition
t.close
t.write(" width =\"" + sheet_width + "mm\"\n")
# Sheet size section
t.write(" height=\"" + sheet_height + "mm\"\n")
def CreateSheet(filePath,shWidth,shHeight):
t.write(" viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
t=open(filePath,"a")
# identical values for width and height and Viewbox' width and height
t.write(" width =\""+shWidth+"mm\"\n")
# will synchronise mm and svg-units
t.write(" height=\""+shHeight+"mm\"\n")
t.write(" viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n")
t.close
t.close
# identical values for width and height and Viewbox' width and height will synchronise mm and svg-units


def endSvg(file_path):
#- Create closing svg-tag
# Create closing svg-tag
def EndSvg(filePath):
t=open(filePath,"a")
t = open(file_path, "a", encoding="utf-8")
t.write("</svg>")
t.write("</svg>")
t.close
t.close


def svgRect(width, height, x, y):
#- Function to generate an svg-instruction to draw a rectangle with the given values
# Ggenerates an svg-instruction to draw a rectangle with the given values
def svgrect(width,height,x,y):
svg_line = (
svgLine=("<rect width=\""+width+"\" height=\""+height+"\" x=\""+x+"\" y=\""+y+"\" />")
"<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x
return svgLine
+ "\" y=\"" + y + "\" />"
)
return svg_line


def svgPath(x1, y1, x2, y2):
#- Function to generate an svg-instruction to draw a path element (line) with the given values
# Generates an svg-instruction to draw a path element (line) with the given values
def svgpath(x1,y1,x2,y2):
if x2=="v" or x2=="V" or x2=="h" or x2=="H":
if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H":
svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
else:
else:
svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
return svgLine
return svg_line


def svgText(x, y, str_value, str_angle="0"):
#- Function to generate an svg-instruction to place a text element with the given values
"""
def svgtext(posX,posY,strValue):
Generates an svg-instruction to place a text element with the given values.
svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>")
Optional str_angle enables vertical and arbitrarily rotated texts
return svgLine
"""
if str_angle == "0":
svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>")
else:
svg_line = (
"<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle
+ "," + x + "," + y + ")\">" + str_value + "</text>"
)
return svg_line


def ediText(entry_name, x, y, str_value, str_angle="0"):
#- Function to generate an svg-instruction to place an editable text element with the given values
"""
def FCeditext(entryName,posX,posY,strValue):
Generates an svg-instruction to place an editable text element with the given values.
svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \
Optional str_angle enables vertical and arbitrarily rotated editable texts
+"\"> <tspan>"+strValue+"</tspan> </text>")
return svgLine
"""
if str_angle == "0":
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\"> <tspan>" + str_value + "</tspan> </text>"
)
else:
svg_line = (
"<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
+ "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\"> <tspan>"
+ str_value + "</tspan> </text>"
)
return svg_line


def createFrame(file_path, sheet_width, sheet_height):
#- Frame creation
# Creates rectangles for sheet frame and drawing area
def CreateFrame(filePath,shWidth,shHeight):
t=open(filePath,"a")
t = open(file_path, "a", encoding="utf-8")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" <g id=\"drawing-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;\
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n")
#- calculate upper left corner of inner Frame, drawing area
stroke-linecap:round\">\n")
frame_x = str(20)
# inner Frame, drawing area
#- upper left corner
frame_y = str(10)
drawingX=str(20)
drawingY=str(10)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20-10)
frame_width = str(int(sheet_width) - 20 - 10)
drawingHeight=str(int(shHeight)-10-10)
frame_height = str(int(sheet_height) - 10 - 10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
# outer frame
#- calculate upper left corner outer frame, sheet frame
#- upper left corner
frame_x = str(15)
drawingX=str(15)
frame_y = str(5)
drawingY=str(5)
#- frame dimensions
#- frame dimensions
drawingWidth=str(int(shWidth)-20)
frame_width = str(int(sheet_width)-20)
drawingHeight=str(int(shHeight)-10)
frame_height = str(int(sheet_height)-10)
#- frame rectangle
#- frame rectangle
t.write(" "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
t.write(" " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
t.write(" </g>\n\n")
t.write(" </g>\n\n")
t.close
t.close


def createTitleBlock(file_path, sheet_width, sheet_height):
#- Title block movable
"""Creates a title block and transfers it to the position according to the sheet dimensions"""
def CreateTitleBlock(filePath,shWidth,shHeight):
#- calculate title block origin (lower left corner), offset from page origin

tbX = str(int(sheet_width) - 10 - 180) # 180 according to DIN EN ISO 7200
#- lower left corner
tbX=str(int(shWidth)-10-180) # 180 according to DIN EN ISO 7200
tbY = str(int(sheet_height) - 10)
t = open(file_path, "a", encoding="utf-8")
tbY=str(int(shHeight)-10)
#- group to transform all elements at once
#- group to transfer all included title block elements at once
t=open(filePath,"a")
t.write(" <g id=\"titleblock\"\n")
t.write(" <g id=\"titleblock\"\n")
t.write(" transform=\"translate("+tbX+","+tbY+")\">\n")
t.write(" transform=\"translate(" + tbX + "," + tbY + ")\">\n")
#- title block
#- sub-group of title block line framework
t.write(" <g id=\"titleblock-frame\"\n")
t.write(" <g id=\"titleblock-frame\"\n")
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.35;\
t.write(" style=\"fill:none;stroke:#000000;stroke-width:0.35;\
stroke-linecap:miter;stroke-miterlimit:4\">\n")
stroke-linecap:miter;stroke-miterlimit:4\">\n")
t.write(" "+svgpath(" 0"," 0"," 0","-63")+"\n")
t.write(" " + svgPath(" 0"," 0"," 0","-63") + "\n")
t.write(" "+svgpath(" 0","-63","180"," 0")+"\n")
t.write(" " + svgPath(" 0","-63","180"," 0") + "\n")
t.write(" "+svgpath(" 0","-30","h","155")+"\n")
t.write(" " + svgPath(" 0","-30","h","155") + "\n")
t.write(" "+svgpath("155"," 0","v","-63")+"\n")
t.write(" " + svgPath("155"," 0","v","-63") + "\n")
t.write(" </g>\n")
t.write(" </g>\n")
#- small texts, left-aligned
#- sub-group of title block static texts (left-aligned by default)
t.write(" <g id=\"titleblock-text-non-editable\"\n")
t.write(" <g id=\"titleblock-text-non-editable\"\n")
t.write(" style=\"font-size:5.0;text-anchor:start;fill:#000000;\
t.write(" style=\"font-size:5.0;text-anchor:start;fill:#000000;\
font-family:osifont\">\n")
font-family:osifont\">\n")
t.write(" "+svgtext(" 4.5","-43.5 ","Some static text")+"\n")
t.write(" " + svgText(" 4.5","-43.5 ","Some static text") + "\n")
t.write(" "+svgtext(" 4.5","-13.5 ","More static text")+"\n")
t.write(" " + svgText(" 4.5","-13.5 ","More static text") + "\n")
t.write(" " + svgText("162.5","-3.5 ","Vertical static text","-90") + "\n")
t.write(" </g>\n")
t.write(" </g>\n")

t.write(" </g>\n\n")
t.write(" </g>\n\n")
t.close
t.close


def createEditableText(file_path, sheet_width, sheet_height):
#- Title block editable texts
"""Creates editable texts positioned according to the page origin"""
def CreateEditableText(filePath,shWidth,shHeight):
#- calculate offset to titleblock origin

edX = int(sheet_width) - 10 - 180 # 180 according to DIN EN ISO 7200
#- offsets for editable texts
edY = int(sheet_height) - 10
edX=int(shWidth)-10-180 # 180 according to DIN EN ISO 7200
t = open(file_path, "a", encoding="utf-8")
edY=int(shHeight)-10
#- group editable texts using the same attributes

t=open(filePath,"a")
t.write(" <g id=\"titleblock-editable-texts\"\n")
t.write(" <g id=\"titleblock-editable-texts\"\n")
t.write(" style=\"font-size:7.0;text-anchor:start;fill:#0000d0;\
t.write(" style=\"font-size:7.0;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
font-family:osifont\">\n")
t.write(
t.write(" "+FCeditext("EdiText-1",str(edX+60),str(edY-43.5),"Some editable text")+"\n")
t.write(" "+FCeditext("EdiText-2",str(edX+60),str(edY-13.5),"More editable text")+"\n")
" " + ediText("EdiText-1",str(edX + 60),str(edY - 43.5),"Some editable text") + "\n"
)
t.write(
" " + ediText("EdiText-2",str(edX + 60),str(edY - 13.5),"More editable text") + "\n"
)
t.write(
" " + ediText("EdiText-3",str(edX + 173),str(edY - 4.5),"90° editable text","-90")
+ "\n"
)
t.write(" </g>\n\n")
t.write(" </g>\n\n")
t.close
t.close


def pathToTemplate(template_name):
# --- Main section ---
"""Link a given template name to the path of the template folder"""

#- Get the path to the template folder that is set in the FreeCAD parameters
CreateSvgFile(template_file) # overwrites existing File
parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
template_path = parameter_path.GetString("TemplateDir")
#- Link template_path and template_name for any OS
path_to_file = os.path.join(template_path, template_name) # to join path segments OS neutral
return path_to_file


def main():
# Set sheet format (A3)
"""This one creates an A3 template with simple frame and title block"""
formatWidth = "420"
#- Set the name of the template file and get its location
formatHeight = "297"
template_file = pathToTemplate("MyTemplate.svg") # Change the template name here
#- Here starts the compiling of the svg file
createSvgFile(template_file) # overwrites existing File
#- Set sheet format (DIN A3)
format_width = "420"
format_height = "297"
startSvg(template_file, format_width, format_height) # adds svg start tag
createFrame(template_file, format_width, format_height)
createTitleBlock(template_file, format_width, format_height)
createEditableText(template_file, format_width, format_height)
endSvg(template_file) # adds svg end tag
# At this point a new SVG-file is generated and saved
return


if __name__ == '__main__':
StartSvg(template_file) # adds Start tag and namespaces
# This will be true only if the file is "executed"
CreateSheet(template_file, formatWidth, formatHeight)
# but not if imported as module
CreateFrame(template_file, formatWidth, formatHeight)
main()
CreateTitleBlock(template_file, formatWidth, formatHeight)
CreateEditableText(template_file, formatWidth, formatHeight)
EndSvg(template_file)
# At this point a new SVG-file is generated and saved
}}
}}


A to jest kod svg pochodzący z tej makrodefinicji:
And this is the svg-code coming out of this macro:


{{Code|lang=xml|code=
{{Code|lang=xml|code=
Line 451: Line 642:
<svg
<svg
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:freecad="http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace"
xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
width ="420mm"
width ="420mm"
height="297mm"
height="297mm"
Line 474: Line 665:
<text x=" 4.5" y="-43.5 ">Some static text</text>
<text x=" 4.5" y="-43.5 ">Some static text</text>
<text x=" 4.5" y="-13.5 ">More static text</text>
<text x=" 4.5" y="-13.5 ">More static text</text>
<text x="162.5" y="-3.5 " transform="rotate(-90,162.5,-3.5 )">Vertical static text</text>
</g>
</g>
</g>
</g>
Line 481: Line 673:
<text freecad:editable="EdiText-1" x="290" y="243.5"> <tspan>Some editable text</tspan> </text>
<text freecad:editable="EdiText-1" x="290" y="243.5"> <tspan>Some editable text</tspan> </text>
<text freecad:editable="EdiText-2" x="290" y="273.5"> <tspan>More editable text</tspan> </text>
<text freecad:editable="EdiText-2" x="290" y="273.5"> <tspan>More editable text</tspan> </text>
<text freecad:editable="EdiText-3" x="403" y="282.5" transform="rotate(-90,403,282.5)"> <tspan>90° editable text</tspan> </text>
</g>
</g>


Line 486: Line 679:
}}
}}


</div> <!-- End of collapsible element for ... section. Do not remove! -->
</div> <!-- Koniec elementu zwijanego dla sekcji treści. Nie usuwaj! -->
</div> <!-- End of collapsible element for ... section. Do not remove! -->
</div> <!-- Koniec elementu zwijanego dla ... sekcji. Nie usuwaj! -->

I jak to powinno wyglądać po wstawieniu ''(plus powiększony blok tytułowy)'':


[[Image:TechDraw TemplateGenerator.png|600px]]
And what it should look like when inserted (plus magnified title block):


Kolorowanie edytowalnych tekstów na niebiesko jest tylko osobistym wyborem, aby łatwiej odróżnić statyczne i edytowalne teksty na gotowym rysunku.
[[Image:TechDraw TemplateGenerator.png|TechDraw TemplateGenerator.png]]

Latest revision as of 18:57, 2 February 2024

Other languages:
Ćwiczenie
Temat
Tworzenie szablonu Rysunku Technicznego - makrodefinicja Python
Poziom trudności
Podstawowa znajomość środowiska Python i struktur svg jest przydatna
Czas wykonania
(Jeszcze nie wiem)
Autorzy
FBXL5
Wersja FreeCAD
0.21 i nowszy
Pliki z przykładami
Nie dołączono
Zobacz również
Makro TemplateHelper

Wprowadzenie

Ten poradnik opisuje jak z kilku linijek kodu Pythona wygenerować prosty szablon do wykorzystania w środowisku pracy Rysunek Techniczny.

Do kodowania można użyć dowolnego edytora tekstu. Mój wybór to Atom, ale wbudowany edytor FreeCAD też działa dobrze.

Poniższe przykłady kodu można skopiować i wkleić do pustego pliku tekstowego, a następnie zapisać pod wybraną nazwą jako plik typu *.py lub *.FCMacro.

Szablon stanowi tło dla zadań rysunkowych, a jego wymiary są wykorzystywane przez sterowniki drukarki do prawidłowego skalowania rysunku.

Szablony są plikami svg, więc makrodefinicja musi skomponować kilka linii kodu svg (który jest podzbiorem kodu xml).

Uwaga: Kiedy FreeCAD został przeniesiony z freecadweb.org do freecad.org, ta strona została odpowiednio zaktualizowana, a wynikowy kod SVG nie jest już kompatybilny z wersjami FreeCAD starszymi niż v0.21. W przypadku tych wersji należy ręcznie zmienić freecad.org na freecadweb.org w wierszu deklaracji przestrzeni nazw w wynikowym kodzie SVG, w przeciwnym razie edytowalne teksty nie zostaną rozpoznane.

Struktura zwykłej pustej strony

Format SVG jest podzbiorem formatu XML. Dlatego plik SVG, jak każdy plik XML, składa się z dwóch części:

  • Nagłówka, czyli deklaracji formatu.
  • Zawartości, zawierającej informacje co pokazać i gdzie to umieścić
(nie wiem po co ma być nagłówek, plik svg jest nadal poprawnym plikiem szablonu bez niego ...)

Nagłówek

Nagłówek to tylko jedna linia deklarująca, której wersji języka XML powinien użyć interpreter do obsługi instrukcji w treści.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

Zawartość

Struktura zaczyna się od znacznika otwierającego, który zawiera informacje o przestrzeniach nazw oraz o wielkości szablonu i miejscu jego umieszczenia. A kończy się znacznikiem zamykającym.

<svg
  xmlns="http://www.w3.org/2000/svg" version="1.1"
  xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
  width="420mm"
  height="297mm"
  viewBox="0 0 420 297">
</svg>
xmlns=http://www.w3.org/2000/svg: Zewnętrzny link do przestrzeni nazw xml, aby wyszukać standardowe polecenia xml.
version="1.1": Używana wersja xml to 1.1
xmlns:freecad=...=Svg_Namespace": Zewnętrzny link do strony Wiki FreeCAD Svg Namespace. Link nie jest używany do pobierania informacji lub wartości w czasie wykonywania, ale jest kluczem do aktywacji niestandardowych atrybutów, np. tych dla edytowalnych tekstów.
"freecad:" zostaną poprzedzone nazwami atrybutów niestandardowych.
width= "420mm": Szerokość obszaru rysowania.
height= "297mm": Wysokość obszaru rysowania.
viewBox= "0 0 420 297": Położenie lewego górnego rogu (0;0) i prawego dolnego rogu (420;297) w przestrzeni konstrukcyjnej svg (w jednostkach svg).
Szerokość, wysokość i viewBox w tej kombinacji ustawiają 1 jednostkę svg na 1 mm dla całego dokumentu. Jednostka wymiarowa może być od tej pory pomijana.
W tym przypadku 420 i 297 dają stronę A3. Dostosuj te wartości, aby wygenerować inne rozmiary stron.

Dla pustej strony rozmiar DIN A3 w orientacji poziomej to wszystko.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
  xmlns="http://www.w3.org/2000/svg" version="1.1"
  xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
  width="420mm"
  height="297mm"
  viewBox="0 0 420 297">
</svg>

Kod Python ...

Kodowanie rozpoczyna się od frameworka zawierającego oddzielną funkcję główną:

#! python
# -*- coding: utf-8 -*-
# (c) 2024 Your name LGPL


def main():
    """Here is where the magic happens"""
    return

if __name__ == '__main__':
    # This will be true only if the file is "executed"
    # but not if imported as module
    main()

... aby stworzyć pustą stronę

Proces tworzenia szablonu składa się z następujących etapów

  • Zlokalizowanie folderu szablonów.
  • Otwarcie pliku do zapisu w celu utworzenia pliku svg od podstaw, napisanie linii nagłówka i zamknięcie pliku w pierwszym kroku.
  • A następnie wielokrotne otwieranie pliku w celu dodania kolejnych segmentów, zamykając go ponownie za każdym razem.

Makrodefinicja składa się z kilku funkcji, które są wywoływane z sekcji głównej.
Dodatkowe funkcje można wstawić przed funkcją EndSvg, a potrzebne wywołania przed wywołaniem EndSvg().
Aby wcięcie było prawidłowe, musimy zwracać uwagę na liczbę spacji używanych podczas operacji zapisu.

pathToTemplate()

Przed wygenerowaniem jakiegokolwiek kodu potrzebny jest folder do przechowywania nowego pliku szablonu i należy ustawić nazwę pliku.

Użytkownik powinien wybrać folder szablonu. Jego ścieżka jest następnie zapisywana w preferencjach środowiska pracy Rysunek Techniczny.
Nie jest konieczne, aby wiedzieć, gdzie przechowywane są preferencje, ponieważ FreeCAD posiada polecenia pozwalające na bezpośrednie adresowanie potrzebnych parametrów.

def pathToTemplate(template_name):
    """Link a given template name to the path of the template folder"""
    #- Get the path to the template folder that is set in the FreeCAD parameters
    parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
    template_path = parameter_path.GetString("TemplateDir")
    #- Link template_path and template_name for any OS
    path_to_file = os.path.join(template_path, template_name)  # to join path segments OS neutral
    return path_to_file

parameter_path przyjmuje ścieżkę do "folderu" w ramach pliku konfiguracyjnego, w którym znajduje się parametr "TemplateDir".
template_path przyjmuje zawartość "TemplateDir", która jest ścieżką do katalogu z szablonem.
template_name przyjmuje nazwę szablonu, który ma zostać utworzony.

Teraz nazwa szablonu musi być połączona ze ścieżką szablonu w sposób, który jest kompatybilny z systemami operacyjnymi opartymi na Uniksie i Windows.
Robi się to za pomocą polecenia "os.path.join" i przechowuje w pliku szablonu. Aby włączyć to polecenie, wymagana jest instrukcja "import os".

createSvgFile()

Spowoduje to utworzenie nowego pliku szablonu i zapisanie nagłówka xml.

def createSvgFile(file_path):
    # Create a file and insert a header line (with t as the space saving variant of template)
    t = open(file_path, "w")  # w = write, overwrites existing files
    t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
    t.close

startSvg()

Powoduje to dołączenie pliku szablonu i utworzenie znacznika otwierającego svg wraz z jego atrybutami.
Każda instrukcja zapisu zawiera jedną linię kodu svg zakończoną "\n", znacznikiem CR/LF.

def startSvg(file_path, sheet_width, sheet_height):
    # Create svg-tag including namespace and format definitions
    t = open(file_path, "a", encoding="utf-8")
    # a = append, new lines are added at the end of an existing file
    # encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
    t.write("\n" + "\n")
    t.write("<svg\n")
    #- Namespace declarations
    t.write("  xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
    t.write("  xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
    #- Format definition
    t.write("  width =\"" + sheet_width + "mm\"\n")
    t.write("  height=\"" + sheet_height + "mm\"\n")
    t.write("  viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
    # identical values for width and height and Viewbox' width and height
    # will synchronise mm and svg-units
    t.close

endSvg()

To dalej dołącza plik szablonu i tworzy znacznik zamykający svg; To ostatecznie kończy kod szablonu.

def endSvg(file_path):
    # Create closing svg-tag
    t = open(file_path, "a", encoding="utf-8")
    t.write("</svg>")
    t.close

main()

Funkcja main() wywołuje funkcje i przekazuje pewne parametry.

def main():
    """This one creates an empty A3 template"""
    #- Set the name of the template file and get its location
    template_file = pathToTemplate("MyTemplate.svg")  # Change the template name here
    #- Here starts the compiling of the svg file
    createSvgFile(template_file)  # overwrites existing File
    #- Set sheet format (DIN A3)
    format_width  = "420"
    format_height = "297"
    startSvg(template_file, format_width, format_height)  # adds svg start tag
    endSvg(template_file)  # adds svg end tag
    # At this point a new SVG-file is generated and saved
    return

W tym przykładzie format_width i format_height są twardo zakodowanymi wymiarami, obie niepotrzebne linie oznaczają punkty, w których inne sposoby pobierania danych formatu mogłyby umieścić swoją zawartość.

Kompletna makrodefinicja pustej strony

To makro składa się z powyższych segmentów kodu, gotowych do uruchomienia.

#! python
# -*- coding: utf-8 -*-
# (c) 2024 Your name LGPL

import os  # to enable the use of os.path.join()


# - SVG creation -
def createSvgFile(file_path):
    # Create a file and insert a header line (with t as the space saving variant of template)
    t = open(file_path, "w")  # w = write, overwrites existing files
    t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
    t.close

def startSvg(file_path, sheet_width, sheet_height):
    # Create svg-tag including namespace and format definitions
    t = open(file_path, "a", encoding="utf-8")
    # a = append, new lines are added at the end of an existing file
    # encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
    t.write("\n" + "\n")
    t.write("<svg\n")
    #- Namespace declarations
    t.write("  xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
    t.write("  xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
    #- Format definition
    t.write("  width =\"" + sheet_width + "mm\"\n")
    t.write("  height=\"" + sheet_height + "mm\"\n")
    t.write("  viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
    # identical values for width and height and Viewbox' width and height
    # will synchronise mm and svg-units
    t.close

def endSvg(file_path):
    # Create closing svg-tag
    t = open(file_path, "a", encoding="utf-8")
    t.write("</svg>")
    t.close

def pathToTemplate(template_name):
    """Link a given template name to the path of the template folder"""
    #- Get the path to the template folder that is set in the FreeCAD parameters
    parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
    template_path = parameter_path.GetString("TemplateDir")
    #- Link template_path and template_name for any OS
    path_to_file = os.path.join(template_path, template_name)  # to join path segments OS neutral
    return path_to_file

def main():
    """This one creates an empty A3 template"""
    #- Set the name of the template file and get its location
    template_file = pathToTemplate("MyTemplate.svg")  # Change the template name here
    #- Here starts the compiling of the svg file
    createSvgFile(template_file)  # overwrites existing File
    #- Set sheet format (DIN A3)
    format_width  = "420"
    format_height = "297"
    startSvg(template_file, format_width, format_height)  # adds svg start tag
    endSvg(template_file)  # adds svg end tag
    # At this point a new SVG-file is generated and saved
    return

if __name__ == '__main__':
    # This will be true only if the file is "executed"
    # but not if imported as module
    main()


... aby stworzyć stronę w której jest trochę tuszu

Aby stworzyć rysunek z pustej kartki potrzebujemy:

  • ramki czyli prostokąty utworzone za pomocą instrukcji rect,

bloku tytułowego i więcej linii utworzonych za pomocą instrukcji path,

  • proste teksty do indeksów i etykietowania komórek bloku tytułowego,
  • teksty edytowalne, takie jak numer lub nazwa części,

Zwykle te elementy graficzne są używane wielokrotnie, więc kod generujący jest umieszczony w czterech funkcjach:

  • svgRect() dla prostokątnych elementów ramki.
  • svgPath() dla elementów prostych.
  • svgText() dla tekstów statycznych.
  • ediText() dla tekstów edytowalnych.

Wszystkie funkcje są umieszczane wewnątrz powyższej makrodefinicji przed funkcją main(), a powiązane z nimi wywołania funkcji są wstawiane do funkcji main() między startSvg(...) i endSvg(...).

funkcja svgRect()

Aby narysować prostokąt musimy tylko wywołać funkcję svgRect i przekazać wartości szerokości, wysokości i położenia lewego górnego rogu. Jest to lepiej czytelne w tym miejscu, niż gdyby svg_line zawierał całą linię kodu svg.

def svgRect(width, height, x, y):
    # Generates an svg-instruction to draw a rectangle with the given values
    svg_line = (
        "<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x
        + "\" y=\"" + y + "\" />"
        )
    return svg_line

Instrukcja svg jest podzielona, aby pozostać w zalecanej długości linii Pythona, mimo to spowoduje to utworzenie pojedynczej linii svg.

funkcja svgPath()

Aby narysować linię wystarczy wywołać funkcję svgPath i przekazać współrzędne punktu początkowego i końcowego linii.

Zamiast współrzędnych punktu końcowego możemy przekazać znacznik do rysowania linii poziomej (h) lub pionowej (v), a następnie długość linii lub znacznik do rysowania linii poziomej (H) lub pionowej (V), a następnie rzędną x lub y punktu końcowego.

Każda ścieżka zaczyna się w punkcie początkowym i pierwszą czynnością jest ruch z "podniesionym piórem" (bez rysowania) do punktu początkowego. W tym przypadku ruch względny i ruch bezwzględny są takie same i dlatego nie ma znaczenia, czy znacznik m jest pisany wielką czy małą literą.

def svgPath(x1, y1, x2, y2):
    # Generates an svg-instruction to draw a path element (line) with the given values
    if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H":
        svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
    else:
        svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
    return svg_line

funkcja svgText()

Aby narysować fragment tekstu wystarczy wywołać funkcję svgText i przekazać współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu.

(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej).

def svgText(x, y, str_value, str_angle="0"):
    """
    Generates an svg-instruction to place a text element with the given values.
    Optional str_angle enables vertical and arbitrarily rotated texts
    """
    if str_angle == "0":
        svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>")
    else:
        svg_line = (
            "<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle
            + "," + x + "," + y + ")\">" + str_value + "</text>"
            )
    return svg_line

funkcja ediText()

Aby narysować fragment edytowalnego tekstu wystarczy wywołać funkcję ediText i przekazać jej nazwę, współrzędne punktu zakotwiczenia tekstu oraz sam ciąg tekstowy i opcjonalnie kąt dla obracanego tekstu. Aby obrócić tekst, instrukcja transformacji musi zostać wstawiona do każdego znacznika tekstowego osobno. Środki obrotu są ustawione na te same współrzędne, co powiązane punkty zakotwiczenia tekstu.

FreeCAD tworzy obiekt słownika z każdym wstawionym szablonem, a każdy wpis ma nazwę (klucz) i wartość.

(Wyrównanie tekstu jest kontrolowane przez otaczający znacznik grupy lub domyślnie wyrównane do lewej).

def ediText(entry_name, x, y, str_value, str_angle="0"):
    """
    Generates an svg-instruction to place an editable text element with the given values.
    Optional str_angle enables vertical and arbitrarily rotated editable texts
    """
    if str_angle == "0":
        svg_line = (
            "<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
            + "\">  <tspan>" + str_value + "</tspan>  </text>"
            )
    else:
        svg_line = (
            "<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
            + "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\">  <tspan>"
            + str_value + "</tspan>  </text>"
            )
    return svg_line

Funkcja createXxxx

Funkcje te rozpoczynają się od kodu otwierającego plik w trybie dołączania i zapisującego znacznik otwierający grupę.

Następnie widzimy sekcję do ustawiania i obliczania wartości oraz z instrukcjami zapisu, które wywołują powyższe funkcje w celu wygenerowania kodu svg.

I wreszcie znacznik zamykający grupę, po którym następuje instrukcja zamknięcia pliku.

def createFrame(file_path, sheet_width, sheet_height):
    # Creates rectangles for sheet frame and drawing area
    t = open(file_path, "a", encoding="utf-8")
    t.write("    <g id=\"drawing-frame\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n")
    #- upper left corner of inner Frame, drawing area
    frame_x = str(20)
    frame_y = str(10)
    #- frame dimensions
    frame_width = str(int(sheet_width) - 20 - 10)
    frame_height = str(int(sheet_height) - 10 - 10)
    #- frame rectangle
    t.write("      " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
    #- upper left corner outer frame, sheet frame
    frame_x = str(15)
    frame_y = str(5)
    #- frame dimensions
    frame_width = str(int(sheet_width)-20)
    frame_height = str(int(sheet_height)-10)
    #- frame rectangle
    t.write("      " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
    t.write("    </g>\n\n")
    t.close

Rezultat

Makro to dodaje kilka podstawowych elementów graficznych potrzebnych do prawidłowego działania szablonów tj. elementy linii, etykiety oraz teksty do edycji.

#! python
# -*- coding: utf-8 -*-
# (c) 2024 Your name LGPL

import os  # to enable the use of os.path.join()


# - SVG creation -
def createSvgFile(file_path):
    # Create a file and insert a header line (with t as the space saving variant of template)
    t = open(file_path, "w")  # w = write, overwrites existing files
    t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
    t.close

def startSvg(file_path, sheet_width, sheet_height):
    # Create svg-tag including namespace and format definitions
    t = open(file_path, "a", encoding="utf-8")
    # a = append, new lines are added at the end of an existing file
    # encoding="utf-8", helps with special characters if the Python interpreter is in ASCII mode
    t.write("\n" + "\n")
    t.write("<svg\n")
    #- Namespace declarations
    t.write("  xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"\n")
    t.write("  xmlns:freecad=\"http://www.freecad.org/wiki/index.php?title=Svg_Namespace\"\n")
    #- Format definition
    t.write("  width =\"" + sheet_width + "mm\"\n")
    t.write("  height=\"" + sheet_height + "mm\"\n")
    t.write("  viewBox=\"0 0 " + sheet_width + " " + sheet_height + "\">\n")
    # identical values for width and height and Viewbox' width and height
    # will synchronise mm and svg-units
    t.close

def endSvg(file_path):
    # Create closing svg-tag
    t = open(file_path, "a", encoding="utf-8")
    t.write("</svg>")
    t.close

def svgRect(width, height, x, y):
    # Ggenerates an svg-instruction to draw a rectangle with the given values
    svg_line = (
        "<rect width=\"" + width + "\" height=\"" + height + "\" x=\"" + x
        + "\" y=\"" + y + "\" />"
        )
    return svg_line

def svgPath(x1, y1, x2, y2):
    # Generates an svg-instruction to draw a path element (line) with the given values
    if x2 == "v" or x2 == "V" or x2 == "h" or x2 == "H":
        svg_line = ("<path d=\"m " + x1 + "," + y1 + " " + x2 + " " + y2 + "\" />")
    else:
        svg_line = ("<path d=\"m " + x1 + "," + y1 + " l " + x2 + "," + y2 + "\" />")
    return svg_line

def svgText(x, y, str_value, str_angle="0"):
    """
    Generates an svg-instruction to place a text element with the given values.
    Optional str_angle enables vertical and arbitrarily rotated texts
    """
    if str_angle == "0":
        svg_line = ("<text x=\"" + x + "\" y=\"" + y + "\">" + str_value + "</text>")
    else:
        svg_line = (
            "<text x=\"" + x + "\" y=\"" + y + "\" transform=\"rotate(" + str_angle
            + "," + x + "," + y + ")\">" + str_value + "</text>"
            )
    return svg_line

def ediText(entry_name, x, y, str_value, str_angle="0"):
    """
    Generates an svg-instruction to place an editable text element with the given values.
    Optional str_angle enables vertical and arbitrarily rotated editable texts
    """
    if str_angle == "0":
        svg_line = (
            "<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
            + "\">  <tspan>" + str_value + "</tspan>  </text>"
            )
    else:
        svg_line = (
            "<text freecad:editable=\"" + entry_name + "\" x=\"" + x + "\" y=\"" + y
            + "\" transform=\"rotate(" + str_angle + "," + x + "," + y + ")\">  <tspan>"
            + str_value + "</tspan>  </text>"
            )
    return svg_line

def createFrame(file_path, sheet_width, sheet_height):
    # Creates rectangles for sheet frame and drawing area
    t = open(file_path, "a", encoding="utf-8")
    t.write("    <g id=\"drawing-frame\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round\">\n")
    #- calculate upper left corner of inner Frame, drawing area
    frame_x = str(20)
    frame_y = str(10)
    #- frame dimensions
    frame_width = str(int(sheet_width) - 20 - 10)
    frame_height = str(int(sheet_height) - 10 - 10)
    #- frame rectangle
    t.write("      " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
    #- calculate upper left corner outer frame, sheet frame
    frame_x = str(15)
    frame_y = str(5)
    #- frame dimensions
    frame_width = str(int(sheet_width)-20)
    frame_height = str(int(sheet_height)-10)
    #- frame rectangle
    t.write("      " + svgRect(frame_width, frame_height, frame_x, frame_y) + "\n")
    t.write("    </g>\n\n")
    t.close

def createTitleBlock(file_path, sheet_width, sheet_height):
    """Creates a title block and transfers it to the position according to the sheet dimensions"""
    #- calculate title block origin (lower left corner), offset from page origin
    tbX = str(int(sheet_width) - 10 - 180)  # 180 according to DIN EN ISO 7200
    tbY = str(int(sheet_height) - 10)
    t = open(file_path, "a", encoding="utf-8")
    #- group to transfer all included title block elements at once
    t.write("    <g id=\"titleblock\"\n")
    t.write("      transform=\"translate(" + tbX + "," + tbY + ")\">\n")
    #- sub-group of title block line framework
    t.write("      <g id=\"titleblock-frame\"\n")
    t.write("        style=\"fill:none;stroke:#000000;stroke-width:0.35;\
stroke-linecap:miter;stroke-miterlimit:4\">\n")
    t.write("        " + svgPath("  0","  0","  0","-63") + "\n")
    t.write("        " + svgPath("  0","-63","180","  0") + "\n")
    t.write("        " + svgPath("  0","-30","h","155") + "\n")
    t.write("        " + svgPath("155","  0","v","-63") + "\n")
    t.write("      </g>\n")
    #- sub-group of title block static texts (left-aligned by default)
    t.write("      <g id=\"titleblock-text-non-editable\"\n")
    t.write("        style=\"font-size:5.0;text-anchor:start;fill:#000000;\
font-family:osifont\">\n")
    t.write("        " + svgText("  4.5","-43.5 ","Some static text") + "\n")
    t.write("        " + svgText("  4.5","-13.5 ","More static text") + "\n")
    t.write("        " + svgText("162.5","-3.5 ","Vertical static text","-90") + "\n")
    t.write("      </g>\n")
    t.write("    </g>\n\n")
    t.close

def createEditableText(file_path, sheet_width, sheet_height):
    """Creates editable texts positioned according to the page origin"""
    #- calculate offset to titleblock origin
    edX = int(sheet_width) - 10 - 180 # 180 according to DIN EN ISO 7200
    edY = int(sheet_height) - 10
    t = open(file_path, "a", encoding="utf-8")
    #- group editable texts using the same attributes
    t.write("    <g id=\"titleblock-editable-texts\"\n")
    t.write("      style=\"font-size:7.0;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write(
        "      " + ediText("EdiText-1",str(edX + 60),str(edY - 43.5),"Some editable text") + "\n"
        )
    t.write(
        "      " + ediText("EdiText-2",str(edX + 60),str(edY - 13.5),"More editable text") + "\n"
        )
    t.write(
        "      " + ediText("EdiText-3",str(edX + 173),str(edY - 4.5),"90° editable text","-90")
        + "\n"
        )
    t.write("    </g>\n\n")
    t.close

def pathToTemplate(template_name):
    """Link a given template name to the path of the template folder"""
    #- Get the path to the template folder that is set in the FreeCAD parameters
    parameter_path = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/TechDraw/Files")
    template_path = parameter_path.GetString("TemplateDir")
    #- Link template_path and template_name for any OS
    path_to_file = os.path.join(template_path, template_name)  # to join path segments OS neutral
    return path_to_file

def main():
    """This one creates an A3 template with simple frame and title block"""
    #- Set the name of the template file and get its location
    template_file = pathToTemplate("MyTemplate.svg")  # Change the template name here
    #- Here starts the compiling of the svg file
    createSvgFile(template_file)  # overwrites existing File
    #- Set sheet format (DIN A3)
    format_width  = "420"
    format_height = "297"
    startSvg(template_file, format_width, format_height)  # adds svg start tag
    createFrame(template_file, format_width, format_height)
    createTitleBlock(template_file, format_width, format_height)
    createEditableText(template_file, format_width, format_height)
    endSvg(template_file)  # adds svg end tag
    # At this point a new SVG-file is generated and saved
    return

if __name__ == '__main__':
    # This will be true only if the file is "executed"
    # but not if imported as module
    main()

A to jest kod svg pochodzący z tej makrodefinicji:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<svg
  xmlns="http://www.w3.org/2000/svg" version="1.1"
  xmlns:freecad="http://www.freecad.org/wiki/index.php?title=Svg_Namespace"
  width ="420mm"
  height="297mm"
  viewBox="0 0 420 297">
    <g id="drawing-frame"
      style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round">
      <rect width="390" height="277" x="20" y="10" />
      <rect width="400" height="287" x="15" y="5" />
    </g>

    <g id="titleblock"
      transform="translate(230,287)">
      <g id="titleblock-frame"
        style="fill:none;stroke:#000000;stroke-width:0.35;stroke-linecap:miter;stroke-miterlimit:4">
        <path d="m   0,  0 l   0,-63" />
        <path d="m   0,-63 l 180,  0" />
        <path d="m   0,-30 h 155" />
        <path d="m 155,  0 v -63" />
      </g>
      <g id="titleblock-text-non-editable"
        style="font-size:5.0;text-anchor:start;fill:#000000;font-family:osifont">
        <text x="  4.5" y="-43.5 ">Some static text</text>
        <text x="  4.5" y="-13.5 ">More static text</text>
        <text x="162.5" y="-3.5 " transform="rotate(-90,162.5,-3.5 )">Vertical static text</text>
      </g>
    </g>

    <g id="titleblock-editable-texts"
      style="font-size:7.0;text-anchor:start;fill:#0000d0;font-family:osifont">
      <text freecad:editable="EdiText-1" x="290" y="243.5">  <tspan>Some editable text</tspan>  </text>
      <text freecad:editable="EdiText-2" x="290" y="273.5">  <tspan>More editable text</tspan>  </text>
      <text freecad:editable="EdiText-3" x="403" y="282.5" transform="rotate(-90,403,282.5)">  <tspan>90° editable text</tspan>  </text>
    </g>

</svg>

I jak to powinno wyglądać po wstawieniu (plus powiększony blok tytułowy):

Kolorowanie edytowalnych tekstów na niebiesko jest tylko osobistym wyborem, aby łatwiej odróżnić statyczne i edytowalne teksty na gotowym rysunku.