Sandbox:TechDraw TemplateHelper

From FreeCAD Documentation
Revision as of 10:49, 27 April 2021 by FBXL5 (talk | contribs) (Created page with "<languages/> <translate> {{UnfinishedDocu{{#translation:}}}} {{Docnav | | | | | | }} {{TOCright}} == Introduction == This document is about creating a drawing template and...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

This documentation is not finished. Please help and contribute documentation.

GuiCommand model explains how commands should be documented. Browse Category:UnfinishedDocu to see more incomplete pages like this one. See Category:Command Reference for all commands.

See WikiPages to learn about editing the wiki pages, and go to Help FreeCAD to learn about other ways in which you can contribute.

Introduction

This document is about creating a drawing template and to insert into a FreeCAD file using a Python macro.

I managed to automate my drawing generation with a Python script which

  • generates a temporary svg template of the desired size (DIN A formats so far...)
  • inserts this temporary template into the active FreeCAD document
  • fills some values into the editable text fields

The development was somehow chaotic, because I'm still learning Python and some solution may seen to be a little odd - but it works at least...

Now I'd like to share what I have learned and coded so far and rewrite it in a tidier way.

I think it is better to show how to solve certain problems rather than just presenting a somehow finished macro.

Anyone is invited to borrow some code and develop similar or hopefully better tools that make handling drawings easier.

This sandbox is to start my documentation. At some further state it may be split into several pages for easier use.

Task list

To automatically generate a drawing page we need to know

  • how to insert a template into a FreeCAD document
  • how to set up a user interface to set some page parameters
  • how to generate a template
  • how to manipulate editable texts

Insert a page and template

Usually a template will be inserted into the active FreeCAD document which means a FreeCAD must be running and a file must be opened.

As this macro is launched from within a FreeCAD file the only variables that needs to be set are

  • templatePath, the path to the template folder and
  • templateName, the name of the template to be inserted.

(MyTemplate.svg is the name for the generated template in a following section. Change it to test the macro with your favourite template.)

(This has still to be done manually before running the macro as long as I'm not sure where to read the path variable ...)

An object to address the active document is introduced and named activeDoc.

And finally two variables are filled with the names of the generated page and template. (I prefer 2 digits for the numbers in contrast to FreeCAD)


Insert just one page and template

Code to add a page and insert a template

#! python
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
#
#
#- set the template path to the directory where your templates are stored
templatePath = "/Users/.../FreeCAD/Templates/"
templateName = "MyTemplate.svg"

# --- Main section ---

# -- Add a page and a template to the active document --

activeDoc = App.activeDocument()

newPage = 'Page01'
newTemplate = 'Template01'

# add a page object to the active document
activeDoc.addObject('TechDraw::DrawPage',newPage)

# add a template object to the active document
activeDoc.addObject('TechDraw::DrawSVGTemplate',newTemplate)

# load the svg template into the template object
activeDoc.getObject(newTemplate).Template = templatePath+templateName

# add the template object to the page's object list
activeDoc.getObject(newPage).Template = activeDoc.getObject(newTemplate)

# rename 'Page' to 'Sheet'
activeDoc.getObject(newPage).Label = 'Sheet ' + newPage[4:]

# open the page object for editing
activeDoc.getObject(newPage).ViewObject.doubleClicked()
  • A page object and a template object are added to the active document's object list.
  • The content of the svg document is loaded into the template object.
  • The template object is put onto the page's objects list.
  • Drawings are on sheets rather than on pages and so they are renamed.
Since the property "Name" cannot be changed the property "Label" must be altered.
  • Finally the drawing view is opened and activated for editing.


Insert more than just one page and template

The above code works for single pages only. It needs some extra lines to detect existing pages and to increment the page number.

The macro tries to read the property label of Page01 to detect if page01 exists. If it doesn't it will be created.

If it exists the page number will be incremented and the next try to detect a page starts.

Code to check for existing pages to add a first page or yet another

#! python
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
#
#
#- set the template path to the directory where your templates are stored
templatePath = "/Users/.../FreeCAD/Templates/"
templateName = "MyTemplate.svg"

# --- Main section ---

# -- Add a page and a template to the active document --

activeDoc = App.activeDocument()

newPage = 'Page01' # first page name to start search

# to find the current page number to create
pageCount = 1
pageFound = True
while pageFound:
	try:
		print(activeDoc.getObject(newPage).Label)
	except AttributeError:
		print(newPage + " to be created")
		pageFound = False
	else:
		pageCount = pageCount + 1
		if pageCount < 10:
			newPage = newPage[0:4] + '0' + str(pageCount)
		else:
			newPage = newPage[0:4] + str(pageCount)

# set template number according to page number
newTemplate = ('Template' + newPage[4:])

# add a page object to the active document
activeDoc.addObject('TechDraw::DrawPage',newPage)

# add a template object to the active document
activeDoc.addObject('TechDraw::DrawSVGTemplate',newTemplate)

# load the svg template into the template object
activeDoc.getObject(newTemplate).Template = templatePath+templateName

# add the template object to the page's object list
activeDoc.getObject(newPage).Template = activeDoc.getObject(newTemplate)

# rename 'Page' to 'Sheet'
activeDoc.getObject(newPage).Label = 'Sheet ' + newPage[4:]

# open the page object for editing
activeDoc.getObject(newPage).ViewObject.doubleClicked()

I recommend to save the file after each added page.


Generate a template

A template provides a background for the drawing tasks. The template's dimensions are used by the printer drivers to scale the drawing correctly.

The templates are svg-files and so the macro has to write lines of svg code (which is a subset of xml code).


Generate a simple blank page template

This macro shows the principle how an svg-file can be put together. (Format is A3)

Code to create a simple svg-file

#! python
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
#
#
#- set the template path to the directory where your templates are stored
templatePath = "/Users/.../FreeCAD/Templates/"
templateName = "MyTemplate.svg"

# - SVG creation -

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

#- Create opening svg-tag
#   Namespace section
def StartSvg(filePath):
    t=open(filePath,"a") # a = append, new lines are added at the end of an existing file
    t.write("\n"+"\n")
    t.write("<svg\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.close
#   Sheet size section
def CreateSheet(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("  width =\""+shWidth+"mm\"\n")
    t.write("  height=\""+shHeight+"mm\"\n")
    t.write("  viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n")
    t.close
    # identical values for width and height and Viewbox' width and height will synchronise mm and svg-units

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

# --- Main section ---

CreateSvgFile(templatePath+templateName) # overwrites existing File

# Set sheet format (DIN A3)
formatWidth  = "420"
formatHeight = "297"

StartSvg(templatePath+templateName)  # adds Start tag and namespaces
CreateSheet(templatePath+templateName, formatWidth, formatHeight)
EndSvg(templatePath+templateName)
# At this point a new SVG-file is generated and saved
The result, a simple template for a blank page (A3 landscape):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<svg
  xmlns="http://www.w3.org/2000/svg" version="1.1"
  xmlns:freecad="http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace"
  width ="420mm"
  height="297mm"
  viewBox="0 0 420 297">
</svg>
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.
After that the file will be repeatedly opened to append further segments and closed again after appending.
(The FreeCAD namespace is an investment into the future when editable texts come into play)


Now some ink will be added to the blank page:

Add code segments to...

The following code segments are to be added to the above code. They should placed between the svg-tags.

They are usually split in a part to collect data for parameter values and one part to create the Python code lines with given parameters.

All functions dealing with formatWidth and formatHeight can create svg-code to match each DIN format from A4 to A0.

... create frames

Code segments (to create the svg-instructions) to draw two rectangles, one for a sheet frame and one around the drawing area.

Additional code to create frames

# - SVG creation -
class ToteBag:
    pass
# This class is empty to act as a container for several variables
#- one bag for drawing borders
borders = ToteBag()

#- set default values - custom values may be added 
def setDINBorderValues(format):
    borders.drawingAreaTop    = 10 # distance from page boundary
    borders.drawingAreaBottom = 10
    borders.drawingAreaLeft   = 20
    borders.drawingAreaRight  = 10
    borders.sheetFrameTop     = 5  # distance from drawing area border
    borders.sheetFrameBottom  = 5
    borders.sheetFrameLeft    = 5
    borders.sheetFrameRight   = 5

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

#- Frame creation
def CreateFrame(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("    <g id=\"drawing-frame\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.5;\
stroke-linecap:miter;stroke-miterlimit:4\">\n")
    #- set frame offsets
    dAT = borders.drawingAreaTop
    dAB = borders.drawingAreaBottom
    dAL = borders.drawingAreaLeft
    dAR = borders.drawingAreaRight
    sFT = borders.sheetFrameTop
    sFB = borders.sheetFrameBottom
    sFL = borders.sheetFrameLeft
    sFR = borders.sheetFrameRight

    # inner Frame, drawing area
    #- upper left corner
    drawingX=str(dAL)
    drawingY=str(dAT)
    #- lower right corner
    drawingWidth=str(int(shWidth)-dAR)
    drawingHeight=str(int(shHeight)-dAB)
    t.write("      \n")
    #- frame dimensions
    drawingWidth=str(int(shWidth)-dAL-dAR)
    drawingHeight=str(int(shHeight)-dAT-dAB)
    t.write("      "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
    # outer frame
    #- upper left corner
    drawingX=str(dAL-sFL)
    drawingY=str(dAT-sFT)
    #- lower right corner
    drawingWidth=str(int(shWidth)-dAR+sFR)
    drawingHeight=str(int(shHeight)-dAB+sFB)
    t.write("      \n")
    #- frame dimensions
    drawingWidth=str(int(shWidth)-dAL-dAR+sFL+sFR)
    drawingHeight=str(int(shHeight)-dAT-dAB+sFT+sFB)
    t.write("      "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
    t.write("    </g>\n\n")
    t.close

# --- Main section ---

#borders = ToteBag()
setDINBorderValues('A3')

CreateFrame(templatePath+templateName, formatWidth, formatHeight)
Each write instruction has to start with a specific number of blank spaces for the correct indentation of the resulting svg-file.
The toteBag class is used to collect and distribute global values. I'm not sure yet if this really makes sense...


... to add index fields

Code segments to calculate a few parameters based on sheet dimensions and then create code to place the separator lines, index letters and numbers and a few lines to help folding this drawing and placing a puncher. Additional functions to create several path and text instructions for the svg-file.


Additional code to create index fields

# SVG creation

#- Function to generate 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":
        svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
    else:
        svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
    return svgLine

#- Function to generate an svg-instruction to place a text element with the given values
def svgtext(posX,posY,strValue):
    svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>")
    return svgLine

#- Indexes and folding marks creation
def CreateDecoration(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("    <g id=\"index-separators\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.25;\
stroke-linecap:round\">\n")
    #- set frame offsets
    dAT = borders.drawingAreaTop
    dAB = borders.drawingAreaBottom
    dAL = borders.drawingAreaLeft
    dAR = borders.drawingAreaRight
    sFT = borders.sheetFrameTop
    sFB = borders.sheetFrameBottom
    sFL = borders.sheetFrameLeft
    sFR = borders.sheetFrameRight
    drawingX=str(dAL)
    drawingY=str(dAT)
    drawingWidth=str(int(shWidth)-dAL-dAR)
    drawingHeight=str(int(shHeight)-dAT-dAB)

    #- starting point values of center lines
    indexCenter=str(int(drawingWidth)/2+dAL)
    indexMiddle=str(int(drawingHeight)/2+dAT)
    indexLeft=str(dAL+5)
    indexRight=str(int(drawingWidth)+dAL-5)
    indexUpper=str(dAT+5)
    indexLower=str(int(drawingHeight)+dAT-5)

    #- centre and middle markings of drawing area
    if shWidth=="210": # format=="DIN-A4":
        indexLeft=str(dAL)
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-15")+"\n")
    elif shWidth=="420": # format=="DIN-A3":
        indexLeft=str(dAL+5)
        t.write("        "+svgpath(indexCenter,indexUpper,"v","-10")+"\n")
        t.write("        "+svgpath(indexCenter,indexLower,"v"," 10")+"\n")
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-20")+"\n")
        t.write("        "+svgpath(indexRight,indexMiddle,"h"," 10")+"\n")
    else :
        t.write("        "+svgpath(indexCenter,indexUpper,"v","-10")+"\n")
        t.write("        "+svgpath(indexCenter,indexLower,"v"," 10")+"\n")
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-10")+"\n")
        t.write("        "+svgpath(indexRight,indexMiddle,"h"," 10")+"\n")

    #- starting point values of separator lines
    indexLeft =str(dAL)
    indexRight=str(int(drawingWidth)+dAL)
    indexUpper=str(dAT)
    indexLower=str(int(drawingHeight)+dAT)

    #- set number of horizontal and vertical indexes
    if shWidth=="420": # format=="DIN-A3":
        indexCountX=8
        indexCountY=6
    elif shWidth=="594": # format=="DIN-A2":
        indexCountX=12
        indexCountY=8
    elif shWidth=="841": # format=="DIN-A1":
        indexCountX=16
        indexCountY=12
    elif shWidth=="1189": # format=="DIN-A0":
        indexCountX=24
        indexCountY=16
    else :
        indexCountX=0
        indexCountY=0

    #- indexCenter and indexMiddle contain strings but floating point
    #   numbers are needed to calculate
    floatCenter=int(drawingWidth)/2+dAL
    floatMiddle=int(drawingHeight)/2+dAT

    #- horizontal index separators
    max=int(indexCountX/2-1)
    for value in range(0,max):
        indexX=str(floatCenter+(value+1)*50)
        t.write("        "+svgpath(indexX,indexUpper,"v"," -5")+"\n")
        t.write("        "+svgpath(indexX,indexLower,"v","  5")+"\n")
        indexX=str(floatCenter-(value+1)*50)
        t.write("        "+svgpath(indexX,indexUpper,"v"," -5")+"\n")
        t.write("        "+svgpath(indexX,indexLower,"v","  5")+"\n")

    #- vertical index separators
    max=int(indexCountY/2-1)
    for value in range(0,max):
        indexY=str(floatMiddle+(value+1)*50)
        t.write("        "+svgpath(indexLeft, indexY,"h"," -5")+"\n")
        t.write("        "+svgpath(indexRight,indexY,"h","  5")+"\n")
        indexY=str(floatMiddle-(value+1)*50)
        t.write("        "+svgpath(indexLeft, indexY,"h"," -5")+"\n")
        t.write("        "+svgpath(indexRight,indexY,"h","  5")+"\n")

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

    t.write("    <g id=\"indexes\"\n")
    t.write("      style=\"font-size:3.5;text-anchor:middle;fill:#000000;\
    font-family:osifont\">\n")

    #- position point values of indexes
    indexLeft=str(dAL-sFL/2)
    indexRight=str(int(drawingWidth)+dAL+sFR/2)
    indexUpper=str(dAT-1)
    indexLower=str(int(drawingHeight)+dAT+sFB-1)

    #- horizontal indexes, numbers
    max=int(indexCountX/2)
    for value in range(0,max):
        indexX=str(floatCenter+value*50+25)
        t.write("        "+svgtext(indexX,indexUpper,str(int(indexCountX/2+value+1)))+"\n")
        t.write("        "+svgtext(indexX,indexLower,str(int(indexCountX/2+value+1)))+"\n")
        indexX=str(floatCenter-value*50-25)
        t.write("        "+svgtext(indexX,indexUpper,str(int(indexCountX/2-value)))+"\n")
        t.write("        "+svgtext(indexX,indexLower,str(int(indexCountX/2-value)))+"\n")

    #- vertical indexes, letters
    max=int(indexCountY/2)
    for value in range(0,max):
        indexY=str(floatMiddle+value*50+25)
        if int(indexCountY/2+value+1)>9 :  # to avoid the letter J
            t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2+value+2)))+"\n")
            t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2+value+2)))+"\n")
        else :
            t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2+value+1)))+"\n")
            t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2+value+1)))+"\n")
        indexY=str(floatMiddle-value*50-25)
        t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2-value)))+"\n")
        t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2-value)))+"\n")

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

    #- puncher mark
    t.write("    <g id=\"puncher mark\"\n")
    t.write("      style=\"fill:none;stroke:#b0b0b0;stroke-width:0.25;\
    stroke-linecap:miter;stroke-miterlimit:4\">\n")
    if shWidth in["1189", "841", "594"] : # A3 and A4 have extended middle markings
        t.write("        "+svgpath(str(dAL-sFL),str(int(formatHeight)-(297/2)),"h","-10")+"\n")
    t.write("    </g>\n\n")

    #- folding marks
    t.write("    <g id=\"folding marks\"\n")
    t.write("      style=\"fill:none;stroke:#b0b0b0;stroke-width:0.25;\
    stroke-linecap:miter;stroke-miterlimit:4\">\n")
    if shWidth=="420": # DIN-A3
        t.write("        "+svgpath("125",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("125",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("230",str(dAT-sFT),"v","-5")+"\n")
        t.write("        "+svgpath("230",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
    elif shWidth=="594": # DIN-A2
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("402",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("402",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","123","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"123","h","  5")+"\n")
    elif shWidth=="841": # DIN-A1
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("400",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("400",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("651",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("651",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","297","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"297","h","  5")+"\n")
    elif shWidth=="1189": # DIN-A0
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("400",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("400",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("590",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("590",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("780",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("780",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("999",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("999",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","247","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"247","h","  5")+"\n")
        t.write("        "+svgpath("  5","544","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"544","h","  5")+"\n")

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

# --- Main section ---

CreateDecoration(templatePath+templateName, formatWidth, formatHeight)
Comment.
More comments.

... create title block elements

Code segments (to create the svg-instructions) to draw

  • a title block outline
  • the inner segmentation
  • static texts like e.g.labels for the text boxes
  • editable texts, at least their placeholders

The first two points can be combined if only one line type and a single thickness is use.

All except the editable texts can be drawn in relation to the svg-file's origin as the base point of the title block and then be grouped to move them to the desired position in a single step.

Additional code to create a title block

#- Title block movable
def CreateTitleBlock(filePath,shWidth,shHeight):
    #- set frame offsets
    dAB = borders.drawingAreaBottom
    dAR = borders.drawingAreaRight

    #- lower left corner
    tbX=str(int(shWidth)-dAR-180) # 180 according to DIN EN ISO 7200
    tbY=str(int(shHeight)-dAB)
    #- group to move allelements in one step
    t=open(filePath,"a")
    t.write("    <g id=\"titleblock\"\n")
    t.write("      transform=\"translate("+tbX+","+tbY+")\">\n")
    t.write("      \n\n")
    #- title block
    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"," -4","h","155")+"\n")
    t.write("        "+svgpath("  0","-16","h","155")+"\n")
    t.write("        "+svgpath("  0","-30","h","155")+"\n")
    t.write("        "+svgpath("  0","-46.5","h"," 50")+"\n")
    t.write("        "+svgpath(" 25"," -4","v","-26")+"\n")
    t.write("        "+svgpath(" 50"," -4","v","-59")+"\n")
    t.write("        "+svgpath("140"," -4","v","-12")+"\n")
    t.write("        "+svgpath("155","  0","v","-63")+"\n")
    t.write("        "+svgpath("160","  0","v","-63")+"\n")
    t.write("        "+svgpath("155"," -7","h"," 25")+"\n")
    t.write("        "+svgpath("155","-14","h"," 25")+"\n")
    t.write("        "+svgpath("155","-21","h"," 25")+"\n")
    t.write("        "+svgpath("155","-28","h"," 25")+"\n")
    t.write("        "+svgpath("155","-35","h"," 25")+"\n")
    t.write("        "+svgpath("155","-42","h"," 25")+"\n")
    t.write("        "+svgpath("155","-49","h"," 25")+"\n")
    t.write("        "+svgpath("155","-56","h"," 25")+"\n")
    t.write("      </g>\n")
    #- small texts, left-aligned
    t.write("      <g id=\"titleblock-text-non-editable\"\n")
    t.write("        style=\"font-size:2.5;text-anchor:start;fill:#000000;\
font-family:osifont\">\n")

    t.write("        "+svgtext("  1.5","-60  ","Author Name:")+"\n")
    t.write("        "+svgtext("  1.5","-52  ","Date:")+"\n")
    t.write("        "+svgtext("  1.5","-43.5 ","Supervisor Name:")+"\n")
    t.write("        "+svgtext("  1.5","-35.5 ","Date:")+"\n")
    t.write("        "+svgtext("  1.5","-27  ","Format:")+"\n")
    t.write("        "+svgtext("  1.5","-13  ","Scale:")+"\n")
    t.write("        "+svgtext(" 26.5","-13  ","Weight:")+"\n")
    t.write("        "+svgtext(" 51.5","-27  ","Company:")+"\n")
    t.write("        "+svgtext(" 51.5","-13  ","Version:")+"\n")
    t.write("        "+svgtext("141.5","-13  ","Sheet")+"\n")
    t.write("      </g>\n")
    #- revision indexes, centered
    t.write("      <g id=\"titleblock-revision-indexes\"\n")
    t.write("        style=\"font-size:5.0;text-anchor:middle;fill:#000000;\
font-family:osifont\">\n")
    t.write("        "+svgtext("157.5"," -1.5  ","A")+"\n")
    t.write("        "+svgtext("157.5"," -8.5  ","B")+"\n")
    t.write("        "+svgtext("157.5","-15.5  ","C")+"\n")
    t.write("        "+svgtext("157.5","-22.5  ","D")+"\n")
    t.write("        "+svgtext("157.5","-29.5  ","E")+"\n")
    t.write("        "+svgtext("157.5","-36.5  ","F")+"\n")
    t.write("        "+svgtext("157.5","-43.5  ","G")+"\n")
    t.write("        "+svgtext("157.5","-50.5  ","H")+"\n")
    t.write("        "+svgtext("157.5","-57.5  ","I")+"\n")
    t.write("      </g>\n")
    
    t.write("    </g>\n\n")
    t.close


# --- Main section ---

CreateTitleBlock(templatePath+templateName, formatWidth, formatHeight)

Now there will be a supergroup "title block" containing the groups "titleblock-frame", "titleblock-text-non-editable", and"titleblock-revision-indexes" in the resulting svg-file.

The editable texts are put into a separate group as they require absolute coordinates

Additional code to create a editable texts

#- Function to generate an svg-instruction to place an editable text element with the given values
def FCeditext(entryName,posX,posY,strValue):
    svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \
    +"\">  <tspan>"+strValue+"</tspan>  </text>")
    return svgLine

#- Title block editable texts
def CreateEditableText(filePath,shWidth,shHeight):
    #- set frame offsets
    dAB = borders.drawingAreaBottom
    dAR = borders.drawingAreaRight

    #- offsets for editable texts
    edX=int(shWidth)-dAR-180 # 180 according to DIN EN ISO 7200
    edY=int(shHeight)-dAB

    t=open(filePath,"a")
    t.write("    <g id=\"titleblock-editable-owner\"\n")
    t.write("      style=\"font-size:3.5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")

    t.write("      "+FCeditext("Owner",str(edX+60),str(edY-27.0),"Owner")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-address\"\n")
    t.write("      style=\"font-size:2.5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Address-1",str(edX+60),str(edY-23.5),"Address1")+"\n")
    t.write("      "+FCeditext("Address-2",str(edX+60),str(edY-20.0),"Address2")+"\n")
    t.write("      "+FCeditext("MailTo",   str(edX+60),str(edY-16.5),"MailTo")+"\n")
    t.write("      "+FCeditext("Copyright",str(edX+2), str(edY-1.0), "Copyright")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-medium\"\n")
    t.write("      style=\"font-size:5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Author",    str(edX+2),  str(edY-55.0),"Author")+"\n")
    t.write("      "+FCeditext("AuDate",    str(edX+7),  str(edY-47.5),"YYYY/MM/DD")+"\n")
    t.write("      "+FCeditext("Supervisor",str(edX+2),  str(edY-38.5),"Supervisor")+"\n")
    t.write("      "+FCeditext("SvDate",    str(edX+7),  str(edY-31.0),"YYYY/MM/DD")+"\n")
    t.write("      "+FCeditext("SubTitle",  str(edX+64), str(edY-42.0),"Sub-title")+"\n")
    t.write("      "+FCeditext("Version",   str(edX+60), str(edY-8.0), "Version")+"\n")
    t.write("      "+FCeditext("Revision-A",str(edX+162),str(edY-1.5), "______")+"\n")
    t.write("      "+FCeditext("Revision-B",str(edX+162),str(edY-8.5), "______")+"\n")
    t.write("      "+FCeditext("Revision-C",str(edX+162),str(edY-15.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-D",str(edX+162),str(edY-22.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-E",str(edX+162),str(edY-29.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-F",str(edX+162),str(edY-36.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-G",str(edX+162),str(edY-43.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-H",str(edX+162),str(edY-50.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-I",str(edX+162),str(edY-57.5),"______")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-centered\"\n")
    t.write("      style=\"font-size:5;text-anchor:middle;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Format", str(edX+12), str(edY-21),"A3")+"\n")
    t.write("      "+FCeditext("Sheets", str(edX+148),str(edY-8), "1 / 1")+"\n")
    t.write("      "+FCeditext("Scale",  str(edX+12), str(edY-8), "1 : 1")+"\n")
    t.write("      "+FCeditext("Weight", str(edX+35), str(edY-8), "___ kg")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-Large\"\n")
    t.write("      style=\"font-size:7;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Title",   str(edX+62),str(edY-50),"Drawing Title")+"\n")
    t.write("      "+FCeditext("PtNumber",str(edX+62),str(edY-32),"Part Number")+"\n")
    t.write("    </g>\n\n")
    t.close

# --- Main section ---

CreateEditableText(templatePath+templateName, formatWidth, formatHeight)

The resulting macro

All sections put together give a macro like this:

(Don't forget to edit the template path and start the macro from within FreeCAD.)

Complete code to create a template

#! python
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
#
#
#- set the template path to the directory where your templates are stored
templatePath = "/Users/.../FreeCAD/Templates/"
templateName = "MyTemplate.svg"

# - SVG creation -

class ToteBag:
    pass
# This class is empty to act as a container for several variables
#- one bag for drawing borders
borders = ToteBag()

#- set values according to DIN XXX
def setDINBorderValues(format):
    borders.drawingAreaTop    = 10 # distance from page boundary
    borders.drawingAreaBottom = 10
    borders.drawingAreaLeft   = 20
    borders.drawingAreaRight  = 10
    borders.sheetFrameTop     = 5  # distance from drawing area border
    borders.sheetFrameBottom  = 5
    borders.sheetFrameLeft    = 5
    borders.sheetFrameRight   = 5

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

#- Function to generate 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":
        svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
    else:
        svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
    return svgLine

#- Function to generate an svg-instruction to place a text element with the given values
def svgtext(posX,posY,strValue):
    svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>")
    return svgLine

#- Function to generate an svg-instruction to place an editable text element with the given values
def FCeditext(entryName,posX,posY,strValue):
    svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \
    +"\">  <tspan>"+strValue+"</tspan>  </text>")
    return svgLine

#- Create a file and insert a header line
def CreateSvgFile(filePath):
    t=open(filePath,"w") # w = write, overwrites existing files
    t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
    t.close

#- Create opening svg-tag
#   Namespace section
def StartSvg(filePath):
    t=open(filePath,"a") # a = append, new lines are added at the end of an existing file
    t.write("\n"+"\n")
    t.write("<svg\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.close
#   Sheet size section
def CreateSheet(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("  width =\""+shWidth+"mm\"\n")
    t.write("  height=\""+shHeight+"mm\"\n")
    t.write("  viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n")
    t.close
    # identical values for width and height and Viewbox' width and height will synchronise mm and svg-units

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

#- Frame creation
def CreateFrame(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("    <g id=\"drawing-frame\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.5;\
stroke-linecap:round\">\n")
    #- set frame offsets
    dAT = borders.drawingAreaTop
    dAB = borders.drawingAreaBottom
    dAL = borders.drawingAreaLeft
    dAR = borders.drawingAreaRight
    sFT = borders.sheetFrameTop
    sFB = borders.sheetFrameBottom
    sFL = borders.sheetFrameLeft
    sFR = borders.sheetFrameRight
    # inner Frame, drawing area
    #- upper left corner
    drawingX=str(dAL)
    drawingY=str(dAT)
    #- lower right corner
    drawingWidth=str(int(shWidth)-dAR)
    drawingHeight=str(int(shHeight)-dAB)
    t.write("      \n")
    #- frame dimensions
    drawingWidth=str(int(shWidth)-dAL-dAR)
    drawingHeight=str(int(shHeight)-dAT-dAB)
    #- frame rectangle
    t.write("      "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
    # outer frame
    #- upper left corner
    drawingX=str(dAL-sFL)
    drawingY=str(dAT-sFT)
    #- lower right corner
    drawingWidth=str(int(shWidth)-dAR+sFR)
    drawingHeight=str(int(shHeight)-dAB+sFB)
    t.write("      \n")
    #- frame dimensions
    drawingWidth=str(int(shWidth)-dAL-dAR+sFL+sFR)
    drawingHeight=str(int(shHeight)-dAT-dAB+sFT+sFB)
    #- frame rectangle
    t.write("      "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
    t.write("    </g>\n\n")
    t.close

#- Indexes and folding marks creation
def CreateDecoration(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("    <g id=\"index-separators\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.25;\
stroke-linecap:round\">\n")
    #- set frame offsets
    dAT = borders.drawingAreaTop
    dAB = borders.drawingAreaBottom
    dAL = borders.drawingAreaLeft
    dAR = borders.drawingAreaRight
    sFT = borders.sheetFrameTop
    sFB = borders.sheetFrameBottom
    sFL = borders.sheetFrameLeft
    sFR = borders.sheetFrameRight
    drawingX=str(dAL)
    drawingY=str(dAT)
    drawingWidth=str(int(shWidth)-dAL-dAR)
    drawingHeight=str(int(shHeight)-dAT-dAB)

    #- starting point values of center lines
    indexCenter=str(int(drawingWidth)/2+dAL)
    indexMiddle=str(int(drawingHeight)/2+dAT)
    indexLeft=str(dAL+5)
    indexRight=str(int(drawingWidth)+dAL-5)
    indexUpper=str(dAT+5)
    indexLower=str(int(drawingHeight)+dAT-5)

    #- centre and middle markings of drawing area
    if shWidth=="210": # format=="DIN-A4":
        indexLeft=str(dAL)
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-15")+"\n")
    elif shWidth=="420": # format=="DIN-A3":
        indexLeft=str(dAL+5)
        t.write("        "+svgpath(indexCenter,indexUpper,"v","-10")+"\n")
        t.write("        "+svgpath(indexCenter,indexLower,"v"," 10")+"\n")
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-20")+"\n")
        t.write("        "+svgpath(indexRight,indexMiddle,"h"," 10")+"\n")
    else :
        t.write("        "+svgpath(indexCenter,indexUpper,"v","-10")+"\n")
        t.write("        "+svgpath(indexCenter,indexLower,"v"," 10")+"\n")
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-10")+"\n")
        t.write("        "+svgpath(indexRight,indexMiddle,"h"," 10")+"\n")

    #- starting point values of separator lines
    indexLeft =str(dAL)
    indexRight=str(int(drawingWidth)+dAL)
    indexUpper=str(dAT)
    indexLower=str(int(drawingHeight)+dAT)

    #- set number of horizontal and vertical indexes
    if shWidth=="420": # format=="DIN-A3":
        indexCountX=8
        indexCountY=6
    elif shWidth=="594": # format=="DIN-A2":
        indexCountX=12
        indexCountY=8
    elif shWidth=="841": # format=="DIN-A1":
        indexCountX=16
        indexCountY=12
    elif shWidth=="1189": # format=="DIN-A0":
        indexCountX=24
        indexCountY=16
    else :
        indexCountX=0
        indexCountY=0

    #- indexCenter and indexMiddle contain strings but floating point
    #   numbers are needed to calculate
    floatCenter=int(drawingWidth)/2+dAL
    floatMiddle=int(drawingHeight)/2+dAT

    #- horizontal index separators
    max=int(indexCountX/2-1)
    for value in range(0,max):
        indexX=str(floatCenter+(value+1)*50)
        t.write("        "+svgpath(indexX,indexUpper,"v"," -5")+"\n")
        t.write("        "+svgpath(indexX,indexLower,"v","  5")+"\n")
        indexX=str(floatCenter-(value+1)*50)
        t.write("        "+svgpath(indexX,indexUpper,"v"," -5")+"\n")
        t.write("        "+svgpath(indexX,indexLower,"v","  5")+"\n")

    #- vertical index separators
    max=int(indexCountY/2-1)
    for value in range(0,max):
        indexY=str(floatMiddle+(value+1)*50)
        t.write("        "+svgpath(indexLeft, indexY,"h"," -5")+"\n")
        t.write("        "+svgpath(indexRight,indexY,"h","  5")+"\n")
        indexY=str(floatMiddle-(value+1)*50)
        t.write("        "+svgpath(indexLeft, indexY,"h"," -5")+"\n")
        t.write("        "+svgpath(indexRight,indexY,"h","  5")+"\n")

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

    t.write("    <g id=\"indexes\"\n")
    t.write("      style=\"font-size:3.5;text-anchor:middle;fill:#000000;\
    font-family:osifont\">\n")

    #- position point values of indexes
    indexLeft=str(dAL-sFL/2)
    indexRight=str(int(drawingWidth)+dAL+sFR/2)
    indexUpper=str(dAT-1)
    indexLower=str(int(drawingHeight)+dAT+sFB-1)

    #- horizontal indexes, numbers
    max=int(indexCountX/2)
    for value in range(0,max):
        indexX=str(floatCenter+value*50+25)
        t.write("        "+svgtext(indexX,indexUpper,str(int(indexCountX/2+value+1)))+"\n")
        t.write("        "+svgtext(indexX,indexLower,str(int(indexCountX/2+value+1)))+"\n")
        indexX=str(floatCenter-value*50-25)
        t.write("        "+svgtext(indexX,indexUpper,str(int(indexCountX/2-value)))+"\n")
        t.write("        "+svgtext(indexX,indexLower,str(int(indexCountX/2-value)))+"\n")

    #- vertical indexes, letters
    max=int(indexCountY/2)
    for value in range(0,max):
        indexY=str(floatMiddle+value*50+25)
        if int(indexCountY/2+value+1)>9 :  # to avoid the letter J
            t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2+value+2)))+"\n")
            t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2+value+2)))+"\n")
        else :
            t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2+value+1)))+"\n")
            t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2+value+1)))+"\n")
        indexY=str(floatMiddle-value*50-25)
        t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2-value)))+"\n")
        t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2-value)))+"\n")

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

    #- puncher mark
    t.write("    <g id=\"puncher mark\"\n")
    t.write("      style=\"fill:none;stroke:#b0b0b0;stroke-width:0.25;\
    stroke-linecap:miter;stroke-miterlimit:4\">\n")
    if shWidth in["1189", "841", "594"] : # A3 and A4 have extended middle markings
        t.write("        "+svgpath(str(dAL-sFL),str(int(formatHeight)-(297/2)),"h","-10")+"\n")
    t.write("    </g>\n\n")

    #- folding marks
    t.write("    <g id=\"folding marks\"\n")
    t.write("      style=\"fill:none;stroke:#b0b0b0;stroke-width:0.25;\
    stroke-linecap:miter;stroke-miterlimit:4\">\n")
    if shWidth=="420": # DIN-A3
        t.write("        "+svgpath("125",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("125",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("230",str(dAT-sFT),"v","-5")+"\n")
        t.write("        "+svgpath("230",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
    elif shWidth=="594": # DIN-A2
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("402",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("402",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","123","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"123","h","  5")+"\n")
    elif shWidth=="841": # DIN-A1
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("400",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("400",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("651",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("651",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","297","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"297","h","  5")+"\n")
    elif shWidth=="1189": # DIN-A0
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("400",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("400",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("590",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("590",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("780",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("780",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("999",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("999",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","247","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"247","h","  5")+"\n")
        t.write("        "+svgpath("  5","544","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"544","h","  5")+"\n")

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

#- Title block movable
def CreateTitleBlock(filePath,shWidth,shHeight):
    #- set frame offsets
    dAB = borders.drawingAreaBottom
    dAR = borders.drawingAreaRight

    #- lower left corner
    tbX=str(int(shWidth)-dAR-180) # 180 according to DIN EN ISO 7200
    tbY=str(int(shHeight)-dAB)
    #- group to move allelements in one step
    t=open(filePath,"a")
    t.write("    <g id=\"titleblock\"\n")
    t.write("      transform=\"translate("+tbX+","+tbY+")\">\n")
    t.write("      \n\n")
    #- title block
    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"," -4","h","155")+"\n")
    t.write("        "+svgpath("  0","-16","h","155")+"\n")
    t.write("        "+svgpath("  0","-30","h","155")+"\n")
    t.write("        "+svgpath("  0","-46.5","h"," 50")+"\n")
    t.write("        "+svgpath(" 25"," -4","v","-26")+"\n")
    t.write("        "+svgpath(" 50"," -4","v","-59")+"\n")
    t.write("        "+svgpath("140"," -4","v","-12")+"\n")
    t.write("        "+svgpath("155","  0","v","-63")+"\n")
    t.write("        "+svgpath("160","  0","v","-63")+"\n")
    t.write("        "+svgpath("155"," -7","h"," 25")+"\n")
    t.write("        "+svgpath("155","-14","h"," 25")+"\n")
    t.write("        "+svgpath("155","-21","h"," 25")+"\n")
    t.write("        "+svgpath("155","-28","h"," 25")+"\n")
    t.write("        "+svgpath("155","-35","h"," 25")+"\n")
    t.write("        "+svgpath("155","-42","h"," 25")+"\n")
    t.write("        "+svgpath("155","-49","h"," 25")+"\n")
    t.write("        "+svgpath("155","-56","h"," 25")+"\n")
    t.write("      </g>\n")
    #- small texts, left-aligned
    t.write("      <g id=\"titleblock-text-non-editable\"\n")
    t.write("        style=\"font-size:2.5;text-anchor:start;fill:#000000;\
font-family:osifont\">\n")

    t.write("        "+svgtext("  1.5","-60  ","Author Name:")+"\n")
    t.write("        "+svgtext("  1.5","-52  ","Date:")+"\n")
    t.write("        "+svgtext("  1.5","-43.5 ","Supervisor Name:")+"\n")
    t.write("        "+svgtext("  1.5","-35.5 ","Date:")+"\n")
    t.write("        "+svgtext("  1.5","-27  ","Format:")+"\n")
    t.write("        "+svgtext("  1.5","-13  ","Scale:")+"\n")
    t.write("        "+svgtext(" 26.5","-13  ","Weight:")+"\n")
    t.write("        "+svgtext(" 51.5","-27  ","Owner:")+"\n")
    t.write("        "+svgtext(" 51.5","-13  ","Version:")+"\n")
    t.write("        "+svgtext("141.5","-13  ","Sheet")+"\n")
    t.write("      </g>\n")
    #- revision indexes, centered
    t.write("      <g id=\"titleblock-revision-indexes\"\n")
    t.write("        style=\"font-size:5.0;text-anchor:middle;fill:#000000;\
font-family:osifont\">\n")
    t.write("        "+svgtext("157.5"," -1.5  ","A")+"\n")
    t.write("        "+svgtext("157.5"," -8.5  ","B")+"\n")
    t.write("        "+svgtext("157.5","-15.5  ","C")+"\n")
    t.write("        "+svgtext("157.5","-22.5  ","D")+"\n")
    t.write("        "+svgtext("157.5","-29.5  ","E")+"\n")
    t.write("        "+svgtext("157.5","-36.5  ","F")+"\n")
    t.write("        "+svgtext("157.5","-43.5  ","G")+"\n")
    t.write("        "+svgtext("157.5","-50.5  ","H")+"\n")
    t.write("        "+svgtext("157.5","-57.5  ","I")+"\n")
    t.write("      </g>\n")

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

#- Title block editable texts
def CreateEditableText(filePath,shWidth,shHeight):
    #- set frame offsets
    dAB = borders.drawingAreaBottom
    dAR = borders.drawingAreaRight

    #- offsets for editable texts
    edX=int(shWidth)-dAR-180 # 180 according to DIN EN ISO 7200
    edY=int(shHeight)-dAB

    t=open(filePath,"a")
    t.write("    <g id=\"titleblock-editable-owner\"\n")
    t.write("      style=\"font-size:3.5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")

    t.write("      "+FCeditext("Owner",str(edX+60),str(edY-27.0),"Owner")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-address\"\n")
    t.write("      style=\"font-size:2.5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Address-1",str(edX+60),str(edY-23.5),"Address1")+"\n")
    t.write("      "+FCeditext("Address-2",str(edX+60),str(edY-20.0),"Address2")+"\n")
    t.write("      "+FCeditext("MailTo",   str(edX+60),str(edY-16.5),"MailTo")+"\n")
    t.write("      "+FCeditext("Copyright",str(edX+2), str(edY-1.0), "Copyright")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-medium\"\n")
    t.write("      style=\"font-size:5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Author",    str(edX+2),  str(edY-55.0),"Author")+"\n")
    t.write("      "+FCeditext("AuDate",    str(edX+7),  str(edY-47.5),"YYYY/MM/DD")+"\n")
    t.write("      "+FCeditext("Supervisor",str(edX+2),  str(edY-38.5),"Supervisor")+"\n")
    t.write("      "+FCeditext("SvDate",    str(edX+7),  str(edY-31.0),"YYYY/MM/DD")+"\n")
    t.write("      "+FCeditext("SubTitle",  str(edX+64), str(edY-42.0),"Sub-title")+"\n")
    t.write("      "+FCeditext("Version",   str(edX+60), str(edY-8.0), "Version")+"\n")
    t.write("      "+FCeditext("Revision-A",str(edX+162),str(edY-1.5), "______")+"\n")
    t.write("      "+FCeditext("Revision-B",str(edX+162),str(edY-8.5), "______")+"\n")
    t.write("      "+FCeditext("Revision-C",str(edX+162),str(edY-15.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-D",str(edX+162),str(edY-22.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-E",str(edX+162),str(edY-29.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-F",str(edX+162),str(edY-36.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-G",str(edX+162),str(edY-43.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-H",str(edX+162),str(edY-50.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-I",str(edX+162),str(edY-57.5),"______")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-centered\"\n")
    t.write("      style=\"font-size:5;text-anchor:middle;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Format", str(edX+12), str(edY-21),"A3")+"\n")
    t.write("      "+FCeditext("Sheets", str(edX+148),str(edY-8), "1 / 1")+"\n")
    t.write("      "+FCeditext("Scale",  str(edX+12), str(edY-8), "1 : 1")+"\n")
    t.write("      "+FCeditext("Weight", str(edX+35), str(edY-8), "___ kg")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-Large\"\n")
    t.write("      style=\"font-size:7;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Title",   str(edX+62),str(edY-50),"Drawing Title")+"\n")
    t.write("      "+FCeditext("PtNumber",str(edX+62),str(edY-32),"Part Number")+"\n")
    t.write("    </g>\n\n")
    t.close

# --- Main section ---

CreateSvgFile(templatePath+templateName) # overwrites existing File

# Set sheet format (DIN A3)
formatWidth  = "420"
formatHeight = "297"

#borders = ToteBag()
setDINBorderValues('DIN')

StartSvg(templatePath+templateName)  # adds Start tag and namespaces
CreateSheet(templatePath+templateName, formatWidth, formatHeight)
CreateFrame(templatePath+templateName, formatWidth, formatHeight)
CreateDecoration(templatePath+templateName, formatWidth, formatHeight)
CreateTitleBlock(templatePath+templateName, formatWidth, formatHeight)
CreateEditableText(templatePath+templateName, formatWidth, formatHeight)
EndSvg(templatePath+templateName)
# At this point a new SVG-file is generated and saved

The tote bag concept could be used to shorten function calls as the needed parameters could be distributed by global classes...


Future sections:

Manipulate editable texts

Set up a user interface

More results:

Combined code

Generate A3 template and insert it

Just copy this code and paste it into an empty *.py file, edit the template path, save and start the macro from within FreeCAD:

Complete code to create a template and insert it into the active document

#! python
# -*- coding: utf-8 -*-
# (c) 2021 Your name LGPL
#
#
#- set the template path to the directory where your templates are stored
templatePath = "/Users/.../FreeCAD/Templates/"
templateName = "MyTemplate.svg"

# - SVG creation -

class ToteBag:
    pass
# This class is empty to act as a container for several variables
#- one bag for drawing borders
borders = ToteBag()

#- set values according to DIN XXX
def setDINBorderValues(format):
    borders.drawingAreaTop    = 10 # distance from page boundary
    borders.drawingAreaBottom = 10
    borders.drawingAreaLeft   = 20
    borders.drawingAreaRight  = 10
    borders.sheetFrameTop     = 5  # distance from drawing area border
    borders.sheetFrameBottom  = 5
    borders.sheetFrameLeft    = 5
    borders.sheetFrameRight   = 5

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

#- Function to generate 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":
        svgLine=("<path d=\"m "+x1+","+y1+" "+x2+" "+y2+"\" />")
    else:
        svgLine=("<path d=\"m "+x1+","+y1+" l "+x2+","+y2+"\" />")
    return svgLine

#- Function to generate an svg-instruction to place a text element with the given values
def svgtext(posX,posY,strValue):
    svgLine=("<text x=\""+posX+"\" y=\""+posY+"\">"+strValue+"</text>")
    return svgLine

#- Function to generate an svg-instruction to place an editable text element with the given values
def FCeditext(entryName,posX,posY,strValue):
    svgLine=("<text freecad:editable=\""+entryName+"\" x=\""+posX+"\" y=\""+posY \
    +"\">  <tspan>"+strValue+"</tspan>  </text>")
    return svgLine

#- Create a file and insert a header line
def CreateSvgFile(filePath):
    t=open(filePath,"w") # w = write, overwrites existing files
    t.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>")
    t.close

#- Create opening svg-tag
#   Namespace section
def StartSvg(filePath):
    t=open(filePath,"a") # a = append, new lines are added at the end of an existing file
    t.write("\n"+"\n")
    t.write("<svg\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.close
#   Sheet size section
def CreateSheet(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("  width =\""+shWidth+"mm\"\n")
    t.write("  height=\""+shHeight+"mm\"\n")
    t.write("  viewBox=\"0 0 "+shWidth+" "+shHeight+"\">\n")
    t.close
    # identical values for width and height and Viewbox' width and height will synchronise mm and svg-units

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

#- Frame creation
def CreateFrame(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("    <g id=\"drawing-frame\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.5;\
stroke-linecap:round\">\n")
    #- set frame offsets
    dAT = borders.drawingAreaTop
    dAB = borders.drawingAreaBottom
    dAL = borders.drawingAreaLeft
    dAR = borders.drawingAreaRight
    sFT = borders.sheetFrameTop
    sFB = borders.sheetFrameBottom
    sFL = borders.sheetFrameLeft
    sFR = borders.sheetFrameRight
    # inner Frame, drawing area
    #- upper left corner
    drawingX=str(dAL)
    drawingY=str(dAT)
    #- lower right corner
    drawingWidth=str(int(shWidth)-dAR)
    drawingHeight=str(int(shHeight)-dAB)
    t.write("      \n")
    #- frame dimensions
    drawingWidth=str(int(shWidth)-dAL-dAR)
    drawingHeight=str(int(shHeight)-dAT-dAB)
    #- frame rectangle
    t.write("      "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
    # outer frame
    #- upper left corner
    drawingX=str(dAL-sFL)
    drawingY=str(dAT-sFT)
    #- lower right corner
    drawingWidth=str(int(shWidth)-dAR+sFR)
    drawingHeight=str(int(shHeight)-dAB+sFB)
    t.write("      \n")
    #- frame dimensions
    drawingWidth=str(int(shWidth)-dAL-dAR+sFL+sFR)
    drawingHeight=str(int(shHeight)-dAT-dAB+sFT+sFB)
    #- frame rectangle
    t.write("      "+svgrect(drawingWidth,drawingHeight,drawingX,drawingY)+"\n")
    t.write("    </g>\n\n")
    t.close

#- Indexes and folding marks creation
def CreateDecoration(filePath,shWidth,shHeight):
    t=open(filePath,"a")
    t.write("    <g id=\"index-separators\"\n")
    t.write("      style=\"fill:none;stroke:#000000;stroke-width:0.25;\
stroke-linecap:round\">\n")
    #- set frame offsets
    dAT = borders.drawingAreaTop
    dAB = borders.drawingAreaBottom
    dAL = borders.drawingAreaLeft
    dAR = borders.drawingAreaRight
    sFT = borders.sheetFrameTop
    sFB = borders.sheetFrameBottom
    sFL = borders.sheetFrameLeft
    sFR = borders.sheetFrameRight
    drawingX=str(dAL)
    drawingY=str(dAT)
    drawingWidth=str(int(shWidth)-dAL-dAR)
    drawingHeight=str(int(shHeight)-dAT-dAB)

    #- starting point values of center lines
    indexCenter=str(int(drawingWidth)/2+dAL)
    indexMiddle=str(int(drawingHeight)/2+dAT)
    indexLeft=str(dAL+5)
    indexRight=str(int(drawingWidth)+dAL-5)
    indexUpper=str(dAT+5)
    indexLower=str(int(drawingHeight)+dAT-5)

    #- centre and middle markings of drawing area
    if shWidth=="210": # format=="DIN-A4":
        indexLeft=str(dAL)
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-15")+"\n")
    elif shWidth=="420": # format=="DIN-A3":
        indexLeft=str(dAL+5)
        t.write("        "+svgpath(indexCenter,indexUpper,"v","-10")+"\n")
        t.write("        "+svgpath(indexCenter,indexLower,"v"," 10")+"\n")
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-20")+"\n")
        t.write("        "+svgpath(indexRight,indexMiddle,"h"," 10")+"\n")
    else :
        t.write("        "+svgpath(indexCenter,indexUpper,"v","-10")+"\n")
        t.write("        "+svgpath(indexCenter,indexLower,"v"," 10")+"\n")
        t.write("        "+svgpath(indexLeft,indexMiddle,"h","-10")+"\n")
        t.write("        "+svgpath(indexRight,indexMiddle,"h"," 10")+"\n")

    #- starting point values of separator lines
    indexLeft =str(dAL)
    indexRight=str(int(drawingWidth)+dAL)
    indexUpper=str(dAT)
    indexLower=str(int(drawingHeight)+dAT)

    #- set number of horizontal and vertical indexes
    if shWidth=="420": # format=="DIN-A3":
        indexCountX=8
        indexCountY=6
    elif shWidth=="594": # format=="DIN-A2":
        indexCountX=12
        indexCountY=8
    elif shWidth=="841": # format=="DIN-A1":
        indexCountX=16
        indexCountY=12
    elif shWidth=="1189": # format=="DIN-A0":
        indexCountX=24
        indexCountY=16
    else :
        indexCountX=0
        indexCountY=0

    #- indexCenter and indexMiddle contain strings but floating point
    #   numbers are needed to calculate
    floatCenter=int(drawingWidth)/2+dAL
    floatMiddle=int(drawingHeight)/2+dAT

    #- horizontal index separators
    max=int(indexCountX/2-1)
    for value in range(0,max):
        indexX=str(floatCenter+(value+1)*50)
        t.write("        "+svgpath(indexX,indexUpper,"v"," -5")+"\n")
        t.write("        "+svgpath(indexX,indexLower,"v","  5")+"\n")
        indexX=str(floatCenter-(value+1)*50)
        t.write("        "+svgpath(indexX,indexUpper,"v"," -5")+"\n")
        t.write("        "+svgpath(indexX,indexLower,"v","  5")+"\n")

    #- vertical index separators
    max=int(indexCountY/2-1)
    for value in range(0,max):
        indexY=str(floatMiddle+(value+1)*50)
        t.write("        "+svgpath(indexLeft, indexY,"h"," -5")+"\n")
        t.write("        "+svgpath(indexRight,indexY,"h","  5")+"\n")
        indexY=str(floatMiddle-(value+1)*50)
        t.write("        "+svgpath(indexLeft, indexY,"h"," -5")+"\n")
        t.write("        "+svgpath(indexRight,indexY,"h","  5")+"\n")

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

    t.write("    <g id=\"indexes\"\n")
    t.write("      style=\"font-size:3.5;text-anchor:middle;fill:#000000;\
    font-family:osifont\">\n")

    #- position point values of indexes
    indexLeft=str(dAL-sFL/2)
    indexRight=str(int(drawingWidth)+dAL+sFR/2)
    indexUpper=str(dAT-1)
    indexLower=str(int(drawingHeight)+dAT+sFB-1)

    #- horizontal indexes, numbers
    max=int(indexCountX/2)
    for value in range(0,max):
        indexX=str(floatCenter+value*50+25)
        t.write("        "+svgtext(indexX,indexUpper,str(int(indexCountX/2+value+1)))+"\n")
        t.write("        "+svgtext(indexX,indexLower,str(int(indexCountX/2+value+1)))+"\n")
        indexX=str(floatCenter-value*50-25)
        t.write("        "+svgtext(indexX,indexUpper,str(int(indexCountX/2-value)))+"\n")
        t.write("        "+svgtext(indexX,indexLower,str(int(indexCountX/2-value)))+"\n")

    #- vertical indexes, letters
    max=int(indexCountY/2)
    for value in range(0,max):
        indexY=str(floatMiddle+value*50+25)
        if int(indexCountY/2+value+1)>9 :  # to avoid the letter J
            t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2+value+2)))+"\n")
            t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2+value+2)))+"\n")
        else :
            t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2+value+1)))+"\n")
            t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2+value+1)))+"\n")
        indexY=str(floatMiddle-value*50-25)
        t.write("        "+svgtext(indexLeft,indexY,chr(64+int(indexCountY/2-value)))+"\n")
        t.write("        "+svgtext(indexRight,indexY,chr(64+int(indexCountY/2-value)))+"\n")

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

    #- puncher mark
    t.write("    <g id=\"puncher mark\"\n")
    t.write("      style=\"fill:none;stroke:#b0b0b0;stroke-width:0.25;\
    stroke-linecap:miter;stroke-miterlimit:4\">\n")
    if shWidth in["1189", "841", "594"] : # A3 and A4 have extended middle markings
        t.write("        "+svgpath(str(dAL-sFL),str(int(formatHeight)-(297/2)),"h","-10")+"\n")
    t.write("    </g>\n\n")

    #- folding marks
    t.write("    <g id=\"folding marks\"\n")
    t.write("      style=\"fill:none;stroke:#b0b0b0;stroke-width:0.25;\
    stroke-linecap:miter;stroke-miterlimit:4\">\n")
    if shWidth=="420": # DIN-A3
        t.write("        "+svgpath("125",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("125",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("230",str(dAT-sFT),"v","-5")+"\n")
        t.write("        "+svgpath("230",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
    elif shWidth=="594": # DIN-A2
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("402",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("402",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","123","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"123","h","  5")+"\n")
    elif shWidth=="841": # DIN-A1
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("400",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("400",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("651",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("651",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","297","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"297","h","  5")+"\n")
    elif shWidth=="1189": # DIN-A0
        t.write("        "+svgpath("210",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("210",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("400",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("400",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("590",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("590",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("780",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("780",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("999",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("999",str(int(formatHeight)-dAB+sFB),"v","  5")+"\n")
        t.write("        "+svgpath("105",str(dAT-sFT),"v"," -5")+"\n")
        t.write("        "+svgpath("  5","247","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"247","h","  5")+"\n")
        t.write("        "+svgpath("  5","544","h"," -5")+"\n")
        t.write("        "+svgpath(str(int(formatWidth)-dAR+sFR),"544","h","  5")+"\n")

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

#- Title block movable
def CreateTitleBlock(filePath,shWidth,shHeight):
    #- set frame offsets
    dAB = borders.drawingAreaBottom
    dAR = borders.drawingAreaRight

    #- lower left corner
    tbX=str(int(shWidth)-dAR-180) # 180 according to DIN EN ISO 7200
    tbY=str(int(shHeight)-dAB)
    #- group to move allelements in one step
    t=open(filePath,"a")
    t.write("    <g id=\"titleblock\"\n")
    t.write("      transform=\"translate("+tbX+","+tbY+")\">\n")
    t.write("      \n\n")
    #- title block
    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"," -4","h","155")+"\n")
    t.write("        "+svgpath("  0","-16","h","155")+"\n")
    t.write("        "+svgpath("  0","-30","h","155")+"\n")
    t.write("        "+svgpath("  0","-46.5","h"," 50")+"\n")
    t.write("        "+svgpath(" 25"," -4","v","-26")+"\n")
    t.write("        "+svgpath(" 50"," -4","v","-59")+"\n")
    t.write("        "+svgpath("140"," -4","v","-12")+"\n")
    t.write("        "+svgpath("155","  0","v","-63")+"\n")
    t.write("        "+svgpath("160","  0","v","-63")+"\n")
    t.write("        "+svgpath("155"," -7","h"," 25")+"\n")
    t.write("        "+svgpath("155","-14","h"," 25")+"\n")
    t.write("        "+svgpath("155","-21","h"," 25")+"\n")
    t.write("        "+svgpath("155","-28","h"," 25")+"\n")
    t.write("        "+svgpath("155","-35","h"," 25")+"\n")
    t.write("        "+svgpath("155","-42","h"," 25")+"\n")
    t.write("        "+svgpath("155","-49","h"," 25")+"\n")
    t.write("        "+svgpath("155","-56","h"," 25")+"\n")
    t.write("      </g>\n")
    #- small texts, left-aligned
    t.write("      <g id=\"titleblock-text-non-editable\"\n")
    t.write("        style=\"font-size:2.5;text-anchor:start;fill:#000000;\
font-family:osifont\">\n")

    t.write("        "+svgtext("  1.5","-60  ","Author Name:")+"\n")
    t.write("        "+svgtext("  1.5","-52  ","Date:")+"\n")
    t.write("        "+svgtext("  1.5","-43.5 ","Supervisor Name:")+"\n")
    t.write("        "+svgtext("  1.5","-35.5 ","Date:")+"\n")
    t.write("        "+svgtext("  1.5","-27  ","Format:")+"\n")
    t.write("        "+svgtext("  1.5","-13  ","Scale:")+"\n")
    t.write("        "+svgtext(" 26.5","-13  ","Weight:")+"\n")
    t.write("        "+svgtext(" 51.5","-27  ","Owner:")+"\n")
    t.write("        "+svgtext(" 51.5","-13  ","Version:")+"\n")
    t.write("        "+svgtext("141.5","-13  ","Sheet")+"\n")
    t.write("      </g>\n")
    #- revision indexes, centered
    t.write("      <g id=\"titleblock-revision-indexes\"\n")
    t.write("        style=\"font-size:5.0;text-anchor:middle;fill:#000000;\
font-family:osifont\">\n")
    t.write("        "+svgtext("157.5"," -1.5  ","A")+"\n")
    t.write("        "+svgtext("157.5"," -8.5  ","B")+"\n")
    t.write("        "+svgtext("157.5","-15.5  ","C")+"\n")
    t.write("        "+svgtext("157.5","-22.5  ","D")+"\n")
    t.write("        "+svgtext("157.5","-29.5  ","E")+"\n")
    t.write("        "+svgtext("157.5","-36.5  ","F")+"\n")
    t.write("        "+svgtext("157.5","-43.5  ","G")+"\n")
    t.write("        "+svgtext("157.5","-50.5  ","H")+"\n")
    t.write("        "+svgtext("157.5","-57.5  ","I")+"\n")
    t.write("      </g>\n")

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

#- Title block editable texts
def CreateEditableText(filePath,shWidth,shHeight):
    #- set frame offsets
    dAB = borders.drawingAreaBottom
    dAR = borders.drawingAreaRight

    #- offsets for editable texts
    edX=int(shWidth)-dAR-180 # 180 according to DIN EN ISO 7200
    edY=int(shHeight)-dAB

    t=open(filePath,"a")
    t.write("    <g id=\"titleblock-editable-owner\"\n")
    t.write("      style=\"font-size:3.5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")

    t.write("      "+FCeditext("Owner",str(edX+60),str(edY-27.0),"Owner")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-address\"\n")
    t.write("      style=\"font-size:2.5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Address-1",str(edX+60),str(edY-23.5),"Address1")+"\n")
    t.write("      "+FCeditext("Address-2",str(edX+60),str(edY-20.0),"Address2")+"\n")
    t.write("      "+FCeditext("MailTo",   str(edX+60),str(edY-16.5),"MailTo")+"\n")
    t.write("      "+FCeditext("Copyright",str(edX+2), str(edY-1.0), "Copyright")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-medium\"\n")
    t.write("      style=\"font-size:5;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Author",    str(edX+2),  str(edY-55.0),"Author")+"\n")
    t.write("      "+FCeditext("AuDate",    str(edX+7),  str(edY-47.5),"YYYY/MM/DD")+"\n")
    t.write("      "+FCeditext("Supervisor",str(edX+2),  str(edY-38.5),"Supervisor")+"\n")
    t.write("      "+FCeditext("SvDate",    str(edX+7),  str(edY-31.0),"YYYY/MM/DD")+"\n")
    t.write("      "+FCeditext("SubTitle",  str(edX+64), str(edY-42.0),"Sub-title")+"\n")
    t.write("      "+FCeditext("Version",   str(edX+60), str(edY-8.0), "Version")+"\n")
    t.write("      "+FCeditext("Revision-A",str(edX+162),str(edY-1.5), "______")+"\n")
    t.write("      "+FCeditext("Revision-B",str(edX+162),str(edY-8.5), "______")+"\n")
    t.write("      "+FCeditext("Revision-C",str(edX+162),str(edY-15.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-D",str(edX+162),str(edY-22.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-E",str(edX+162),str(edY-29.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-F",str(edX+162),str(edY-36.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-G",str(edX+162),str(edY-43.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-H",str(edX+162),str(edY-50.5),"______")+"\n")
    t.write("      "+FCeditext("Revision-I",str(edX+162),str(edY-57.5),"______")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-centered\"\n")
    t.write("      style=\"font-size:5;text-anchor:middle;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Format", str(edX+12), str(edY-21),"A3")+"\n")
    t.write("      "+FCeditext("Sheets", str(edX+148),str(edY-8), "1 / 1")+"\n")
    t.write("      "+FCeditext("Scale",  str(edX+12), str(edY-8), "1 : 1")+"\n")
    t.write("      "+FCeditext("Weight", str(edX+35), str(edY-8), "___ kg")+"\n")
    t.write("    </g>\n")

    t.write("    <g id=\"titleblock-editable-Large\"\n")
    t.write("      style=\"font-size:7;text-anchor:start;fill:#0000d0;\
font-family:osifont\">\n")
    t.write("      "+FCeditext("Title",   str(edX+62),str(edY-50),"Drawing Title")+"\n")
    t.write("      "+FCeditext("PtNumber",str(edX+62),str(edY-32),"Part Number")+"\n")
    t.write("    </g>\n\n")
    t.close

# --- Main section ---

CreateSvgFile(templatePath+templateName) # overwrites existing File

# Set sheet format (DIN A3)
formatWidth  = "420"
formatHeight = "297"

#borders = ToteBag()
setDINBorderValues('DIN')

StartSvg(templatePath+templateName)  # adds Start tag and namespaces
CreateSheet(templatePath+templateName, formatWidth, formatHeight)
CreateFrame(templatePath+templateName, formatWidth, formatHeight)
CreateDecoration(templatePath+templateName, formatWidth, formatHeight)
CreateTitleBlock(templatePath+templateName, formatWidth, formatHeight)
CreateEditableText(templatePath+templateName, formatWidth, formatHeight)
EndSvg(templatePath+templateName)
# At this point a new SVG-file is generated and saved
# and shall be attached to the active document

# -- Add a page and a template to the active document --

activeDoc = App.activeDocument()

# add a page to the active document
newPage = 'Page01' # first page name to start search

# to find the current page number to create
pageCount = 1
pageFound = True
while pageFound:
    try:
        print(activeDoc.getObject(newPage).Label)
    except AttributeError:
        print(newPage + " to be created")
        pageFound = False
    else:
        pageCount = pageCount + 1
        if pageCount < 10:
            newPage = newPage[0:4] + '0' + str(pageCount)
        else:
            newPage = newPage[0:4] + str(pageCount)

# set template number according to page number
newTemplate = ('Template' + newPage[4:])

# add a page object to the active document
activeDoc.addObject('TechDraw::DrawPage',newPage)

# add a template object to the active document
activeDoc.addObject('TechDraw::DrawSVGTemplate',newTemplate)

# load the svg template into the template object
activeDoc.getObject(newTemplate).Template = templatePath+templateName

# add the template object to the page's object list
activeDoc.getObject(newPage).Template = activeDoc.getObject(newTemplate)

# rename 'Page' to 'Sheet'
activeDoc.getObject(newPage).Label = 'Sheet ' + newPage[4:]

# open the page object for editing
activeDoc.getObject(newPage).ViewObject.doubleClicked()



This is a sandbox / Dies ist eine Sandbox