Skip to main content
Version: API v1

Working with Vector Graphics

PDFDancer allows you to work with vector graphics elements (paths) in PDFs. You can both select existing paths and create new ones - draw lines, shapes, bezier curves, and complex vector graphics.


Selecting Paths​

Paths are vector graphics elements in PDFs (lines, shapes, drawings).

from pdfdancer import PDFDancer

with PDFDancer.open("document.pdf") as pdf:
# Get all paths on a specific page
paths = pdf.page(3).select_paths()

# Get paths at specific coordinates
paths_at_point = pdf.page(3).select_paths_at(x=150, y=320)

for path in paths:
print(f"Path ID: {path.internal_id}")

Understanding Vector Paths​

Vector paths in PDFs can represent:

  • Lines and curves
  • Shapes (rectangles, circles, polygons)
  • Complex drawings and diagrams
  • Borders and decorative elements

Working with Paths​

from pdfdancer import PDFDancer

with PDFDancer.open("document.pdf") as pdf:
# Select paths on a page
paths = pdf.page(1).select_paths()

print(f"Found {len(paths)} paths on page 0")

# You can iterate through paths
for i, path in enumerate(paths):
print(f"Path {i}: {path.internal_id}")

Modifying Path Colors​

Modify the stroke and fill colors of existing paths. Use the edit() method on a path reference to obtain a PathEdit builder, then chain color methods and call apply() to persist changes.

API Version

Path color modification is available via API v1. The v1 API uses 1-based page indexing.

Finalize Method: apply() vs add()

The new path color modification API (v1) uses .apply() to finalize builder operations, while the existing drawing API (v0) uses .add(). This is an intentional API version difference.

Basic Color Modification​

from pdfdancer import PDFDancer, Color

with PDFDancer.open("document.pdf") as pdf:
# Select a path to modify
paths = pdf.page(1).select_paths()
path = paths[0]

# Modify stroke color (RGB + alpha transparency)
path.edit().stroke_color(Color(255, 0, 0, 255)).apply()

# Modify fill color
path.edit().fill_color(Color(0, 255, 0, 200)).apply()

# Set both at once
path.edit() \
.stroke_color(Color(0, 0, 255)) \
.fill_color(Color(255, 255, 0)) \
.apply()

pdf.save("output.pdf")

Color Transparency​

The alpha component (0-255) controls transparency. Alpha defaults to 255 (fully opaque) when not specified.

# Semi-transparent red stroke (alpha = 128)
path.edit().stroke_color(Color(255, 0, 0, 128)).apply()

# Fully opaque (alpha = 255, default)
path.edit().stroke_color(Color(255, 0, 0)).apply()

Partial Updates​

Omit a color to leave it unchanged. Setting a color to null is not supported.

# Only change stroke, leave fill unchanged
path.edit().stroke_color(Color(0, 0, 255)).apply()

# Only change fill, leave stroke unchanged
path.edit().fill_color(Color(255, 0, 0)).apply()
Clearing Colors

Clearing stroke or fill colors is intentionally not supported. Passing null or omitting a color means "don't change". To remove a color from a path, consider recreating the path without that color property.

Error Handling​

Handle errors that may occur when modifying path colors, such as invalid path references or color values.

from pdfdancer import PDFDancer, Color
from pdfdancer.exceptions import PathNotFoundException, InvalidColorException

with PDFDancer.open("document.pdf") as pdf:
try:
paths = pdf.page(1).select_paths()
path = paths[0]

# Modify stroke color
path.edit().stroke_color(Color(255, 0, 0)).apply()
print("Color modified successfully")

except PathNotFoundException:
print("Error: Path does not exist")
except InvalidColorException:
print("Error: Invalid color value")
except Exception as e:
print(f"Error modifying path color: {e}")

Reading Path Colors​

After modifying a path, retrieve the updated color information using the v1 API snapshot which includes path styling data.

PathReference vs PathObjectRefV1

selectPaths() returns lightweight PathReference objects that support editing operations. To read styling properties like colors, use getSnapshotV1() which returns PathObjectRefV1 objects containing full path metadata including stroke/fill colors and dash patterns.

with PDFDancer.open("document.pdf") as pdf:
# Get v1 snapshot to see path colors
snapshot = pdf.page(1).get_snapshot(api_version=1)

for element in snapshot.elements:
if element.type == "PATH":
path_obj = element
print(f"Path {path_obj.internal_id}:")
print(f" Stroke: {path_obj.stroke_color}")
print(f" Fill: {path_obj.fill_color}")
print(f" Stroke width: {path_obj.stroke_width}")

PathObjectRefV1 Properties​

When using the v1 API, paths are returned as PathObjectRefV1 objects containing styling information:

PropertyTypeDescription
internal_id / internalId / getInternalId()StringUnique identifier for the path
typeStringObject type, always "PATH"
positionPositionPage and coordinates (v1 uses 1-based page numbers)
stroke_color / strokeColor / getStrokeColor()ColorStroke outline color (may be null)
fill_color / fillColor / getFillColor()ColorFill color (may be null)
stroke_width / strokeWidth / getStrokeWidth()DoubleStroke width in points (may be null)
dash_array / dashArray / getDashArray()double[]Dash pattern for dashed strokes
dash_phase / dashPhase / getDashPhase()DoubleDash pattern offset

Path Grouping​

Group multiple vector paths together and manipulate them as a unit. Path groups support moving, scaling, rotating, resizing, and removing all grouped paths at once.

Creating Path Groups​

You can create path groups by specifying explicit path IDs or by selecting all paths within a bounding region.

from pdfdancer import PDFDancer, BoundingRect

with PDFDancer.open("document.pdf") as pdf:
page = pdf.page(1)

# Select paths to group
paths = page.select_paths()
path_ids = [paths[0].internal_id, paths[1].internal_id]

# Group by explicit path IDs
group = page.group_paths(path_ids)

# Or group all paths within a bounding region
region = BoundingRect(x=70, y=710, width=100, height=100)
group = page.group_paths_in_region(region)

print(f"Group contains {group.path_count} paths")

Listing Path Groups​

groups = pdf.page(1).get_path_groups()

for group in groups:
print(f"Group {group.group_id}: {group.path_count} paths at ({group.x}, {group.y})")

Manipulating Path Groups​

Once grouped, paths can be moved, scaled, rotated, resized, or removed as a unit.

# Move the group to a new position
group.move_to(200.0, 300.0)

# Scale the group by a factor
group.scale(2.0)

# Rotate the group by degrees
group.rotate(90.0)

# Resize the group to specific dimensions
group.resize(50.0, 50.0)

# Remove the group and all its paths
group.remove()

Clearing Path Clipping​

Some PDFs use clipping paths to hide vector artwork. If a path or grouped logo is still present in the document model but no longer visible after you move it, clear the inherited clipping before saving.

with PDFDancer.open("document.pdf") as pdf:
page = pdf.page(1)
path = page.select_paths()[0]

# Detach clipping from a single path
path.clear_clipping()

# Detach clipping from a grouped set of paths
group = page.group_paths([path.internal_id])
group.clear_clipping()

pdf.save("output.pdf")

Path Group Properties​

PropertyPythonTypeScriptJava
Group IDgroup.group_idgroup.groupIdgroup.getGroupId()
Path countgroup.path_countgroup.pathCountgroup.getPathCount()
Bounding boxgroup.bounding_boxgroup.boundingBoxgroup.getBoundingBox()
X positiongroup.xgroup.xgroup.getX()
Y positiongroup.ygroup.ygroup.getY()

Drawing Lines​

Create straight lines on a page:

from pdfdancer import PDFDancer, Color

with PDFDancer.open("document.pdf") as pdf:
page = pdf.page(1)

# Draw a simple line
page.new_line() \
.from_point(100, 100) \
.to_point(400, 100) \
.stroke_color(Color(0, 0, 0)) \
.stroke_width(2) \
.add()

# Draw a dashed line
page.new_line() \
.from_point(100, 150) \
.to_point(400, 150) \
.stroke_color(Color(255, 0, 0)) \
.stroke_width(1) \
.dash_pattern([5, 3]) \
.add()

pdf.save("output.pdf")

Drawing Rectangles​

Create rectangles with stroke and fill:

from pdfdancer import PDFDancer, Color

with PDFDancer.open("document.pdf") as pdf:
page = pdf.page(1)

# Draw a stroked rectangle
page.new_rectangle() \
.at_coordinates(100, 200) \
.with_size(200, 100) \
.stroke_color(Color(0, 0, 255)) \
.stroke_width(3) \
.add()

# Draw a filled rectangle
page.new_rectangle() \
.at_coordinates(350, 200) \
.with_size(200, 100) \
.fill_color(Color(255, 200, 0)) \
.add()

# Draw a rectangle with both stroke and fill
page.new_rectangle() \
.at_coordinates(100, 350) \
.with_size(200, 100) \
.stroke_color(Color(0, 0, 0)) \
.stroke_width(2) \
.fill_color(Color(200, 200, 255)) \
.add()

pdf.save("output.pdf")

Drawing Bezier Curves​

Create smooth curves using cubic Bezier curves:

from pdfdancer import PDFDancer, Color

with PDFDancer.open("document.pdf") as pdf:
page = pdf.page(1)

# Draw a bezier curve
page.new_bezier() \
.from_point(100, 100) \
.control_point_1(200, 200) \
.control_point_2(300, 200) \
.to_point(400, 100) \
.stroke_color(Color(0, 128, 0)) \
.stroke_width(2) \
.add()

pdf.save("output.pdf")

PathBuilder: Programmatic Path Creation​

The newPath() method returns a builder that lets you create complex vector paths programmatically. This builder pattern allows you to chain commands to construct shapes, curves, and complex drawings.

TypeScript PathBuilder Class

In TypeScript SDK v1.0.22+, newPath() returns a PathBuilder instance that provides a fluent API for constructing vector paths. You can also import and use the PathBuilder class directly:

import { PathBuilder } from 'pdfdancer-client-typescript';

Builder Pattern Basics​

from pdfdancer import PDFDancer, Color

with PDFDancer.open("document.pdf") as pdf:
# new_path() returns a builder - chain commands and call add()
pdf.page(1).new_path() \
.add_line(Point(100, 100), Point(200, 200)) \
.stroke_color(Color(0, 0, 0)) \
.stroke_width(2) \
.add() # Finalizes and adds the path to the page

Advanced Fill Rules​

For complex shapes with overlapping regions, you can control the fill behavior:

// Even-odd fill rule for complex shapes
await pdf.page(1).newPath()
.moveTo(100, 100)
.lineTo(200, 100)
.lineTo(200, 200)
.lineTo(100, 200)
.closePath()
.fillColor(new Color(255, 0, 0))
.evenOddFill(true) // Use even-odd winding rule
.apply();
Even-Odd Fill Rule

The even-odd rule determines whether a point is inside a path by drawing a line from that point to infinity and counting how many times it crosses the path. If the count is odd, the point is inside; if even, it's outside. This is useful for creating shapes with holes or complex overlapping regions.

PathBuilder API Reference​

Complete list of PathBuilder methods:

Drawing Commands:

  • moveTo(x, y) - Move to a point without drawing (sets the current point)
  • lineTo(x, y) - Draw a line from current point to (x, y)
  • bezierTo(cp1x, cp1y, cp2x, cp2y, x, y) - Draw a cubic Bezier curve
  • closePath() - Draw a line back to the starting point

Styling:

  • strokeColor(Color) - Set the outline color
  • fillColor(Color) - Set the fill color
  • strokeWidth(number) - Set outline width in points
  • dashPattern([on, off, ...]) - Set dash pattern for strokes

Advanced:

  • dashPhase(number) - Set the offset for dash patterns
  • evenOddFill(boolean) - Use even-odd fill rule (TypeScript)

Finalize:

  • at(pageNumber, x, y) / at(x, y) - Set position and optionally page index
  • add() / apply() - Add the path to the document
Python Method Names

Python uses snake_case: move_to(), line_to(), stroke_color(), etc.


Drawing Complex Paths​

Create complex vector graphics using path commands:

from pdfdancer import PDFDancer, Color, Point

with PDFDancer.open("document.pdf") as pdf:
page = pdf.page(1)

# Draw a complex path (triangle)
page.new_path() \
.add_line(Point(250, 100), Point(350, 250)) \
.add_line(Point(350, 250), Point(150, 250)) \
.add_line(Point(150, 250), Point(250, 100)) \
.stroke_color(Color(128, 0, 128)) \
.stroke_width(3) \
.fill_color(Color(255, 200, 255)) \
.add()

# Draw a path with curves
page.new_path() \
.add_line(Point(100, 400), Point(200, 400)) \
.add_bezier(Point(200, 400), Point(250, 450), Point(300, 450), Point(350, 400)) \
.add_line(Point(350, 400), Point(450, 400)) \
.stroke_color(Color(255, 100, 0)) \
.stroke_width(2) \
.add()

pdf.save("output.pdf")

Path Commands​

Available path commands:

  • move_to(x, y) / moveTo(x, y) - Move to a point without drawing
  • line_to(x, y) / lineTo(x, y) - Draw a straight line to a point
  • add_bezier(...) / bezierTo(cp1x, cp1y, cp2x, cp2y, x, y) / curveTo(...) - Draw a cubic Bezier curve
  • close_path() / closePath() - Close the current path by drawing a line to the start point

Styling Options​

All vector graphics support these styling options:

Stroke (Outline)​

  • stroke_color(Color) / strokeColor(Color) - Set the outline color
  • stroke_width(number) / strokeWidth(number) - Set the outline width in points
  • dash_pattern([on, off, ...]) / dashPattern([on, off, ...]) - Create dashed lines

Fill​

  • fill_color(Color) / fillColor(Color) - Set the fill color for closed shapes
Stroke vs Fill
  • Use stroke for outlines and lines
  • Use fill for solid shapes
  • Use both for outlined shapes with a fill color
  • Omit both to create invisible paths (useful for clipping regions)

Next Steps​