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).
- Python
- TypeScript
- Java
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}")
import { PDFDancer } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
// Get all paths on a specific page
const paths = await pdf.page(3).selectPaths();
// Get paths at specific coordinates
const pathsAtPoint = await pdf.page(3).selectPathsAt(150, 320);
for (const path of paths) {
console.log(`Path ID: ${path.internalId}`);
}
import com.tfc.pdf.pdfdancer.api.PDFDancer;
import com.tfc.pdf.pdfdancer.api.common.model.*;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
// Get all paths on a specific page
List<Path> paths = pdf.page(3).selectPaths();
// Get paths at specific coordinates
List<Path> pathsAtPoint = pdf.page(3).selectPathsAt(150, 320);
for (Path path : paths) {
System.out.println("Path ID: " + path.getInternalId());
}
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
- Python
- TypeScript
- Java
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}")
const pdf = await PDFDancer.open('document.pdf');
// Select paths on a page
const paths = await pdf.page(1).selectPaths();
console.log(`Found ${paths.length} paths on page 0`);
// You can iterate through paths
for (const [i, path] of paths.entries()) {
console.log(`Path ${i}: ${path.internalId}`);
}
import com.tfc.pdf.pdfdancer.api.PDFDancer;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
// Select paths on a page
List<Path> paths = pdf.page(1).selectPaths();
System.out.println("Found " + paths.size() + " paths on page 0");
// You can iterate through paths
for (int i = 0; i < paths.size(); i++) {
Path path = paths.get(i);
System.out.println("Path " + i + ": " + path.getInternalId());
}
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.
- Python
- TypeScript
- Java
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")
import { PDFDancer, BoundingRect } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
const page = pdf.page(1);
// Select paths to group
const paths = await page.selectPaths();
const pathIds = [paths[0].internalId, paths[1].internalId];
// Group by explicit path IDs
const group = await page.groupPaths(pathIds);
// Or group all paths within a bounding region
const region = new BoundingRect(70, 710, 100, 100);
const regionGroup = await page.groupPathsInRegion(region);
console.log(`Group contains ${group.pathCount} paths`);
import com.pdfdancer.client.rest.PDFDancer;
import com.pdfdancer.client.rest.PathGroupReference;
import com.pdfdancer.common.model.BoundingRect;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
// Select paths to group
List<PathReference> paths = pdf.page(1).selectPaths();
List<String> pathIds = List.of(paths.get(0).getInternalId(), paths.get(1).getInternalId());
// Group by explicit path IDs
PathGroupReference group = pdf.page(1).groupPaths(pathIds);
// Or group all paths within a bounding region
BoundingRect region = new BoundingRect(70, 710, 100, 100);
PathGroupReference regionGroup = pdf.page(1).groupPathsInRegion(region);
System.out.println("Group contains " + group.getPathCount() + " paths");
Listing Path Groups
- Python
- TypeScript
- Java
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})")
const groups = await pdf.page(1).getPathGroups();
for (const group of groups) {
console.log(`Group ${group.pathCount} paths at (${group.x}, ${group.y})`);
}
List<PathGroupReference> groups = pdf.page(1).getPathGroups();
for (PathGroupReference group : groups) {
System.out.println("Group " + group.getGroupId() + ": " + group.getPathCount() + " paths");
}
Manipulating Path Groups
Once grouped, paths can be moved, scaled, rotated, resized, or removed as a unit.
- Python
- TypeScript
- Java
# 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()
// Move the group to a new position
await group.moveTo(200.0, 300.0);
// Scale the group by a factor
await group.scale(2.0);
// Rotate the group by degrees
await group.rotate(90.0);
// Resize the group to specific dimensions
await group.resize(50.0, 50.0);
// Remove the group and all its paths
await group.remove();
// Move the group to a new position
group.moveTo(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.
- Python
- TypeScript
- Java
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")
const pdf = await PDFDancer.open('document.pdf');
const page = pdf.page(1);
const path = (await page.selectPaths())[0];
// Detach clipping from a single path
await path.clearClipping();
// Detach clipping from a grouped set of paths
const group = await page.groupPaths([path.internalId]);
await group.clearClipping();
await pdf.save('output.pdf');
import com.pdfdancer.client.rest.PDFDancer;
import com.pdfdancer.client.rest.PathGroupReference;
import com.pdfdancer.client.rest.PathReference;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
PathReference path = pdf.page(1).selectPaths().get(0);
// Detach clipping from a single path
path.clearClipping();
// Detach clipping from a grouped set of paths
PathGroupReference group = pdf.page(1).groupPaths(java.util.List.of(path.getInternalId()));
group.clearClipping();
pdf.save("output.pdf");
Path Group Properties
| Property | Python | TypeScript | Java |
|---|---|---|---|
| Group ID | group.group_id | group.groupId | group.getGroupId() |
| Path count | group.path_count | group.pathCount | group.getPathCount() |
| Bounding box | group.bounding_box | group.boundingBox | group.getBoundingBox() |
| X position | group.x | group.x | group.getX() |
| Y position | group.y | group.y | group.getY() |
Drawing Lines
Create straight lines on a page:
- Python
- TypeScript
- Java
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")
import { PDFDancer, Color } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
const page = pdf.page(1);
// Draw a simple line
await page.newLine()
.fromPoint(100, 100)
.toPoint(400, 100)
.strokeColor(new Color(0, 0, 0))
.strokeWidth(2)
.apply();
// Draw a dashed line
await page.newLine()
.fromPoint(100, 150)
.toPoint(400, 150)
.strokeColor(new Color(255, 0, 0))
.strokeWidth(1)
.dashPattern([5, 3])
.apply();
await pdf.save('output.pdf');
import com.tfc.pdf.pdfdancer.api.PDFDancer;
import com.tfc.pdf.pdfdancer.api.common.model.*;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
Page page = pdf.page(1);
// Draw a simple line
page.newLine()
.fromPoint(100, 100)
.toPoint(400, 100)
.strokeColor(new Color(0, 0, 0))
.strokeWidth(2)
.add();
// Draw a dashed line
page.newLine()
.fromPoint(100, 150)
.toPoint(400, 150)
.strokeColor(new Color(255, 0, 0))
.strokeWidth(1)
.dashPattern(new int[]{5, 3})
.add();
pdf.save("output.pdf");
Drawing Rectangles
Create rectangles with stroke and fill:
- Python
- TypeScript
- Java
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")
import { PDFDancer, Color } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
const page = pdf.page(1);
// Draw a stroked rectangle
await page.newRectangle()
.at(100, 200)
.size(200, 100)
.strokeColor(new Color(0, 0, 255))
.strokeWidth(3)
.apply();
// Draw a filled rectangle
await page.newRectangle()
.at(350, 200)
.size(200, 100)
.fillColor(new Color(255, 200, 0))
.apply();
// Draw a rectangle with both stroke and fill
await page.newRectangle()
.at(100, 350)
.size(200, 100)
.strokeColor(new Color(0, 0, 0))
.strokeWidth(2)
.fillColor(new Color(200, 200, 255))
.apply();
await pdf.save('output.pdf');
import com.tfc.pdf.pdfdancer.api.PDFDancer;
import com.tfc.pdf.pdfdancer.api.common.model.*;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
Page page = pdf.page(1);
// Draw a stroked rectangle
page.newRectangle()
.at(100, 200)
.size(200, 100)
.strokeColor(new Color(0, 0, 255))
.strokeWidth(3)
.add();
// Draw a filled rectangle
page.newRectangle()
.at(350, 200)
.size(200, 100)
.fillColor(new Color(255, 200, 0))
.add();
// Draw a rectangle with both stroke and fill
page.newRectangle()
.at(100, 350)
.size(200, 100)
.strokeColor(new Color(0, 0, 0))
.strokeWidth(2)
.fillColor(new Color(200, 200, 255))
.add();
pdf.save("output.pdf");
Drawing Bezier Curves
Create smooth curves using cubic Bezier curves:
- Python
- TypeScript
- Java
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")
import { PDFDancer, Color } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
const page = pdf.page(1);
// Draw a bezier curve
await page.newBezier()
.startPoint(100, 100)
.controlPoint1(200, 200)
.controlPoint2(300, 200)
.endPoint(400, 100)
.strokeColor(new Color(0, 128, 0))
.strokeWidth(2)
.apply();
await pdf.save('output.pdf');
import com.tfc.pdf.pdfdancer.api.PDFDancer;
import com.tfc.pdf.pdfdancer.api.common.model.*;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
Page page = pdf.page(1);
// Draw a bezier curve
page.newBezier()
.startPoint(100, 100)
.controlPoint1(200, 200)
.controlPoint2(300, 200)
.endPoint(400, 100)
.strokeColor(new Color(0, 128, 0))
.strokeWidth(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.
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
- Python
- TypeScript
- Java
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
import { PDFDancer, Color } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
// newPath() returns a PathBuilder - chain commands and call apply()
await pdf.page(1).newPath()
.moveTo(100, 100)
.lineTo(200, 200)
.strokeColor(new Color(0, 0, 0))
.strokeWidth(2)
.apply(); // Finalizes and adds the path to the page
import com.pdfdancer.client.rest.*;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
// newPath() returns a builder - chain commands and call add()
pdf.page(1).newPath()
.moveTo(100, 100)
.lineTo(200, 200)
.strokeColor(new Color(0, 0, 0))
.strokeWidth(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:
- TypeScript
- Python
- Java
// 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 supported in Python SDK
# (Check API documentation for current implementation status)
// Even-odd fill rule supported in Java SDK
// (Check API documentation for current implementation status)
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 curveclosePath()- Draw a line back to the starting point
Styling:
strokeColor(Color)- Set the outline colorfillColor(Color)- Set the fill colorstrokeWidth(number)- Set outline width in pointsdashPattern([on, off, ...])- Set dash pattern for strokes
Advanced:
dashPhase(number)- Set the offset for dash patternsevenOddFill(boolean)- Use even-odd fill rule (TypeScript)
Finalize:
at(pageNumber, x, y)/at(x, y)- Set position and optionally page indexadd()/apply()- Add the path to the document
Python uses snake_case: move_to(), line_to(), stroke_color(), etc.
Drawing Complex Paths
Create complex vector graphics using path commands:
- Python
- TypeScript
- Java
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")
import { PDFDancer, Color } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
const page = pdf.page(1);
// Draw a complex path (triangle)
await page.newPath()
.moveTo(250, 100)
.lineTo(350, 250)
.lineTo(150, 250)
.closePath()
.strokeColor(new Color(128, 0, 128))
.strokeWidth(3)
.fillColor(new Color(255, 200, 255))
.apply();
// Draw a path with curves
await page.newPath()
.moveTo(100, 400)
.lineTo(200, 400)
.bezierTo(250, 450, 300, 450, 350, 400)
.lineTo(450, 400)
.strokeColor(new Color(255, 100, 0))
.strokeWidth(2)
.apply();
await pdf.save('output.pdf');
import com.tfc.pdf.pdfdancer.api.PDFDancer;
import com.tfc.pdf.pdfdancer.api.common.model.*;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
Page page = pdf.page(1);
// Draw a complex path (triangle)
page.newPath()
.moveTo(250, 100)
.lineTo(350, 250)
.lineTo(150, 250)
.closePath()
.strokeColor(new Color(128, 0, 128))
.strokeWidth(3)
.fillColor(new Color(255, 200, 255))
.add();
// Draw a path with curves
page.newPath()
.moveTo(100, 400)
.lineTo(200, 400)
.curveTo(250, 450, 300, 450, 350, 400)
.lineTo(450, 400)
.strokeColor(new Color(255, 100, 0))
.strokeWidth(2)
.add();
pdf.save("output.pdf");
Path Commands
Available path commands:
move_to(x, y)/moveTo(x, y)- Move to a point without drawingline_to(x, y)/lineTo(x, y)- Draw a straight line to a pointadd_bezier(...)/bezierTo(cp1x, cp1y, cp2x, cp2y, x, y)/curveTo(...)- Draw a cubic Bezier curveclose_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 colorstroke_width(number)/strokeWidth(number)- Set the outline width in pointsdash_pattern([on, off, ...])/dashPattern([on, off, ...])- Create dashed lines
Fill
fill_color(Color)/fillColor(Color)- Set the fill color for closed shapes
- 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
- Working with Fonts – Use custom fonts in your PDFs
- Details about Positioning – Understand PDF coordinate systems
- Cookbook – See complete working examples