Generating SolidWorks drawings manually — open part, new drawing, place views, fill title block, insert BOM, save PDF — is fine for a handful of parts. At scale it becomes a bottleneck. The SolidWorks API exposes every step of that workflow, and a well-structured macro or add-in can generate complete drawing packages for an entire assembly in minutes.
This post covers the full pipeline: creating drawing documents from a template, adding model views, populating title block annotations from custom properties, inserting BOMs, and exporting to PDF. Both VBA (for quick scripts) and C# (for production add-ins) approaches are shown.
The Pipeline
The generation workflow has six discrete steps that map directly to API calls:
- Create the drawing document from a template
- Insert model views (standard orthographic views, flat pattern view for sheet metal)
- Auto-size and auto-align views on the sheet
- Populate title block annotations from the part’s custom properties
- Insert BOM (if the model is an assembly)
- Export to PDF and optionally to DXF
Each step is independent enough that you can automate them individually. Start with whatever is consuming the most manual time.
Step 1: Creating the Drawing Document
' VBA: Create a new drawing from a template
Sub CreateDrawingFromTemplate()
Dim swApp As SldWorks.SldWorks
Dim swDraw As SldWorks.DrawingDoc
Dim drawPath As String
Set swApp = Application.SldWorks
' Path to your drawing template (.drwdot)
Dim templatePath As String
templatePath = "C:\SolidWorks\Templates\A3_Template.drwdot"
' NewDocument creates any document type from a template
' Arguments: templatePath, paperSize (0=A-size), width(m), height(m)
drawPath = swApp.GetUserPreferenceStringValue(swUserPreferenceStringValue_e.swFileLocationsDocumentTemplates)
Set swDraw = swApp.NewDocument(templatePath, 0, 0, 0)
If swDraw Is Nothing Then
MsgBox "Failed to create drawing — check template path"
Exit Sub
End If
' swDraw is now an active empty drawing with one sheet
Debug.Print "Created drawing: " & swDraw.GetPathName()
End Sub
The NewDocument call creates the drawing and activates it. The third and fourth arguments (width, height) are ignored when you provide a template; the template’s sheet format defines the paper size.
In C# (for add-ins):
// C#: Create drawing from template
ISldWorks swApp = /* your ISldWorks instance */;
string templatePath = @"C:\SolidWorks\Templates\A3_Template.drwdot";
IModelDoc2 drawDoc = (IModelDoc2)swApp.NewDocument(templatePath, 0, 0, 0);
IDrawingDoc swDraw = (IDrawingDoc)drawDoc;
Selecting the Right Template
If you need to select a template programmatically based on part characteristics (paper size for a large assembly vs small part), use the API to query part bounding box and decide:
// Determine template based on approximate sheet metal blank size
IBody2 flatBody = GetFlatPatternBody(partDoc); // your helper
double[] bbox = (double[])flatBody.GetBodyBox();
double width = bbox[3] - bbox[0]; // metres
double height = bbox[4] - bbox[1];
string template = (width > 0.42 || height > 0.30)
? @"Templates\A2_Template.drwdot"
: @"Templates\A3_Template.drwdot";
Step 2: Inserting Model Views
' VBA: Insert a standard three-view layout from a part file
Sub InsertViews(swDraw As SldWorks.DrawingDoc, partPath As String)
Dim swSheet As SldWorks.Sheet
Dim swView As SldWorks.View
Set swSheet = swDraw.GetCurrentSheet()
' InsertModelView3 inserts the first view
' Arguments: modelPath, viewName, scale_type (1=custom), scale, x, y, alignH, error
Dim errors As Long
Set swView = swDraw.CreateDrawViewFromModelView3( _
partPath, _
"*Front", _ ' standard named view
0.5, 0.5, _ ' x, y position in metres from sheet origin
errors)
If swView Is Nothing Then
Debug.Print "View insert failed, error: " & errors
Exit Sub
End If
' The first view sets the scale. Projected views use the same scale
' swView.ScaleDecimal = 0.5 ' 1:2
' Project right view from front
Dim swRightView As SldWorks.View
swDraw.ActivateView swView.Name
Set swRightView = swDraw.CreateUnfoldedView( _
swView.Name, _
0.5 + 0.15, 0.5, _ ' offset 150mm to the right
errors)
End Sub
For sheet metal flat pattern views, use the *Flat Pattern named view string:
Set swView = swDraw.CreateDrawViewFromModelView3( _
partPath, _
"*Flat Pattern", _
0.1, 0.1, _
errors)
The flat pattern view requires the part’s Flat-Pattern feature to be unsuppressed. If it is suppressed, the view will insert as an empty frame — no geometry. Check IFeature.Suppressed on the flat pattern feature before inserting.
Auto-Scale Views to Fit
Manually choosing a scale for each part is fragile. A better approach is to insert at scale 1:1, get the view bounding box, then rescale to fit within the sheet’s drawing area:
// C#: Auto-scale a view to fit within a target region
void AutoScaleView(IView view, double maxWidthM, double maxHeightM)
{
double[] bbox = (double[])view.GetOutline();
// bbox: [minX, minY, maxX, maxY] in sheet coordinates (metres)
double viewW = bbox[2] - bbox[0];
double viewH = bbox[3] - bbox[1];
double scaleW = maxWidthM / viewW;
double scaleH = maxHeightM / viewH;
double scale = Math.Min(scaleW, scaleH) * 0.9; // 10% margin
// Round to nearest standard scale (1:1, 1:2, 1:5, 1:10, etc.)
double[] stdScales = { 1.0, 0.5, 0.25, 0.2, 0.1, 0.05 };
scale = stdScales.OrderBy(s => Math.Abs(s - scale)).First();
view.ScaleDecimal = scale;
}
Step 3: Populating the Title Block
The title block in a SOLIDWORKS drawing template is a sheet format (.sldfrm). Editable fields in the title block are note annotations linked to custom properties via the $PRPSHEET: and $PRPMODEL: syntax. When the property exists on the referenced model, the annotation displays it automatically.
If the notes are not linked (they are static text in the template), you must update them programmatically:
' VBA: Find a note by its text content and replace it
Sub FillTitleBlockNote(swDraw As SldWorks.DrawingDoc, _
searchText As String, newValue As String)
Dim swSheet As SldWorks.Sheet
Set swSheet = swDraw.GetCurrentSheet()
' EditTemplate opens the sheet format for editing
swDraw.EditTemplate
Dim noteCount As Long
Dim notes() As String
noteCount = swSheet.GetAnnotationCount()
Dim swAnn As SldWorks.Annotation
Dim i As Long
For i = 1 To noteCount
' GetAnnotations returns IDispatch array — iterate differently
Next i
' Better: iterate via FeatureManager annotations
Dim swNote As SldWorks.Note
Dim swFeat As SldWorks.Feature
Set swFeat = swDraw.FirstFeature()
Do While Not swFeat Is Nothing
If swFeat.GetTypeName2() = "BlockDef" Then
' skip block defs
Else
Dim swAnn2 As SldWorks.Annotation
Set swAnn2 = swFeat.GetFirstAnnotation2()
Do While Not swAnn2 Is Nothing
If swAnn2.GetType() = swAnnotationType_e.swNote Then
Set swNote = swAnn2.GetSpecificAnnotation()
If InStr(swNote.GetText(), searchText) > 0 Then
swNote.SetText newValue
End If
End If
Set swAnn2 = swAnn2.GetNext2()
Loop
End If
Set swFeat = swFeat.GetNextFeature()
Loop
swDraw.EditSheet
End Sub
The more robust approach is to use the $PRPSHEET: link in the template itself, and then just set the custom property on the drawing document:
' VBA: Set drawing-level custom property (used by $PRPSHEET: links in title block)
Sub SetDrawingProperty(swDraw As SldWorks.ModelDoc2, propName As String, propValue As String)
Dim swCustMgr As SldWorks.CustomPropertyManager
Set swCustMgr = swDraw.Extension.CustomPropertyManager("") ' "" = top-level document
' Add or update
Dim result As Long
result = swCustMgr.Add3(propName, swCustomInfoType_e.swCustomInfoText, propValue, _
swCustomPropertyAddOption_e.swCustomPropertyReplaceValue)
End Sub
' Usage: mirror model custom properties into the drawing
Dim swPartDoc As SldWorks.PartDoc
Set swPartDoc = swApp.OpenDoc6(partPath, swDocumentTypes_e.swDocPART, _
swOpenDocOptions_e.swOpenDocOptions_Silent, "", 0, 0)
Dim partCustMgr As SldWorks.CustomPropertyManager
Set partCustMgr = swPartDoc.Extension.CustomPropertyManager("")
Dim propNames() As String, propValues() As String, propTypes() As Long
partCustMgr.GetAll2 propNames, propTypes, propValues
Dim j As Long
For j = 0 To UBound(propNames)
SetDrawingProperty swDraw, propNames(j), propValues(j)
Next j
This pulls every custom property from the part (Material, PartNumber, Revision, Finish, etc.) and sets it on the drawing document. Any $PRPSHEET:PartNumber link in the title block will then display the correct value without manual entry.
Step 4: Inserting the BOM
For assemblies, the BOM is the most time-consuming manual step. The API inserts a BOM table and gives you access to every cell.
' VBA: Insert a BOM into a drawing and export it
Sub InsertAndExportBOM(swDraw As SldWorks.DrawingDoc)
Dim swSheet As SldWorks.Sheet
Set swSheet = swDraw.GetCurrentSheet()
' Get the first view (assembly view to attach BOM to)
Dim views() As String
views = swSheet.GetViewNames()
Dim swView As SldWorks.View
Set swView = swDraw.GetFirstView()
Dim swTableAnnotation As SldWorks.TableAnnotation
' InsertTableAnnotation2: view, anchorType, bomType, x, y, config, filename, tableStyle
Set swTableAnnotation = swDraw.InsertTableAnnotation2( _
False, _ ' don't use auto anchor
swBOMConfigurationAnchor_e.swBOMConfigAnchor_TopLeft, _
swBomType_e.swBomType_Indented, _ ' indented BOM
0.05, 0.05, _ ' x, y on sheet (metres)
swView.ReferencedDocument, _ ' assembly doc
"", _ ' use active config
"") ' use default table template
If swTableAnnotation Is Nothing Then
Debug.Print "BOM insert failed"
Exit Sub
End If
' Iterate rows and columns to verify / fix data
Dim rowCount As Long, colCount As Long
rowCount = swTableAnnotation.RowCount
colCount = swTableAnnotation.ColumnCount
Dim r As Long, c As Long
For r = 0 To rowCount - 1
For c = 0 To colCount - 1
Dim cellText As String
cellText = swTableAnnotation.Text(r, c)
' cellText is the display value for this cell
Next c
Next r
' Export BOM to CSV
Dim csvPath As String
csvPath = "C:\Output\BOM_" & swView.ReferencedDocument.GetTitle() & ".csv"
swTableAnnotation.SaveAsFile csvPath
End Sub
InsertTableAnnotation2 is the correct API for programmatic BOM insertion. Note that IDrawingDoc.InsertBom3 is deprecated in recent versions and InsertTableAnnotation2 is the replacement.
Customizing columns before inserting: Use ITableAnnotation.SetColumnTitle and ITableAnnotation.SetColumnWidth after insertion to adjust the layout. Column width is in metres.
Step 5: Exporting to PDF
' VBA: Export current drawing to PDF
Sub ExportToPDF(swDraw As SldWorks.ModelDoc2, outputPath As String)
Dim swExportPDFData As SldWorks.ExportPdfData
Set swExportPDFData = swApp.GetExportFileData(swExportDataFileType_e.swExportPdf)
' Export all sheets
swExportPDFData.SetSheets swSheetsToExport_e.swExportData_ExportAllSheets, Nothing
swExportPDFData.ViewPdfAfterSaving = False
Dim errors As Long, warnings As Long
Dim result As Boolean
result = swDraw.Extension.SaveAs3(outputPath, 0, 0, swExportPDFData, Nothing, errors, warnings)
If Not result Then
Debug.Print "PDF export failed: error=" & errors & ", warnings=" & warnings
End If
End Sub
SaveAs3 with an ExportPdfData object is the correct call for PDF export. The older SaveAs overloads without the data argument do not give you control over sheet selection or PDF quality settings.
Error codes from SaveAs3 worth knowing:
errors = 2: model contains suppressed features that affect the drawing — rebuild firsterrors = 9: the output path does not exist or is read-onlyerrors = 12: requires a full install of SolidWorks (not a viewer) — hit when running headless via Task Scheduler without a licensed installation
Putting It Together: Batch Generation Script
The complete batch pipeline iterates a list of part files, creates a drawing for each, and exports PDF and DXF:
Sub BatchGenerateDrawings()
Dim partFiles() As String
partFiles = Array( _
"C:\Parts\BracketA.SLDPRT", _
"C:\Parts\PlateB.SLDPRT", _
"C:\Parts\ChannelC.SLDPRT")
Dim i As Long
For i = 0 To UBound(partFiles)
Dim partPath As String
partPath = partFiles(i)
' Open part silently
Dim errors As Long, warnings As Long
Dim swPart As SldWorks.ModelDoc2
Set swPart = swApp.OpenDoc6(partPath, _
swDocumentTypes_e.swDocPART, _
swOpenDocOptions_e.swOpenDocOptions_Silent, _
"", errors, warnings)
If swPart Is Nothing Then GoTo NextPart
' Create drawing
Dim swDraw As SldWorks.DrawingDoc
' ... (CreateDrawingFromTemplate, InsertViews calls)
' Populate title block
' ... (SetDrawingProperty calls)
' Export
Dim baseName As String
baseName = "C:\Output\" & Left(Dir(partPath), Len(Dir(partPath)) - 7)
ExportToPDF swDraw, baseName & ".pdf"
' Save drawing
swDraw.Save3 swSaveAsOptions_e.swSaveAsOptions_Silent, errors, warnings
' Close part and drawing
swApp.CloseDoc swDraw.GetPathName()
swApp.CloseDoc partPath
NextPart:
Next i
End Sub
The key discipline here is opening parts in Silent mode (swOpenDocOptions_Silent). Without it, SOLIDWORKS shows UI for missing references, update prompts, and warnings — the script blocks and requires manual intervention. Silent mode suppresses all UI, so the script runs unattended. If a part has unresolved references in silent mode, it opens with lightweight components; add a CompletelyResolve call before creating the drawing view.
Task Scheduler vs API for Unattended Runs
If you want this to run overnight or on a schedule, SOLIDWORKS Task Scheduler macros and how to pass folder paths correctly covers the setup. Task Scheduler launches SOLIDWORKS headlessly and runs a macro — the same VBA above, pointed at a folder rather than a hardcoded array.
For more complex drawing automation (Property Manager Pages, event-driven generation as parts are saved), the upgrade path to a C# add-in is covered in when to upgrade from a SolidWorks macro to a full add-in.
The Manual Steps This Does Not Replace
The API cannot reliably auto-dimension views (dimension placement is a complex spatial problem and the API does not expose a “smart” dimensioning algorithm). Title block fields that require judgment (drawing notes, tolerance block overrides, non-standard revision notes) still need manual review. The pipeline above gets you from zero to a dimensioned-free drawing with views, title block data, and BOM in the correct format — the final review pass takes seconds instead of minutes.
For teams that export DXF flat patterns alongside drawings, CadShift’s batch export handles the DXF side in parallel with the drawing generation — the same part list drives both outputs with consistent naming and layer mapping.