Batch Engineering Drawings with FreeCAD Python

Automating the creation of 2D technical drawings from 3D CAD models saves significant engineering time and eliminates repetitive manual drafting. This article explains how to use FreeCAD's headless Python environment and the TechDraw workbench to batch-process 3D models (such as STEP or native FCStd files), apply standard drawing templates, insert projected orthographic views, and export production-ready PDF or DXF drawings automatically.

Understanding the FreeCAD TechDraw API

FreeCAD includes a dedicated drawing module called TechDraw. When driving FreeCAD via Python, the core components required to generate a drawing include:

  • TechDraw::DrawPage: The container object representing the sheet.
  • TechDraw::DrawSVGTemplate: The title block and sheet border loaded from an SVG file.
  • TechDraw::DrawProjGroup or TechDraw::DrawViewPart: Objects that calculate 2D hidden-line-removal (HLR) projections from 3D geometry.

Preparing the Automation Environment

FreeCAD can execute scripts without launching the graphical interface using the freecadcmd binary (or FreeCADCmd.exe on Windows). Ensure your system paths point to FreeCAD's bin and lib directories so Python can import the FreeCAD, Part, and TechDraw modules.

The Batch Drawing Script

The following script iterates through a folder of 3D models, loads each part, applies an SVG drawing template, generates standard third-angle projection views (Front, Top, Right, and Isometric), and saves the result as a PDF.

import os
import FreeCAD
import Part
import TechDraw

# Configuration
INPUT_DIR = "/path/to/cad_models"
OUTPUT_DIR = "/path/to/output_drawings"
TEMPLATE_PATH = "/usr/share/freecad/Mod/TechDraw/Templates/A4_Landscape_TD.svg"

os.makedirs(OUTPUT_DIR, exist_ok=True)

def process_file(file_path):
    filename = os.path.basename(file_path)
    base_name, ext = os.path.splitext(filename)
    
    # Create a fresh FreeCAD document
    doc = FreeCAD.newDocument(base_name)
    
    # Load 3D geometry
    if ext.lower() in ['.step', '.stp']:
        shape = Part.read(file_path)
        part_feature = doc.addObject("Part::Feature", "ImportedPart")
        part_feature.Shape = shape
    elif ext.lower() == '.fcstd':
        doc.restore(file_path)
        # Find the first solid feature in the file
        part_feature = None
        for obj in doc.Objects:
            if hasattr(obj, "Shape") and obj.Shape.Solids:
                part_feature = obj
                break
        if not part_feature:
            print(f"Skipping {filename}: No solid geometry found.")
            FreeCAD.closeDocument(doc.Name)
            return
    else:
        FreeCAD.closeDocument(doc.Name)
        return

    doc.recompute()

    # Create TechDraw Page
    page = doc.addObject('TechDraw::DrawPage', 'Page')
    template = doc.addObject('TechDraw::DrawSVGTemplate', 'Template')
    template.Template = TEMPLATE_PATH
    page.Template = template

    # Create Projection Group (Multi-view)
    proj_group = doc.addObject('TechDraw::DrawProjGroup', 'ProjGroup')
    page.addView(proj_group)
    proj_group.Source = [part_feature]
    proj_group.ProjectionType = "ThirdAngle"

    # Define Views: Front, Top, Right, Isometric
    proj_group.addProjectionType("Front")
    proj_group.addProjectionType("Top")
    proj_group.addProjectionType("Right")
    proj_group.addProjectionType("Iso")

    # Layout configuration
    proj_group.ScaleType = "Automatic"
    proj_group.X = 140.0
    proj_group.Y = 100.0

    doc.recompute()

    # Export Drawing to PDF
    output_pdf = os.path.join(OUTPUT_DIR, f"{base_name}.pdf")
    TechDraw.writePageAsPdf(page, output_pdf)
    
    # Optional DXF Export:
    # TechDraw.writePageAsDxf(page, os.path.join(OUTPUT_DIR, f"{base_name}.dxf"))

    print(f"Generated drawing: {output_pdf}")
    FreeCAD.closeDocument(doc.Name)

def run_batch():
    valid_extensions = ('.step', '.stp', '.fcstd')
    files = [f for f in os.listdir(INPUT_DIR) if f.lower().endswith(valid_extensions)]
    
    for f in files:
        full_path = os.path.join(INPUT_DIR, f)
        process_file(full_path)

if __name__ == "__main__":
    run_batch()

Running the Automation Headless

To run the automation script headlessly from the terminal or a continuous integration (CI) pipeline, execute:

freecadcmd batch_drawings.py

On Windows, use:

"C:\Program Files\FreeCAD 0.21\bin\FreeCADCmd.exe" batch_drawings.py

Customizing Output Parameters

  1. Drawing Scale: By default, setting proj_group.ScaleType = "Automatic" allows FreeCAD to fit components to the sheet. For fixed scale (such as 1:2), set proj_group.ScaleType = "Custom" followed by proj_group.Scale = 0.5.
  2. Hidden Lines: Toggle hidden line rendering on views using proj_group.HardHidden = True or accessing individual sub-views via proj_group.views['Front'].HardHidden = True.
  3. Title Block Metadata: Modify template text fields dynamically by accessing template editable texts through template.setEditableText("Author", "Automated Script").