Rasterex APINPM Package/docs/components/annotation/shapes

Shapes

Mark equipment zones, room areas, and site boundaries using rectangles, ellipses, clouds, and polygons.

Overview

Shape drawing tools allow users to highlight specific areas, mark revision zones, or annotate CAD models and PDF drawings.

Use the tools API surface to activate shape modes and apply custom colors, fill, opacity, and line weights.

  • Cloud (`SHAPE_CLOUD`): Highlight changes, revisions, and site survey correction areas.
  • Rectangle (`SHAPE_RECTANGLE`): Mark room partitions, hardware components, and equipment placements.
  • Ellipse (`SHAPE_ELLIPSE`): Outline circular sections, conduits, or inspection points.

Quick Examples

Activate shape drawing modes programmatically using the tools surface:

Activate rectangle drawing mode

await viewer.tools.set({
  action: "SHAPE_RECTANGLE",
  enabled: true,
  style: {
    strokeColor: "#2563eb",
    fillColor: "#dbeafe",
    strokeWidth: 2,
    opacity: 0.9,
    fillOpacity: 0.3
  }
});

Deactivate shapes tool

// Deactivate rectangle drawing mode
await viewer.tools.set({
  action: "SHAPE_RECTANGLE",
  enabled: false
});

// Clear active tools and return to mouse cursor select mode
viewer.tools.clear();

Supported Shape Actions

Pass any of these action keys to viewer.tools.set:

  • `SHAPE_RECTANGLE`: Standard 4-point rectangle outline.
  • `SHAPE_RECTANGLE_ROUNDED`: Rectangle with rounded corners.
  • `SHAPE_ELLIPSE`: Ellipse or perfect circle markup.
  • `SHAPE_CLOUD`: Dynamic revision cloud outline.
  • `SHAPE_POLYGON`: Custom multi-point shape contours.

Vanilla Integration

Copy these project setup files to configure mounting, shape tools selection buttons, and tool clearing:

import { createViewer } from "@rasterex/viewer";
import "./style.css";

const viewer = createViewer({
  container: "#viewer"
});

const statusEl = document.querySelector("#status");

function setStatus(message) {
  if (statusEl) statusEl.textContent = message;
}

async function startViewer() {
  await viewer.mount();
  await viewer.ready();
  
  await viewer.documents.open({
    url: "https://pdfobject.com/pdf/sample.pdf",
    displayName: "sample.pdf"
  });

  setStatus("Viewer ready");
}

async function enableShape(action) {
  setStatus(`Activating ${action}...`);
  try {
    const result = await viewer.tools.set({
      action,
      enabled: true,
      style: {
        strokeColor: "#2563eb",
        fillColor: "#dbeafe",
        strokeWidth: 2,
        fillOpacity: 0.3
      }
    });

    if (result.success) {
      setStatus(`${action} active`);
    } else {
      setStatus(`Failed: ${result.error}`);
    }
  } catch (error) {
    setStatus("Tool activation error");
  }
}

document.querySelectorAll("[data-action]").forEach((button) => {
  button.addEventListener("click", () => {
    enableShape(button.dataset.action).catch(console.error);
  });
});

document.querySelector("#clear-btn")?.addEventListener("click", () => {
  viewer.tools.clear();
  setStatus("Drawing tools cleared");
});

startViewer().catch(console.error);
Vanilla Integration

React Integration

Trigger custom shape drawing states inside a React component:

import { useEffect, useRef, useState } from "react";
import { createViewer, type RasterexViewer, type ToolAction } from "@rasterex/viewer";
import "./style.css";

const shapesList: Array<{ label: string; action: ToolAction }> = [
  { label: "Rectangle", action: "SHAPE_RECTANGLE" },
  { label: "Ellipse", action: "SHAPE_ELLIPSE" },
  { label: "Cloud", action: "SHAPE_CLOUD" }
];

export function ShapesExample() {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const viewerRef = useRef<RasterexViewer | null>(null);
  const [activeTool, setActiveTool] = useState<ToolAction | null>(null);
  const [status, setStatus] = useState("Starting viewer...");

  useEffect(() => {
    if (!containerRef.current) return undefined;

    const viewer = createViewer({ container: containerRef.current });
    viewerRef.current = viewer;

    async function start(): Promise<void> {
      await viewer.mount();
      await viewer.ready();
      await viewer.documents.open({
        url: "https://pdfobject.com/pdf/sample.pdf",
        displayName: "sample.pdf"
      });
      setStatus("Viewer ready");
    }

    void start().catch(console.error);

    return () => {
      viewer.destroy();
      viewerRef.current = null;
    };
  }, []);

  async function activateTool(action: ToolAction): Promise<void> {
    setStatus(`Activating ${action}...`);
    const result = await viewerRef.current?.tools.set({
      action,
      enabled: true,
      style: {
        strokeColor: "#2563eb",
        fillColor: "#dbeafe",
        strokeWidth: 2,
        fillOpacity: 0.3
      }
    });

    if (result?.success) {
      setActiveTool(action);
      setStatus(`${action} active`);
    } else {
      setStatus(`Failed: ${result?.error ?? "error"}`);
    }
  }

  function clearTools(): void {
    viewerRef.current?.tools.clear();
    setActiveTool(null);
    setStatus("Drawing tools cleared");
  }

  return (
    <div>
      <div className="toolbar">
        {shapesList.map((tool) => (
          <button
            key={tool.action}
            type="button"
            aria-pressed={activeTool === tool.action}
            onClick={() => void activateTool(tool.action)}
          >
            {tool.label}
          </button>
        ))}
        <button type="button" onClick={clearTools}>Clear tools</button>
      </div>
      <div ref={containerRef} style={{ height: 600 }} />
      <pre>{status}</pre>
    </div>
  );
}
React Integration

Vue Integration

Trigger custom shape drawing states inside a Vue composition component template:

<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
import { createViewer, type RasterexViewer, type ToolAction } from "@rasterex/viewer";
import "./style.css";

const containerRef = ref<HTMLElement | null>(null);
const viewerRef = shallowRef<RasterexViewer | null>(null);
const activeTool = ref<ToolAction | null>(null);
const status = ref("Starting viewer...");

const shapesList: Array<{ label: string; action: ToolAction }> = [
  { label: "Rectangle", action: "SHAPE_RECTANGLE" },
  { label: "Ellipse", action: "SHAPE_ELLIPSE" },
  { label: "Cloud", action: "SHAPE_CLOUD" }
];

onMounted(async () => {
  if (!containerRef.value) return;

  const viewer = createViewer({ container: containerRef.value });
  viewerRef.value = viewer;

  try {
    await viewer.mount();
    await viewer.ready();
    await viewer.documents.open({
      url: "https://pdfobject.com/pdf/sample.pdf",
      displayName: "sample.pdf"
    });
    status.value = "Viewer ready";
  } catch (error) {
    status.value = "Viewer failed to start";
  }
});

onBeforeUnmount(() => {
  viewerRef.value?.destroy();
  viewerRef.value = null;
});

async function activateTool(action: ToolAction) {
  status.value = `Activating ${action}...`;
  try {
    const result = await viewerRef.value?.tools.set({
      action,
      enabled: true,
      style: {
        strokeColor: "#2563eb",
        fillColor: "#dbeafe",
        strokeWidth: 2,
        fillOpacity: 0.3
      }
    });

    if (result?.success) {
      activeTool.value = action;
      status.value = `${action} active`;
    } else {
      status.value = `Failed: ${result?.error ?? "error"}`;
    }
  } catch (error) {
    status.value = "Tool error";
  }
}

function clearTools() {
  viewerRef.value?.tools.clear();
  activeTool.value = null;
  status.value = "Drawing tools cleared";
}
</script>

<template>
  <div>
    <div class="toolbar">
      <button
        v-for="tool in shapesList"
        :key="tool.action"
        type="button"
        :aria-pressed="activeTool === tool.action"
        @click="activateTool(tool.action)"
      >
        {{ tool.label }}
      </button>
      <button type="button" @click="clearTools">Clear tools</button>
    </div>
    <div ref="containerRef" style="height: 600px" />
    <pre>{{ status }}</pre>
  </div>
</template>
Vue Integration

Angular Integration

Trigger custom shape drawing states inside an Angular component model:

import {
  AfterViewInit,
  Component,
  ElementRef,
  OnDestroy,
  ViewChild
} from "@angular/core";
import { createViewer, type RasterexViewer, type ToolAction } from "@rasterex/viewer";

@Component({
  selector: "app-shapes",
  templateUrl: "./shapes.component.html"
})
export class ShapesComponent implements AfterViewInit, OnDestroy {
  @ViewChild("viewerContainer", { static: true })
  viewerContainer!: ElementRef<HTMLElement>;

  status = "Starting viewer...";
  activeTool: ToolAction | null = null;

  shapesList: Array<{ label: string; action: ToolAction }> = [
    { label: "Rectangle", action: "SHAPE_RECTANGLE" },
    { label: "Ellipse", action: "SHAPE_ELLIPSE" },
    { label: "Cloud", action: "SHAPE_CLOUD" }
  ];

  private viewer: RasterexViewer | null = null;

  async ngAfterViewInit(): Promise<void> {
    const viewer = createViewer({
      container: this.viewerContainer.nativeElement
    });

    this.viewer = viewer;

    try {
      await viewer.mount();
      await viewer.ready();
      await viewer.documents.open({
        url: "https://pdfobject.com/pdf/sample.pdf",
        displayName: "sample.pdf"
      });
      this.status = "Viewer ready";
    } catch (error) {
      console.error(error);
      this.status = "Viewer failed to start";
    }
  }

  async activateTool(action: ToolAction): Promise<void> {
    this.status = `Activating ${action}...`;
    const result = await this.viewer?.tools.set({
      action,
      enabled: true,
      style: {
        strokeColor: "#2563eb",
        fillColor: "#dbeafe",
        strokeWidth: 2,
        fillOpacity: 0.3
      }
    });

    if (result?.success) {
      this.activeTool = action;
      this.status = `${action} active`;
    } else {
      this.status = result?.error ?? "Tool activation failed";
    }
  }

  clearTools(): void {
    this.viewer?.tools.clear();
    this.activeTool = null;
    this.status = "Drawing tools cleared";
  }

  ngOnDestroy(): void {
    this.viewer?.destroy();
  }
}
Angular Integration

Live Preview

Test the shape tools in real-time.

Interactive Workbench

Live Canvas Preview

Launch the interactive preview to explore the Canvas capabilities. Use the dashboard within the modal to test different tools and configurations.

Additional Resources

Connect shape controls with core canvas setup steps.