Draw from Template
Draw from Template
Use an existing imports DWG as the base, create a design drawing, and clone template entities. On DWG export the backend opens the template DWG, ODA-clones clone_ entities, and overlays only changed fields — so the template’s fonts, linetypes, and hatch patterns are preserved even when the frontend falls back because fonts were not loaded.
Use Cases
- Auto drawing: sections, columns, tables, and other batch outputs
- Need exact match with real fonts / custom linetypes / complex hatch patterns in the template DWG
- Same semantics as vjmap
from+cloneObjectId/cloneFromDb
Core Concepts
| Concept | Description |
|---|---|
| Template | Must be imports type, and the server must still keep the source DWG (hasDwg === true) |
from | Top-level document field, format mapid/version; used to open the template DWG on export |
isClearFromDb | true means clear original template entities after cloning on export, leaving only your content |
clone_<handle> | Same-template clone marker |
clone_<handle>_<mapid>_<ver> | Cross-template (cross-DB) clone marker |
Online Examples
| Example | Description | Link |
|---|---|---|
| New drawing from template | openFromTemplate + designs export | Online Demo{target="_blank"} |
| Clone template entity | Basic cloneEntity usage | Online Demo{target="_blank"} |
| Clone template text | Keep template fonts | Online Demo{target="_blank"} |
| Clone template hatch | Replace loops only; pattern unchanged | Online Demo{target="_blank"} |
| Draw from template | Section sketch + cross-template clone | Online Demo{target="_blank"} |
| Auto-generate section from data | Counterpart of vjmap 03datatodwgmap | Online Demo{target="_blank"} |
Commands and UI
| Command | Alias | Description |
|---|---|---|
NEWFROMTEMPLATE | NFT | Dialog to pick a template; import whole drawing or styles only |
CLONEFROMTEMPLATE | CFT | Pick entities from template, interactively set insert point, clone into current drawing |
await Engine.editor.executerWithOp('NEWFROMTEMPLATE');API: TemplateService
const { TemplateService } = vjcad;Open a drawing from template (UI editing)
// keepEntities: false → keep only layers/linetypes/fonts/hatches/block defs → isClearFromDb: true
// keepEntities: true → import whole drawing including template entities
const doc = await TemplateService.openFromTemplate({
mapid: 'template_sect',
version: 'v1',
keepEntities: false,
name: 'My design'
});
console.log(doc.templateFrom); // "template_sect/v1"
console.log(doc.isClearFromDb); // trueCreate a document programmatically (not shown on screen)
const doc = await TemplateService.createDocFromTemplate({
mapid: 'template_sect',
version: 'v1',
keepEntities: false
});
// After building entities yourself
const json = doc.toDb(); // top level includes from / isClearFromDbLoad template and clone entities
const template = await TemplateService.loadTemplate('template_sect', 'v1');
// Clone by handle; props not listed keep template values
// Cloned entities are not in the drawing yet — call Engine.addEntities yourself
const text = template.cloneEntity('96A0', {
insertionPoint: [100, 200]
});
text.text = 'Borehole name'; // MTEXT uses text.contents
Engine.addEntities(text);cloneEntity returns an object of the same type as the template entity. Height, font, linetype, and hatch pattern not listed in props stay as in the template. To translate a clone, move(from, to) is more generic than editing geometry props per type:
const cloned = template.cloneEntity('96A0');
cloned.move([0, 0], [50, 0]); // shift right by 50When cloning the same handle many times, the template entity is parsed once, and export also does one cross-DB clone and reuses the prototype — good for patterned hatches repeated hundreds of times:
const hatches = template.cloneEntities([
{ handle: '96BB' },
{ handle: '96BB' },
{ handle: '96BB', props: { patternScale: 1.5 } }
]);
// Hatches: replace loops only; pattern and angle stay from template
hatches.forEach((hatch, i) => hatch.setLoops(loopsList[i]));
Engine.addEntities(hatches);Edit template entities in place
With keepEntities: true, template entities are loaded and objectId stays the original handle (no clone_ prefix). On export these go through in-place modify, not clone:
const doc = await TemplateService.openFromTemplate({
mapid: 'template_sect', version: 'v1', keepEntities: true
});
const entity = doc.findByObjectId('96A0');
entity.text = 'Rewrite existing template text';
entity.setModified();Other methods
| Method | Description |
|---|---|
buildDocDataFromTemplate(options) | Returns vcad JSON with from; does not build a document |
openInView(doc) | Open an already-built CadDocument in the UI |
clearCache(mapid?, version?) | Clear template cache; omit args to clear all; call after switching workspace |
template.from | Template ref, e.g. "template_sect/v1" |
template.handles | List of cloneable handles |
template.getEntity(handle) | Read-only template entity — do not mutate |
template.doc / template.rawJson | Template document / raw vcad JSON |
template.buildInsertDoc(handles) | Mini document for SymbolInteractiveInserter interactive insert |
How to get handles
In real projects handles are usually agreed up front (select a template entity in the platform and read its objectId). To find them in code, walk template.rawJson.dbBlocks['*Model'].items — coordinates are [x, y] arrays, convenient for filtering by position, layer, or type.
API: setCloneSource
When drawing programmatically without TemplateHandle.cloneEntity, mark the clone source yourself:
entity.setCloneSource(sourceHandle); // → clone_<handle>
entity.setCloneSource(sourceHandle, mapid, version); // → clone_<handle>_<mapid>_<version>On export the backend ODA-clones from that source drawing.
Export DWG
Design export must use type: 'designs'. Top-level from is written automatically by CadDocument.toDb():
const { DrawingManagerService } = vjcad;
const result = await new DrawingManagerService().exportToDwg({
type: 'designs',
webcadJson: JSON.stringify(Engine.currentDoc.toDb()),
designPath: `auto/${Date.now()}`,
branch: 'main',
isZoomExtents: true,
useCache: false
});Backend flow (summary):
- Open the template DWG as the working DB using
from; fail hard if it cannot open (no silent empty drawing) - Process
clone_entities: same-DBdeepCloneor cross-DBwblockClone, then overlay only changed fields - Entities without a
clone_prefix but with a non-emptyobjectIdare treated as in-place edits of template entities - If
isClearFromDb, delete original template entities only after cloning (must be after clone, or the clone source is gone) - Entities with empty
objectIdare created as normal new entities
With keepEntities: true (do not clear template entities), the first export uses the template’s own base.vcad as the diff baseline, so the DWG only reflects your changes. With keepEntities: false an empty baseline is used and all entities count as new.
Choosing usable templates
The template must still have its source DWG. Imports items from listWebcadDraws() include a hasDwg field:
const drawingManager = new DrawingManagerService();
const [res, serverMaps] = await Promise.all([
drawingManager.listWebcadDraws(),
drawingManager.listServerMaps()
]);
const registered = new Set((serverMaps || []).map(m => m.mapid));
const usable = (res.imports || []).filter(d => {
if (d.hasDwg === false) return false;
if (d.hasDwg === true) return true;
return registered.has(d.mapid); // older servers
});If the server purged the original DWG, re-run IMPORTDWG, or configure noAutoDeleteDwgFile to keep source files.
Text Size Measurement
Automated drawing often needs laid-out text size before computing row heights or block positions. Use measureTexts:
const { measureTexts } = vjcad;
const results = await measureTexts([
// With clone source → server (real template fonts via ODA)
{ text: 'Silty clay', height: 2.5, width: 30, cloneSource: 'clone_96BA_template_sect_v1' },
// No clone source → local WASM
{ text: 'Plain text', height: 3, type: 'TEXT', styleName: 'Standard' }
]);
const [first] = results;
console.log(first.width, first.height, first.geomHeight, first.source);Each result includes width / height, geomWidth / geomHeight, bounds, source ('server' | 'local'), and optional isFallbackFont. Failed items return null without aborting the batch.
Default mode: 'auto' routes by source (cloneSource / templateMapId → server). Force with mode: 'local' | 'server'. Demo: Measure text size{target="_blank"}.
For full API details see Text Width / Height Measurement.
Mapping to vjmap
| vjmap | WebCAD |
|---|---|
doc.from = 'mapid/ver' | TemplateService.openFromTemplate → doc.templateFrom |
isClearFromDb: true | keepEntities: false → doc.isClearFromDb |
cloneObjectId: '96A0' | template.cloneEntity('96A0', props) |
cloneObjectId + cloneFromDb | Cross-template auto-writes clone_<h>_<mapid>_<ver> |
Notes
- Templates always read
patchId: 'base'so frontend display matches the DWG used on export - Do not change inherited fields like
styleName/patternNameon clones, or export will overwrite them too - Cloned entities are not in the drawing yet — call
Engine.addEntitiesyourself - CUSTOM / GROUP and some types may fall back to normal create
- Cross-DB same-name blocks default to ignore conflicts (
kDrcIgnore)
FAQ
Export error Failed to open template xxx/v1
The server purged that drawing’s source DWG, or the version is missing from mapinfos. Re-run IMPORTDWG, or set noAutoDeleteDwgFile.
Fonts / hatch patterns changed after export
Check whether you explicitly passed styleName, patternName, patternScale, etc. in props. Listed fields override the template; omit them to inherit.
Clone looks fine on canvas but cannot be selected
Early SDK versions left cloned id empty so they never entered the spatial index — upgrade the SDK.
Cross-template clone did not take effectcloneEntity decides automatically: if the target document’s templateFrom matches the template it writes clone_<handle>, otherwise clone_<handle>_<mapid>_<version>. If you call setCloneSource yourself, you must pass mapid for cross-template clones.