Working with Fonts
PDFDancer supports both standard PDF fonts and custom TrueType fonts. You can use system fonts or upload your own font files.
Standard PDF Fonts
PDF includes 14 standard fonts that are always available in all PDF readers without embedding:
Times Family (Serif)
Times-Roman- Standard serif fontTimes-Bold- Bold variantTimes-Italic- Italic variantTimes-BoldItalic- Bold italic variant
Helvetica Family (Sans-serif)
Helvetica- Standard sans-serif fontHelvetica-Bold- Bold variantHelvetica-Oblique- Oblique (italic) variantHelvetica-BoldOblique- Bold oblique variant
Courier Family (Monospace)
Courier- Standard monospace fontCourier-Bold- Bold variantCourier-Oblique- Oblique (italic) variantCourier-BoldOblique- Bold oblique variant
Symbol Fonts
Symbol- Mathematical and special charactersZapfDingbats- Decorative symbols
Using Standard Fonts
- Python
- TypeScript
- Java
from pdfdancer import PDFDancer, StandardFonts, Color
with PDFDancer.open("document.pdf") as pdf:
# Use standard fonts with enum (recommended)
pdf.new_paragraph() \
.text("Helvetica Bold text") \
.font(StandardFonts.HELVETICA_BOLD.value, 16) \
.color(Color(255, 0, 0)) \
.at(page_number=1, x=100, y=500) \
.add()
pdf.new_paragraph() \
.text("Times Roman text") \
.font(StandardFonts.TIMES_ROMAN.value, 14) \
.at(page_number=1, x=100, y=480) \
.add()
pdf.new_paragraph() \
.text("Courier monospace code") \
.font(StandardFonts.COURIER_BOLD.value, 12) \
.at(page_number=1, x=100, y=460) \
.add()
# You can also use font names directly as strings
pdf.new_paragraph() \
.text("Direct font name") \
.font("Helvetica", 12) \
.at(page_number=1, x=100, y=440) \
.add()
pdf.save("output.pdf")
import { PDFDancer, StandardFonts, Color } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
// Use standard fonts with enum (recommended)
await pdf.page(1).newParagraph()
.text('Helvetica Bold text')
.font(StandardFonts.HELVETICA_BOLD, 16)
.color(new Color(255, 0, 0))
.at(100, 500)
.apply();
await pdf.page(1).newParagraph()
.text('Times Roman text')
.font(StandardFonts.TIMES_ROMAN, 14)
.at(100, 480)
.apply();
await pdf.page(1).newParagraph()
.text('Courier monospace code')
.font(StandardFonts.COURIER_BOLD, 12)
.at(100, 460)
.apply();
// You can also use font names directly as strings
await pdf.page(1).newParagraph()
.text('Direct font name')
.font('Helvetica', 12)
.at(100, 440)
.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");
// Use standard fonts with enum (recommended)
pdf.newParagraph()
.text("Helvetica Bold text")
.font(StandardFonts.HELVETICA_BOLD.getValue(), 16)
.color(new Color(255, 0, 0))
.at(1, 100, 500)
.add();
pdf.newParagraph()
.text("Times Roman text")
.font(StandardFonts.TIMES_ROMAN.getValue(), 14)
.at(1, 100, 480)
.add();
pdf.newParagraph()
.text("Courier monospace code")
.font(StandardFonts.COURIER_BOLD.getValue(), 12)
.at(1, 100, 460)
.add();
// You can also use font names directly as strings
pdf.newParagraph()
.text("Direct font name")
.font("Helvetica", 12)
.at(1, 100, 440)
.add();
pdf.save("output.pdf");
About Embedded Fonts
What Are Embedded Fonts?
Embedded fonts are font files that are included directly in a PDF document. When you open a PDF with embedded fonts, you can view the document exactly as intended without needing the original font files installed on your system. This is different from the 14 standard PDF fonts, which are built into every PDF reader.
PDF Font Types
PDF supports several font format types, each with different characteristics:
Type 1 Fonts (PostScript)
- Traditional PostScript fonts used in professional publishing
- Compact and efficient
- Limited to 256 glyphs per font
- Can be embedded as complete font or subset
Type 3 Fonts (User-Defined)
- Custom bitmap or vector fonts defined in PDF
- Most flexible but least efficient
- Can contain any graphical content
- Often used for decorative or custom glyphs
- Not searchable or extractable - characters are stored as graphics, not text
- No hinting information for screen display
TrueType Fonts
- Common font format from Microsoft and Apple
- Excellent screen rendering
- Can contain large character sets (Unicode support)
- Usually embedded as subsets to reduce file size
OpenType/CFF Fonts
- Modern font format combining TrueType and PostScript features
- Superior typography features
- Cross-platform compatibility
- Large character set support
Why Embedded Fonts Have Limitations
When you edit text in a PDF that uses embedded fonts, you may encounter several limitations:
1. Subset Restrictions
- Most PDFs embed only the characters actually used (font subsetting)
- If you try to add new characters not in the original text, they won't be available
- Example: Original PDF has "Hello" - trying to change it to "Help!" might fail because "p" and "!" weren't in the subset
2. Type 3 Font Issues
- Type 3 fonts are rendered as graphics, not text
- Modifying Type 3 font text often requires converting to a different font type
- Search and copy operations may not work properly
3. Licensing and Encoding
- Some embedded fonts include usage restrictions
- Character encoding may be custom or non-standard
- Font metrics might not support all glyph variations
4. No Font File Access
- The embedded data is just enough to render existing characters
- You can't add new glyphs or extend the character set
- Font hinting and kerning data may be limited
Working with Embedded Fonts in PDFDancer
When you modify text that uses embedded fonts, PDFDancer will:
- Check character availability - Verify if the new text can be encoded with the embedded font
- Return warnings - The
CommandResultwill include awarningif there are encoding issues - Provide font recommendations - The
TextStatusincludes font recommendations with similarity scores - Indicate font type - Check
FontTypeto see if the font is EMBEDDED, STANDARD, or SYSTEM
- Python
- TypeScript
- Java
from pdfdancer import PDFDancer
with PDFDancer.open("document.pdf") as pdf:
paragraphs = pdf.page(1).select_paragraphs()
for para in paragraphs:
if para.status:
# Check font type
if para.status.get_font_type().value == "EMBEDDED":
print(f"⚠️ Embedded font: {para.font_name}")
# Check if text is encodable
if not para.status.is_encodable():
print("Cannot encode new characters with this font")
# Get font recommendation
rec = para.status.get_font_recommendation()
print(f"Recommended: {rec.get_font_name()} (similarity: {rec.get_similarity_score():.2f})")
# Attempt to modify embedded font text
para = paragraphs[0]
result = para.edit().replace("New text").apply()
if result.warning:
print(f"Warning: {result.warning}")
# Consider using a standard font instead
result = para.edit().replace("New text").font("Helvetica", 12).apply()
import { PDFDancer, FontType } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
const paragraphs = await pdf.page(1).selectParagraphs();
for (const para of paragraphs) {
if (para.objectRef().status) {
// Check font type
if (para.objectRef().status.getFontType() === FontType.EMBEDDED) {
console.log(`⚠️ Embedded font: ${para.getFontName()}`);
// Check if text is encodable
if (!para.objectRef().status.isEncodable()) {
console.log('Cannot encode new characters with this font');
}
// Get font recommendation
const fontInfo = para.objectRef().status.getFontRecommendation();
console.log(`Recommended: ${fontInfo.getDocumentFontName()}`);
}
}
}
// Attempt to modify embedded font text
const para = paragraphs[0];
const result = await para.edit().replace('New text').apply();
if (result.warning) {
console.warn('Warning:', result.warning);
// Consider using a standard font instead
await para.edit().replace('New text').font('Helvetica', 12).apply();
}
import com.tfc.pdf.pdfdancer.api.PDFDancer;
import com.tfc.pdf.pdfdancer.api.common.model.*;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
List<Paragraph> paragraphs = pdf.page(1).selectParagraphs();
for (Paragraph para : paragraphs) {
if (para.getStatus() != null) {
// Check font type
if (para.getStatus().getFontType().getValue().equals("EMBEDDED")) {
System.out.println("Embedded font: " + para.getFontName());
// Check if text is encodable
if (!para.getStatus().isEncodable()) {
System.out.println("Cannot encode new characters with this font");
}
// Get font recommendation
FontRecommendation rec = para.getStatus().getFontRecommendation();
System.out.println("Recommended: " + rec.getFontName() +
" (similarity: " + rec.getSimilarityScore() + ")");
}
}
}
// Attempt to modify embedded font text
Paragraph para = paragraphs.get(0);
CommandResult result = para.edit().replace("New text").apply();
if (result.getWarning() != null) {
System.out.println("Warning: " + result.getWarning());
// Consider using a standard font instead
result = para.edit().replace("New text").font("Helvetica", 12).apply();
}
Best Practices for Embedded Fonts
- Check
TextStatusbefore editing - Know what you're working with - Use standard fonts when possible - They have no character limitations
- Handle warnings gracefully - Check
CommandResult.warningafter modifications - Consider font substitution - If editing fails, switch to a similar standard font
- Test with actual content - Verify that your new text renders correctly
Registering Custom Fonts
You can upload and use your own TrueType (.ttf) fonts in three ways:
Method 1: Using font_file() directly (recommended)
- Python
- TypeScript
- Java
from pathlib import Path
from pdfdancer import PDFDancer, Color
with PDFDancer.open("document.pdf") as pdf:
# Use custom font directly without registration
ttf_path = Path("fonts/DancingScript-Regular.ttf")
pdf.new_paragraph() \
.text("Beautiful custom font\nwith multiple lines") \
.font_file(ttf_path, 24) \
.line_spacing(1.8) \
.color(Color(0, 0, 255)) \
.at(page_number=1, x=300, y=500) \
.add()
pdf.save("output.pdf")
import { PDFDancer } from 'pdfdancer-client-typescript';
import { promises as fs } from 'node:fs';
const pdf = await PDFDancer.open('document.pdf');
// Load custom font file
const fontBytes = await fs.readFile('fonts/DancingScript-Regular.ttf');
// Use the custom font directly with fontFile()
await pdf.page(1).newParagraph()
.text('Beautiful custom font\nwith multiple lines')
.fontFile(fontBytes, 24)
.lineSpacing(1.8)
.color(new Color(0, 0, 255))
.at(300, 500)
.apply();
await pdf.save('output.pdf');
import com.tfc.pdf.pdfdancer.api.PDFDancer;
import com.tfc.pdf.pdfdancer.api.common.model.*;
import java.nio.file.Path;
import java.nio.file.Paths;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
// Use custom font file directly
Path ttfPath = Paths.get("fonts/DancingScript-Regular.ttf");
pdf.newParagraph()
.text("Beautiful custom font\nwith multiple lines")
.fontFile(ttfPath, 24)
.lineSpacing(1.8)
.color(new Color(0, 0, 255))
.at(1, 300, 500)
.add();
pdf.save("output.pdf");
Method 2: Find and use fonts available on the service
- Python
- TypeScript
- Java
from pdfdancer import PDFDancer
with PDFDancer.open("document.pdf") as pdf:
# Search for fonts available on the service
fonts = pdf.find_fonts("Roboto", 14)
if fonts:
# Use the first match (e.g., "Roboto-Regular")
font = fonts[0]
print(f"Using: {font.name} at {font.size}pt")
pdf.new_paragraph() \
.text("Text with service font") \
.font(font.name, font.size) \
.at(page_number=1, x=300, y=500) \
.add()
pdf.save("output.pdf")
// TypeScript client uses font names directly
const pdf = await PDFDancer.open('document.pdf');
await pdf.page(1).newParagraph()
.text('Text with service font')
.font('Roboto-Regular', 14)
.at(300, 500)
.apply();
await pdf.save('output.pdf');
PDFDancer pdf = PDFDancer.createSession("document.pdf");
// Search for fonts available on the service
List<Font> fonts = pdf.findFonts("Roboto", 14);
if (!fonts.isEmpty()) {
// Use the first match (e.g., "Roboto-Regular")
Font font = fonts.get(0);
System.out.println("Using: " + font.getName() + " at " + font.getSize() + "pt");
pdf.newParagraph()
.text("Text with service font")
.font(font.getName(), font.getSize())
.at(1, 300, 500)
.add();
}
pdf.save("output.pdf");
Method 3: Register custom fonts for reuse
- Python
- TypeScript
- Java
from pathlib import Path
from pdfdancer import PDFDancer
with PDFDancer.open("document.pdf") as pdf:
# Register custom TTF font
custom_font_path = Path("fonts/CustomFont.ttf")
pdf.register_font(str(custom_font_path))
# Now use the registered font by name
pdf.new_paragraph() \
.text("Text with registered font") \
.font("CustomFont", 14) \
.at(page_number=1, x=100, y=500) \
.add()
pdf.save("output.pdf")
// TypeScript doesn't have explicit font registration
// Use fontFile() method instead
import java.nio.file.Paths;
PDFDancer pdf = PDFDancer.createSession("document.pdf");
// Register custom TTF font
pdf.registerFont(Paths.get("fonts/CustomFont.ttf").toString());
// Now use the registered font by name
pdf.newParagraph()
.text("Text with registered font")
.font("CustomFont", 14)
.at(1, 100, 500)
.add();
pdf.save("output.pdf");
Font Sizes and Styling
You can combine standard fonts with different sizes, colors, and line spacing for rich typography:
- Python
- TypeScript
- Java
from pdfdancer import PDFDancer, StandardFonts, Color
with PDFDancer.open("document.pdf") as pdf:
# Large heading with bold Helvetica
pdf.new_paragraph() \
.text("Large heading") \
.font(StandardFonts.HELVETICA_BOLD.value, 24) \
.color(Color(0, 0, 0)) \
.at(page_number=1, x=100, y=700) \
.add()
# Normal body text with Times Roman
pdf.new_paragraph() \
.text("Normal body text in Times Roman") \
.font(StandardFonts.TIMES_ROMAN.value, 12) \
.at(page_number=1, x=100, y=660) \
.add()
# Monospace code example with Courier
pdf.new_paragraph() \
.text("def hello():\n print('Hello')") \
.font(StandardFonts.COURIER_BOLD.value, 11) \
.line_spacing(1.5) \
.color(Color(40, 40, 40)) \
.at(page_number=1, x=100, y=620) \
.add()
# Small footnote
pdf.new_paragraph() \
.text("Small footnote") \
.font(StandardFonts.HELVETICA.value, 8) \
.color(Color(128, 128, 128)) \
.at(page_number=1, x=100, y=580) \
.add()
pdf.save("output.pdf")
import { PDFDancer, StandardFonts, Color } from 'pdfdancer-client-typescript';
const pdf = await PDFDancer.open('document.pdf');
// Large heading with bold Helvetica
await pdf.page(1).newParagraph()
.text('Large heading')
.font(StandardFonts.HELVETICA_BOLD, 24)
.color(new Color(0, 0, 0))
.at(100, 700)
.apply();
// Normal body text with Times Roman
await pdf.page(1).newParagraph()
.text('Normal body text in Times Roman')
.font(StandardFonts.TIMES_ROMAN, 12)
.at(100, 660)
.apply();
// Monospace code example with Courier
await pdf.page(1).newParagraph()
.text('def hello():\n print(\'Hello\')')
.font(StandardFonts.COURIER_BOLD, 11)
.lineSpacing(1.5)
.color(new Color(40, 40, 40))
.at(100, 620)
.apply();
// Small footnote
await pdf.page(1).newParagraph()
.text('Small footnote')
.font(StandardFonts.HELVETICA, 8)
.color(new Color(128, 128, 128))
.at(100, 580)
.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");
// Large heading with bold Helvetica
pdf.newParagraph()
.text("Large heading")
.font(StandardFonts.HELVETICA_BOLD.getValue(), 24)
.color(new Color(0, 0, 0))
.at(1, 100, 700)
.add();
// Normal body text with Times Roman
pdf.newParagraph()
.text("Normal body text in Times Roman")
.font(StandardFonts.TIMES_ROMAN.getValue(), 12)
.at(1, 100, 660)
.add();
// Monospace code example with Courier
pdf.newParagraph()
.text("def hello():\n print('Hello')")
.font(StandardFonts.COURIER_BOLD.getValue(), 11)
.lineSpacing(1.5)
.color(new Color(40, 40, 40))
.at(1, 100, 620)
.add();
// Small footnote
pdf.newParagraph()
.text("Small footnote")
.font(StandardFonts.HELVETICA.getValue(), 8)
.color(new Color(128, 128, 128))
.at(1, 100, 580)
.add();
pdf.save("output.pdf");
Next Steps
- Working with Text – Learn about text modification and status information
- Details about Positioning – Understand PDF coordinate systems
- Cookbook – See complete working examples with fonts
- Error Handling – Handle font not found errors