v3.0.0

<bmx-cad-viewer>

A viewer for CAD drawings in DXF - the format AutoCAD, BricsCAD, DraftSight, LibreCAD, QCAD, FreeCAD, SolidWorks, Inventor, Revit and ArchiCAD all write: model space and the layouts with their viewports; lines, arcs, circles, ellipses, polylines, splines, texts, blocks however deep, hatches, dimensions and leaders in their colours, linetypes and lineweights. Pan, zoom and switch layers; point at anything to read what it is; measure distances, areas and angles with snaps; find text; mark it up with rectangles, clouds, arrows, notes and freehand; save the view or the whole as PNG, SVG or PDF. The file is read and drawn in the browser: nothing is uploaded.

12 properties · 8 events · 20 methods · 0 parts

Example

Try it with your own files in confidence: they are opened by your browser, on your device. Nothing is uploaded, no copy reaches us or anyone else, and nothing is kept once you close this tab.

An invented flat, written out as a DXF by this page: walls and their hatching, doors and windows inserted as blocks, furniture, floor patterns, room names, dimensions, a grid on a dashed linetype, and an A3 sheet with the plan at 1:50 and the kitchen at 1:20. Pan and zoom; click anything to see what it is; measure distances, areas and angles - they snap to ends, middles and centres; find text; mark the drawing up; save it as PNG, SVG or PDF. Switch layers on and off: A-AREA starts off, as the drawing saved it.

Show markup
<bmx-cad-viewer id="ex-cad" style="--bmx-cad-viewer-height: 34rem"></bmx-cad-viewer>
<p class="note">An invented flat, written out as a DXF by this page: walls and their hatching, doors and windows inserted as blocks, furniture, floor patterns, room names, dimensions, a grid on a dashed linetype, and an A3 sheet with the plan at 1:50 and the kitchen at 1:20. Pan and zoom; click anything to see what it is; measure distances, areas and angles - they snap to ends, middles and centres; find text; mark the drawing up; save it as PNG, SVG or PDF. Switch layers on and off: A-AREA starts off, as the drawing saved it.</p>

<div class="row" style="gap: 0.75rem; flex-wrap: wrap; align-items: center; margin-block: 0.5rem 1.5rem">
  <label>Open a drawing of your own <input type="file" id="ex-cad-file" accept=".dxf" /></label>
  <button type="button" id="ex-cad-sample">Show the sample again</button>
</div>
<p class="note" id="ex-cad-out" role="status"></p>

<script type="module">
  await customElements.whenDefined('bmx-cad-viewer');
  const viewer = document.getElementById('ex-cad');
  const out = document.getElementById('ex-cad-out');

  /** A small DXF writer: group codes and values, a line each. */
  function sampleDxf() {
    const L = [];
    const g = (code, value) => L.push(String(code), typeof value === 'number' ? String(Math.round(value * 1000) / 1000) : String(value));
    const ent = (type, layer, ...pairs) => {
      g(0, type);
      g(8, layer);
      for (let k = 0; k < pairs.length; k += 2) g(pairs[k], pairs[k + 1]);
    };
    const line = (layer, x1, y1, x2, y2, ...extra) => ent('LINE', layer, 10, x1, 20, y1, 11, x2, 21, y2, ...extra);
    const circle = (layer, x, y, r, ...extra) => ent('CIRCLE', layer, 10, x, 20, y, 40, r, ...extra);
    const arc = (layer, x, y, r, a0, a1) => ent('ARC', layer, 10, x, 20, y, 40, r, 50, a0, 51, a1);
    const poly = (layer, pts, closed = true, ...extra) => {
      ent('LWPOLYLINE', layer, 90, pts.length, 70, closed ? 1 : 0, ...extra);
      for (const p of pts) {
        g(10, p[0]);
        g(20, p[1]);
        if (p[2]) g(42, p[2]);
      }
    };
    const rect = (layer, x, y, w, h) =>
      poly(layer, [
        [x, y],
        [x + w, y],
        [x + w, y + h],
        [x, y + h],
      ]);
    const text = (layer, x, y, h, words, opts = {}) =>
      ent('TEXT', layer, 10, x, 20, y, 11, x, 21, y, 40, h, 1, words, 7, 'NOTES', 72, opts.h ?? 1, 73, opts.v ?? 2, ...(opts.rot ? [50, opts.rot] : []));
    const mtext = (layer, x, y, h, words) => ent('MTEXT', layer, 10, x, 20, y, 40, h, 71, 5, 7, 'NOTES', 1, words);
    const insert = (block, layer, x, y, rot = 0, ...extra) => ent('INSERT', layer, 2, block, 10, x, 20, y, 50, rot, ...extra);
    /** A hatch of polyline paths: solid, or with pattern lines [angle, base x, base y, offset x, offset y, dashes]. */
    const hatch = (layer, paths, pattern) => {
      g(0, 'HATCH');
      g(8, layer);
      g(2, pattern ? 'USER' : 'SOLID');
      g(70, pattern ? 0 : 1);
      g(91, paths.length);
      paths.forEach((pts, k) => {
        g(92, k ? 2 : 3);
        g(72, 0);
        g(73, 1);
        g(93, pts.length);
        for (const p of pts) {
          g(10, p[0]);
          g(20, p[1]);
        }
        g(97, 0);
      });
      g(75, 0);
      g(76, 1);
      if (pattern) {
        g(78, pattern.length);
        for (const [a, bx, by, ox, oy, dashes = []] of pattern) {
          g(53, a);
          g(43, bx);
          g(44, by);
          g(45, ox);
          g(46, oy);
          g(79, dashes.length);
          for (const d of dashes) g(49, d);
        }
      }
    };
    const box = (x, y, w, h) => [
      [x, y],
      [x + w, y],
      [x + w, y + h],
      [x, y + h],
    ];

    // Header: millimetres, linetype scale for a 1:50 sheet.
    g(0, 'SECTION');
    g(2, 'HEADER');
    g(9, '$ACADVER');
    g(1, 'AC1015');
    g(9, '$INSUNITS');
    g(70, 4);
    g(9, '$LTSCALE');
    g(40, 25);
    g(0, 'ENDSEC');

    // Tables: linetypes, layers, a text style.
    g(0, 'SECTION');
    g(2, 'TABLES');
    g(0, 'TABLE');
    g(2, 'LTYPE');
    for (const [name, pattern] of [
      ['CONTINUOUS', []],
      ['CENTER', [31.75, -6.35, 6.35, -6.35]],
      ['DASHED', [12.7, -6.35]],
    ]) {
      g(0, 'LTYPE');
      g(2, name);
      g(73, pattern.length);
      for (const d of pattern) g(49, d);
    }
    g(0, 'ENDTAB');
    g(0, 'TABLE');
    g(2, 'LAYER');
    for (const [name, color, lt = 'CONTINUOUS', weight = -3] of [
      ['0', 7],
      ['A-WALL', 7, 'CONTINUOUS', 50],
      ['A-WALL-HATCH', 8],
      ['A-DOOR', 3],
      ['A-GLAZ', 4],
      ['A-FURN', 30],
      ['A-FIXT', 5],
      ['A-FLOR-PATT', 253],
      ['A-ANNO-TEXT', 7],
      ['A-ANNO-DIMS', 1],
      ['A-GRID', 8, 'CENTER'],
      ['A-AREA', -6, 'DASHED'],
    ]) {
      g(0, 'LAYER');
      g(5, `L-${name}`);
      g(2, name);
      g(70, 0);
      g(62, color);
      g(6, lt);
      g(370, weight);
    }
    g(0, 'ENDTAB');
    g(0, 'TABLE');
    g(2, 'BLOCK_RECORD');
    for (const [handle, name] of [
      ['1E', '*Model_Space'],
      ['1F', '*Paper_Space'],
    ]) {
      g(0, 'BLOCK_RECORD');
      g(5, handle);
      g(2, name);
    }
    g(0, 'ENDTAB');
    g(0, 'TABLE');
    g(2, 'STYLE');
    g(0, 'STYLE');
    g(2, 'NOTES');
    g(3, 'arial.ttf');
    g(40, 0);
    g(41, 1);
    g(0, 'ENDTAB');
    g(0, 'ENDSEC');

    // Blocks: a door, a window, a chair, a bed, a hob ring, a north arrow, a title block, and three dimensions.
    g(0, 'SECTION');
    g(2, 'BLOCKS');
    const block = (name, draw) => {
      g(0, 'BLOCK');
      g(2, name);
      g(70, name.startsWith('*') ? 1 : 0);
      g(10, 0);
      g(20, 0);
      draw();
      g(0, 'ENDBLK');
    };
    block('DOOR-900', () => {
      line('0', 0, 0, 0, 900);
      arc('0', 0, 0, 900, 0, 90);
    });
    block('WINDOW-1200', () => {
      rect('0', 0, 0, 1200, 250);
      line('0', 0, 125, 1200, 125);
    });
    block('CHAIR', () => {
      poly('0', [
        [-225, -225, 0],
        [225, -225, 0],
        [225, 150, 0.4],
        [-225, 150, 0],
      ]);
      line('0', -225, 225, 225, 225);
    });
    block('BED-1600', () => {
      rect('0', 0, 0, 1600, 2000);
      poly('0', [
        [80, 1600, 0],
        [740, 1600, 0.3],
        [740, 1900, 0],
        [80, 1900, 0.3],
      ]);
      poly('0', [
        [860, 1600, 0],
        [1520, 1600, 0.3],
        [1520, 1900, 0],
        [860, 1900, 0.3],
      ]);
      line('0', 0, 1450, 1600, 1450);
    });
    block('HOB-RING', () => {
      circle('0', 0, 0, 110);
      circle('0', 0, 0, 60);
    });
    block('NORTH', () => {
      circle('0', 0, 0, 400);
      ent('SOLID', '0', 10, 0, 20, 380, 11, -150, 21, -250, 12, 0, 22, -120, 13, 0, 23, -120);
      text('0', 0, 560, 220, 'N');
    });
    block('TITLE', () => {
      rect('0', 0, 0, 5200, 1100);
      line('0', 0, 550, 5200, 550);
      line('0', 3600, 0, 3600, 1100);
      text('0', 200, 820, 260, 'Flat 3B, level 2', { h: 0, v: 2 });
      text('0', 200, 270, 150, 'General arrangement, scale 1:50', { h: 0, v: 2 });
      text('0', 3800, 820, 160, 'Sheet', { h: 0, v: 2 });
    });
    const dim = (name, horizontal, a, b, at, value) =>
      block(name, () => {
        const ext = horizontal ? [a, 0, a, at, b, 0, b, at] : [0, a, at, a, 0, b, at, b];
        line('0', ext[0], ext[1], ext[2], ext[3]);
        line('0', ext[4], ext[5], ext[6], ext[7]);
        if (horizontal) {
          line('0', a, at, b, at);
          for (const x of [a, b]) line('0', x - 90, at - 90, x + 90, at + 90);
          mtext('0', (a + b) / 2, at + 180, 200, String(value));
        } else {
          line('0', at, a, at, b);
          for (const y of [a, b]) line('0', at - 90, y - 90, at + 90, y + 90);
          ent('MTEXT', '0', 10, at - 180, 20, (a + b) / 2, 40, 200, 71, 5, 50, 90, 1, String(value));
        }
      });
    block('*Paper_Space', () => {});
    dim('*D1', true, 0, 12000, -900, 12000);
    dim('*D2', false, 0, 8000, -900, 8000);
    dim('*D3', true, 5050, 11750, 8700, 6700);
    g(0, 'ENDSEC');

    // Model space.
    g(0, 'SECTION');
    g(2, 'ENTITIES');
    // Walls: the outer ring hatched between its two outlines, and the partitions.
    rect('A-WALL', 0, 0, 12000, 8000);
    rect('A-WALL', 250, 250, 11500, 7500);
    hatch('A-WALL-HATCH', [box(0, 0, 12000, 8000), box(250, 250, 11500, 7500)]);
    const partitions = [
      [4950, 250, 100, 750],
      [4950, 1900, 100, 3600],
      [4950, 6400, 100, 1350],
      [7950, 250, 100, 3700],
      [5050, 3950, 4150, 100],
      [10100, 3950, 1650, 100],
    ];
    for (const [x, y, w, h] of partitions) {
      rect('A-WALL', x, y, w, h);
      hatch('A-WALL-HATCH', [box(x, y, w, h)]);
    }
    // Doors (blocks, turned to their walls) and windows.
    insert('DOOR-900', 'A-DOOR', 4950, 1000, 90);
    insert('DOOR-900', 'A-DOOR', 4950, 5500, 90);
    insert('DOOR-900', 'A-DOOR', 9200, 4050, 0);
    for (const [x, y, rot] of [
      [1200, 0, 0],
      [3000, 0, 0],
      [9500, 0, 0],
      [1800, 7750, 0],
      [7000, 7750, 0],
      [9800, 7750, 0],
      [12000, 5400, 90],
    ])
      insert('WINDOW-1200', 'A-GLAZ', x, y, rot);
    // Living room: sofa, oval rug, a round table and its four chairs.
    poly('A-FURN', [
      [600, 5600, 0],
      [3000, 5600, 0],
      [3000, 6500, 0],
      [2800, 6500, 0],
      [2800, 5850, -0.2],
      [800, 5850, 0],
      [800, 6500, 0],
      [600, 6500, 0],
    ]);
    ent('ELLIPSE', 'A-FURN', 10, 1800, 20, 4300, 11, 1300, 21, 0, 40, 0.55, 41, 0, 42, Math.PI * 2);
    ent('SPLINE', 'A-FURN', 70, 8, 71, 3, 72, 0, 73, 6, 74, 0, ...[[300, 2300], [900, 3200], [1900, 2600], [2700, 3400], [3600, 2500], [4400, 3000]].flatMap(p => [10, p[0], 20, p[1]]));
    circle('A-FURN', 2600, 1500, 550);
    for (const rot of [0, 90, 180, 270]) {
      const r = (rot * Math.PI) / 180;
      insert('CHAIR', 'A-FURN', 2600 - Math.sin(r) * 850, 1500 + Math.cos(r) * 850, rot + 180);
    }
    // Bedroom.
    insert('BED-1600', 'A-FURN', 7200, 5650);
    rect('A-FURN', 10400, 6600, 1200, 1000);
    // Bathroom: bath, basin and WC; floor tiles.
    poly('A-FIXT', [
      [5150, 300, 0],
      [7850, 300, 0],
      [7850, 1050, 0],
      [5150, 1050, 0],
    ]);
    poly('A-FIXT', [
      [5350, 400, 0],
      [7400, 400, 0.4],
      [7400, 950, 0],
      [5350, 950, 0],
    ]);
    ent('ELLIPSE', 'A-FIXT', 10, 7400, 20, 3500, 11, 0, 21, 280, 40, 0.75, 41, 0, 42, Math.PI * 2);
    ent('ELLIPSE', 'A-FIXT', 10, 6100, 20, 3550, 11, 0, 21, 330, 40, 0.65, 41, 0, 42, Math.PI * 2);
    rect('A-FIXT', 5750, 3700, 700, 200);
    hatch('A-FLOR-PATT', [box(5050, 250, 2900, 3700)], [
      [0, 5050, 250, 0, 300],
      [90, 5050, 250, -300, 0],
    ]);
    // Kitchen: worktop, hob (a 2 by 2 array of one block), sink; floor tiles.
    poly('A-FIXT', [
      [8050, 250, 0],
      [11750, 250, 0],
      [11750, 3950, 0],
      [11150, 3950, 0],
      [11150, 850, 0],
      [8050, 850, 0],
    ]);
    insert('HOB-RING', 'A-FIXT', 9000, 400, 0, 70, 2, 71, 2, 44, 300, 45, 300);
    poly('A-FIXT', [
      [11250, 2000, 0.4],
      [11650, 2000, 0],
      [11650, 2700, 0.4],
      [11250, 2700, 0],
    ]);
    hatch('A-FLOR-PATT', [box(8050, 850, 3100, 3100)], [
      [45, 8050, 850, -212.13, 212.13],
      [135, 8050, 850, -212.13, -212.13],
    ]);
    // Living floor: boards, joints staggered.
    hatch('A-FLOR-PATT', [box(250, 250, 4700, 7500)], [[0, 250, 250, 400, 150, [1200, -12]]]);
    // Room names with their areas, and the room outlines on the A-AREA layer (switched off).
    for (const [name, x, y, w, h] of [
      ['Living', 250, 250, 4700, 7500],
      ['Bedroom', 5050, 4050, 6700, 3700],
      ['Bathroom', 5050, 250, 2900, 3700],
      ['Kitchen', 8050, 250, 3700, 3700],
    ]) {
      mtext('A-ANNO-TEXT', x + w / 2, y + h / 2 + (name === 'Living' ? 1000 : name === 'Kitchen' ? 600 : 0), 220, `${name}\\P${((w * h) / 1e6).toFixed(1)} m²`);
      rect('A-AREA', x, y, w, h);
    }
    // Dimensions, drawn into their blocks; the grid; a north arrow; the title block with its sheet number.
    ent('DIMENSION', 'A-ANNO-DIMS', 2, '*D1', 10, 12000, 20, -900, 11, 6000, 21, -720, 70, 32, 42, 12000);
    ent('DIMENSION', 'A-ANNO-DIMS', 2, '*D2', 10, -900, 20, 8000, 11, -1080, 21, 4000, 70, 33, 42, 8000);
    ent('DIMENSION', 'A-ANNO-DIMS', 2, '*D3', 10, 11750, 20, 8700, 11, 8400, 21, 8880, 70, 32, 42, 6700);
    for (const [x, name] of [
      [0, 'A'],
      [5000, 'B'],
      [12000, 'C'],
    ]) {
      line('A-GRID', x, -1600, x, 9700);
      circle('A-GRID', x, 10050, 350);
      text('A-GRID', x, 10050, 280, name);
    }
    insert('NORTH', 'A-ANNO-TEXT', 13600, 7000);
    g(0, 'INSERT');
    g(8, 'A-ANNO-TEXT');
    g(66, 1);
    g(2, 'TITLE');
    g(10, 6800);
    g(20, -2900);
    g(0, 'ATTRIB');
    g(8, 'A-ANNO-TEXT');
    g(10, 10650);
    g(20, -2700);
    g(40, 300);
    g(1, 'A-101');
    g(2, 'SHEET');
    g(70, 0);
    g(7, 'NOTES');
    g(0, 'SEQEND');
    // The A3 sheet (in millimetres): a border, the plan at 1:50, the kitchen at 1:20 without its dimensions, and their titles.
    const paper = (type, layer, ...pairs) => ent(type, layer, 67, 1, ...pairs);
    paper('LWPOLYLINE', 'A-ANNO-TEXT', 90, 4, 70, 1, 10, 10, 20, 10, 10, 410, 20, 10, 10, 410, 20, 287, 10, 10, 20, 287);
    paper('VIEWPORT', '0', 10, 210, 20, 148.5, 40, 420, 41, 297, 69, 1, 12, 210, 22, 148.5, 45, 297);
    paper('VIEWPORT', '0', 10, 140, 20, 160, 40, 240, 41, 220, 69, 2, 12, 6600, 22, 3700, 45, 11000);
    paper('VIEWPORT', '0', 10, 337, 20, 175, 40, 125, 41, 150, 69, 3, 12, 10000, 22, 2250, 45, 3900, 341, 'L-A-ANNO-DIMS', 341, 'L-A-ANNO-TEXT');
    for (const [x, y, h, words] of [
      [140, 38, 5, 'Plan  1:50'],
      [337, 88, 5, 'Kitchen  1:20'],
      [320, 30, 6, 'Flat 3B, level 2 - A-101'],
    ])
      paper('TEXT', 'A-ANNO-TEXT', 10, x, 20, y, 11, x, 21, y, 40, h, 1, words, 7, 'NOTES', 72, 1, 73, 2);
    g(0, 'ENDSEC');
    // The layouts: the model, and the sheet.
    g(0, 'SECTION');
    g(2, 'OBJECTS');
    for (const [name, order, record, extra] of [
      ['Model', 0, '1E', []],
      ['A3 sheet', 1, '1F', [10, 0, 20, 0, 11, 420, 21, 297]],
    ]) {
      g(0, 'LAYOUT');
      g(100, 'AcDbPlotSettings');
      g(1, name);
      g(44, 420);
      g(45, 297);
      g(72, 1);
      g(100, 'AcDbLayout');
      g(1, name);
      g(71, order);
      for (let k = 0; k < extra.length; k += 2) g(extra[k], extra[k + 1]);
      g(330, record);
    }
    g(0, 'ENDSEC');
    g(0, 'EOF');
    return L.join('\n');
  }

  const showSample = () => viewer.load(sampleDxf(), 'flat-3b.dxf');
  viewer.addEventListener('bmxLoad', e => {
    const i = e.detail;
    out.textContent = `${i.name}: ${i.entities} items on ${i.layers} layers, ${i.blocks} blocks, in ${i.unitName || 'drawing units'}.`;
  });
  viewer.addEventListener('bmxError', e => (out.textContent = e.detail.message));
  document.getElementById('ex-cad-file').addEventListener('change', e => {
    const file = e.target.files[0];
    if (file) viewer.load(file).catch(() => undefined);
  });
  document.getElementById('ex-cad-sample').addEventListener('click', showSample);
  await showSample();
</script>
<bmx-cad-viewer src="plans/level-1.dxf"></bmx-cad-viewer>

Properties

PropertyAttributeTypeDefaultDescription
author author string — Written into each new markup.
background background 'auto' | 'dark' | 'light' 'auto' The background: the theme's (auto), or the dark or light ground CAD programs use.
label label string — The element's accessible name.
lineweights lineweights boolean false Lines drawn at their lineweights (as printed), rather than all fine.
locale locale string — The locale for numbers. Unset: the page's lang.
markups markups BmxCadMarkup[] | string [] The markups (an array, or its JSON): kept by the page, shown on the drawing.
readOnly read-only boolean false Offers no markup tools.
showLayers show-layers boolean true The side panel: layers, find and markups.
space space string 'Model' The space shown: Model, or a layout's name.
src src string — A DXF file to fetch and show (same origin, or served with CORS).
strings strings Partial<BmxCadViewerStrings> | string — Replacements for any of the wording (an object, or its JSON).
tool tool BmxCadViewerTool 'pan' The tool in use: pan (and point to choose), a measurement, or a markup.

Events

EventDetailDescription
bmxError BmxCadViewerErrorDetail Fired when a file cannot be read (a DWG, a binary DXF, not a drawing, or a failed fetch).
bmxLayerChange { layers: BmxCadViewerLayer[]; } Fired when layers are switched on or off.
bmxLoad BmxCadViewerInfo Fired when a drawing has been read and shown.
bmxMarkupChange { markups: BmxCadMarkup[]; } Fired when markups are added, deleted or cleared: store markups to keep them.
bmxMeasure BmxCadViewerMeasure Fired when a measurement is taken.
bmxSelect BmxCadViewerSelection | null Fired when an entity is chosen by pointing at it (null when let go).
bmxSpaceChange { space: string; } Fired when the model or another layout is shown.
bmxViewChange BmxCadViewerView Fired when the view pans or zooms.

Methods

MethodSignatureDescription
clear clear() => Promise<void> Closes the drawing.
clearMarkups clearMarkups() => Promise<void> Removes every markup.
findText findText(query: string) => Promise<{ text: string; layer: string; box: BmxCadBox; }[]> The texts holding some words, in reading order, with where they are.
fit fit() => Promise<void> Fits the whole drawing (or the layout's sheet) in view.
getInfo getInfo() => Promise<BmxCadViewerInfo | null> What is open, or null.
getLayers getLayers() => Promise<BmxCadViewerLayer[]> The drawing's layers, in name order.
getMarkups getMarkups() => Promise<BmxCadMarkup[]> The markups, all spaces.
getSelection getSelection() => Promise<BmxCadViewerSelection | null> The entity chosen, or null.
getSpaces getSpaces() => Promise<string[]> The spaces it can show: Model, then the layouts with something on their sheets.
getView getView() => Promise<BmxCadViewerView> The view: the drawing's point in the middle, and pixels per drawing unit.
load load(input: string | Blob | ArrayBuffer | Uint8Array, name?: string) => Promise<BmxCadViewerInfo> Opens a drawing: DXF text, its bytes, or a file. Resolves with what was opened (or rejects with why not).
saveAs saveAs(kind: "png" | "png-all" | "svg" | "pdf") => Promise<void> Saves the PNG, SVG or PDF (the reader's browser downloads it).
selectAt selectAt(x: number | null, y?: number) => Promise<BmxCadViewerSelection | null> Chooses the entity at a drawing point (as a click there does), or lets go with null.
setLayerVisible setLayerVisible(names: string | string[], visible: boolean) => Promise<void> Switches layers on or off.
setView setView(view: Partial<BmxCadViewerView>) => Promise<void> Sets the view.
toPdf toPdf(options?: { page?: BmxCadPdfOptions["page"]; landscape?: boolean; area?: "view" | "all"; }) => Promise<Uint8Array> The shown space as a one-page PDF in vector (a layout on its own sheet, the model fitted to A3 unless said), on white.
toPng toPng(options?: { area?: "view" | "all"; width?: number; }) => Promise<Blob | null> A PNG picture: of the view as it is (default) or of the whole, width pixels wide.
toSvg toSvg(options?: { area?: "view" | "all"; width?: number; background?: "light" | "dark"; }) => Promise<string> The shown space (and visible layers) as SVG, the markups on top; the view's ground unless background is given.
zoom zoom(factor: number) => Promise<void> Zooms by a factor about the middle of the view (2 doubles the size).
zoomTo zoomTo(box: BmxCadBox) => Promise<void> Zooms to show a box of the drawing: [min x, min y, max x, max y].

CSS custom properties

PropertyDescription
--bmx-cad-viewer-background The drawing's ground (when the background is auto).
--bmx-cad-viewer-font Font for the drawing's texts whose own font is a CAD font.
--bmx-cad-viewer-height Height of the viewer.
--bmx-cad-viewer-layers-width Width of the layers panel.
--bmx-cad-viewer-radius Corner radius of the frame.