Draw Shapes in PDF Documents Using JavaScript in React

Adding shapes to PDF documents is a common requirement in many business scenarios: marking key areas with lines and boxes, adding a prominent border around content outside a table, distinguishing sections with filled color blocks, or overlaying pie and ellipse shapes on drawings and reports. Doing this by hand in a design tool each time is slow and hard to scale. With the drawing capabilities of Spire.PDF for JavaScript, you can write various shapes directly to PDF pages in the browser and let your code handle the annotation and diagramming work automatically.

Spire.PDF for JavaScript is based on WebAssembly and loads, edits, and saves PDF documents directly in the browser, managing input and output files through a virtual file system (VFS) without any backend service. The core object for drawing shapes on a PDF is the page drawing canvas PdfPage.Canvas: it provides methods such as DrawLine, DrawPie, DrawRectangle, and DrawEllipse.

This article covers four core features:

For installation and project configuration, refer to How to Integrate Spire.PDF for JavaScript in a React Project. The examples below assume Spire.PDF is installed and the WebAssembly module is initialized.


Draw Lines on a PDF Page

When drawing lines you can set the color and thickness, and choose between solid and dashed lines—the dash style is controlled by DashStyle and DashPattern.

function App() {
  const drawLines = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Create a PDF document and add a blank page
    let doc = new pdfModule.PdfDocument();
    let page = doc.Pages.Add();

    // Save the current graphics state
    let state = page.Canvas.Save();

    // Create a red pen for drawing lines
    let pen = new pdfModule.PdfPen({
      pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Red() }),
      width: 2,
    });

    // Starting coordinates and length of the lines
    let x = 30.0;
    let y = 50.0;
    let width = 300.0;

    // Draw a solid line
    page.Canvas.DrawLine({ pen: pen, x1: x, y1: y, x2: x + width, y2: y });

    // Set the dash style and dash pattern
    pen.DashStyle = pdfModule.PdfDashStyle.Dash;
    pen.DashPattern = [3.0, 2.0];

    // Draw a dashed line
    page.Canvas.DrawLine({ pen: pen, x1: x, y1: y + 60.0, x2: x + width, y2: y + 60.0 });

    // Restore the graphics state
    page.Canvas.Restore({ state: state });

    // Define the output file name and save the document
    const outputFileName = 'DrawLines_result.pdf';
    doc.SaveToFile(outputFileName);
    doc.Close();

    // Read the generated file from the VFS and trigger a download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Draw Lines in PDF</h1>
      <button onClick={drawLines}>
        Draw
      </button>
    </div>
  );
}

export default App;

The result of drawing one solid line and one dashed line on a PDF page

The result of drawing one solid line and one dashed line on a PDF page


Draw a Pie on a PDF Page

Pies express proportions: the bounding rectangle sets the position and size, while startAngle and sweepAngle set the opening angle of the sector.

function App() {
  const drawPie = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Create a PDF document and add a blank page
    let doc = new pdfModule.PdfDocument();
    let page = doc.Pages.Add();

    // Save the current graphics state
    let state = page.Canvas.Save();

    // Create a dark red pen
    let pen = new pdfModule.PdfPen({
      pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_DarkRed() }),
      width: 2,
    });

    // Draw the first pie
    page.Canvas.DrawPie({ pen: pen, x: 10.0, y: 30.0, width: 130.0, height: 130.0, startAngle: 360.0, sweepAngle: 300.0 });

    // Draw the second pie
    page.Canvas.DrawPie({ pen: pen, x: 160.0, y: 30.0, width: 130.0, height: 130.0, startAngle: 360.0, sweepAngle: 330.0 });

    // Draw the third pie
    page.Canvas.DrawPie({ pen: pen, x: 320.0, y: 30.0, width: 130.0, height: 130.0, startAngle: 360.0, sweepAngle: 360.0 });

    // Restore the graphics state
    page.Canvas.Restore({ state: state });

    // Define the output file name and save the document
    const outputFileName = 'DrawPie_result.pdf';
    doc.SaveToFile(outputFileName);
    doc.Close();

    // Read the generated file from the VFS and trigger a download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Draw a Pie in PDF</h1>
      <button onClick={drawPie}>
        Draw
      </button>
    </div>
  );
}

export default App;

The result of drawing three pies on a PDF page

The result of drawing three pies on a PDF page


Draw a Rectangle on a PDF Page

A rectangle can be drawn as an outline only, or filled. Besides a solid color (PdfSolidBrush), the fill also supports a linear gradient (PdfLinearGradientBrush) and a radial gradient (PdfRadialGradientBrush).

function App() {
  const drawRectangle = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Create a PDF document and add a blank page
    let doc = new pdfModule.PdfDocument();
    let page = doc.Pages.Add();

    // Save the current graphics state
    let state = page.Canvas.Save();

    // Create a black pen
    let pen = new pdfModule.PdfPen({
      pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Black() }),
      width: 1,
    });

    // Draw a rectangle outline with the pen
    page.Canvas.DrawRectangle({
      pen: pen,
      rectangle: new pdfModule.RectangleF({
        location: new pdfModule.PointF(20.0, 30.0),
        size: new pdfModule.SizeF({ width: 150.0, height: 120.0 }),
      }),
    });

    // Create a linear gradient brush
    let linearGradientBrush = new pdfModule.PdfLinearGradientBrush({
      point1: new pdfModule.PointF(200.0, 30.0),
      point2: new pdfModule.PointF(350.0, 150.0),
      color1: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Green() }),
      color2: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Red() }),
    });

    // Draw a filled rectangle with the linear gradient brush
    page.Canvas.DrawRectangle({
      brush: linearGradientBrush,
      rectangle: new pdfModule.RectangleF({
        location: new pdfModule.PointF(200.0, 30.0),
        size: new pdfModule.SizeF({ width: 150.0, height: 120.0 }),
      }),
    });

    // Create a radial gradient brush
    let radialGradientBrush = new pdfModule.PdfRadialGradientBrush({
      centreStart: new pdfModule.PointF(380.0, 30.0),
      radiusStart: 150.0,
      centreEnd: new pdfModule.PointF(530.0, 150.0),
      radiusEnd: 150.0,
      colorStart: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Orange() }),
      colorEnd: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Blue() }),
    });

    // Draw a filled rectangle with the radial gradient brush
    page.Canvas.DrawRectangle({
      brush: radialGradientBrush,
      rectangle: new pdfModule.RectangleF({
        location: new pdfModule.PointF(380.0, 30.0),
        size: new pdfModule.SizeF({ width: 150.0, height: 120.0 }),
      }),
    });

    // Restore the graphics state
    page.Canvas.Restore({ state: state });

    // Define the output file name and save the document
    const outputFileName = 'DrawRectangle_result.pdf';
    doc.SaveToFile(outputFileName);
    doc.Close();

    // Read the generated file from the VFS and trigger a download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Draw a Rectangle in PDF</h1>
      <button onClick={drawRectangle}>
        Draw
      </button>
    </div>
  );
}

export default App;

The result of drawing a rectangle outline and gradient-filled rectangles on a PDF page

The result of drawing a rectangle outline and gradient-filled rectangles on a PDF page


Draw an Ellipse on a PDF Page

An ellipse likewise supports outlines and fills: use PdfPen for the outline and PdfSolidBrush for the fill, or take one of the preset pens from PdfPens.

function App() {
  const drawEllipse = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Create a PDF document and add a blank page
    let doc = new pdfModule.PdfDocument();
    let page = doc.Pages.Add();

    // Save the current graphics state
    let state = page.Canvas.Save();

    // Create a CadetBlue pen
    let pen = pdfModule.PdfPens.get_CadetBlue();

    // Draw the ellipse outline
    page.Canvas.DrawEllipse({ pen: pen, x: 50.0, y: 30.0, width: 120.0, height: 100.0 });

    // Create a fill brush
    let brush = new pdfModule.PdfSolidBrush({
      pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_CadetBlue() }),
    });

    // Draw the filled ellipse
    page.Canvas.DrawEllipse({ brush: brush, x: 180.0, y: 30.0, width: 120.0, height: 100.0 });

    // Restore the graphics state
    page.Canvas.Restore({ state: state });

    // Define the output file name and save the document
    const outputFileName = 'DrawEllipse_result.pdf';
    doc.SaveToFile(outputFileName);
    doc.Close();

    // Read the generated file from the VFS and trigger a download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Draw an Ellipse in PDF</h1>
      <button onClick={drawEllipse}>
        Draw
      </button>
    </div>
  );
}

export default App;

The result of drawing an ellipse outline and a filled ellipse on a PDF page

The result of drawing an ellipse outline and a filled ellipse on a PDF page


FAQ

Why does the drawn shape appear at the edge of the page or outside the visible area

Cause: The coordinates used by DrawLine, DrawPie, DrawRectangle, and DrawEllipse have their origin at the bottom-left corner of the page, with the x-axis pointing right and the y-axis pointing up, in units of points. If you copy screen coordinates directly (where the origin is at the top-left), the drawn shape will end up in the opposite position or outside the page.

Solution: Convert the coordinates using the bottom-left corner of the page as the origin. You can read the page size first and then lay out the shape, for example by using PdfPage.Size to get the page width and height and calculating the shape's position from them:

// Get the page size and calculate coordinates with the bottom-left corner as the origin
let size = page.Size;
let x = size.Width / 4;
let y = size.Height / 3;
page.Canvas.DrawRectangle({ pen: pen, x: x, y: y, width: 200, height: 120 });

Why does the existing content on the page shift after drawing

Cause: Drawing modifies the canvas's current transform and graphics state. If you change the coordinate system with ScaleTransform, TranslateTransform, and similar methods before drawing, or fail to restore the state afterward, subsequent content will be affected.

Solution: Use Canvas.Save and Canvas.Restore in pairs, wrapping the drawing operations between them, to make sure the canvas state is restored to its previous level once drawing is complete:

// Save the state before drawing
let state = page.Canvas.Save();
// ... perform drawing ...
// Restore the state after drawing
page.Canvas.Restore({ state: state });

Why can't I see the drawn shape in the saved PDF

Cause: Shapes are drawn onto the canvas object. If you do not call doc.SaveToFile to write the document back to a file after drawing, or if the output file and the file read for download are not the same name, you will still see the original content.

Solution: Make sure you call doc.SaveToFile(outputFileName) to save after drawing, and read and download from the virtual file system using the same outputFileName:

// Save the document to the specified file name
doc.SaveToFile(outputFileName);
doc.Close();

// Read from the VFS using the same file name to trigger a download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);

Get a Free License

If you want to remove the evaluation message from the result documents or get rid of the feature limitations, please contact our sales team to obtain a temporary license valid for 30 days.