Introduction
Aspose.PDF FOSS for Java is an MIT-licensed Java library for creating and editing PDF
documents. Its classes are organized under the org.aspose.pdf package and its
sub-packages (org.aspose.pdf.annotations, org.aspose.pdf.forms,
org.aspose.pdf.facades, and others), and Document is the entry point for most
workflows. The library has no third-party runtime dependencies; the only dependency in its
Maven build is JUnit, at test scope.
The library announcement, the
facades post, and the
page-editing post already cover document creation,
form fields, the facade classes for extraction, encryption, and stamping, and page
operations with PdfFileEditor. This post covers five other areas of the same API that
those posts do not demonstrate: text-markup annotations, free-text callouts,
document-level actions, a size guard on stream decoding, and diagnostic logging.
Each area is a small, self-contained API. The sections below show the calls, the defaults, and the edge cases that the library’s own tests pin down.
Key Features
Text Markup Annotations
HighlightAnnotation, UnderlineAnnotation, StrikeOutAnnotation, and
SquigglyAnnotation each take a Page and a Rectangle. The constructor derives the
annotation’s quad points from the rectangle, so getQuadPoints() returns eight values
without any manual calculation. For the rectangle (100, 200, 300, 250) the result is
[100, 250, 300, 250, 100, 200, 300, 200]: top-left, top-right, bottom-left, then
bottom-right. setQuadPoints() replaces the derived values with your own eight-value
array, and an annotation rebuilt from an existing dictionary keeps the quad points already
stored there. getSubtype() returns the PDF subtype name: Highlight, Underline,
StrikeOut, or Squiggly.
Constructing an annotation does not attach it to the page. Add it with
page.getAnnotations().add(...).
try (Document doc = new Document()) {
Page page = doc.getPages().add();
HighlightAnnotation highlight = new HighlightAnnotation(page, new Rectangle(100, 200, 300, 250));
double[] quadPoints = highlight.getQuadPoints();
UnderlineAnnotation underline = new UnderlineAnnotation(page, new Rectangle(50, 100, 200, 120));
StrikeOutAnnotation strikeOut = new StrikeOutAnnotation(page, new Rectangle(50, 140, 200, 160));
SquigglyAnnotation squiggly = new SquigglyAnnotation(page, new Rectangle(50, 180, 200, 200));
page.getAnnotations().add(highlight);
page.getAnnotations().add(underline);
page.getAnnotations().add(strikeOut);
page.getAnnotations().add(squiggly);
doc.save("markup.pdf");
}
Free-Text Callouts
FreeTextAnnotation places text directly on a page. Its three-argument constructor takes a
DefaultAppearance, which carries the font name, font size, and text color. Three
properties shape the callout:
setIntent()takes aFreeTextIntent:FreeText,FreeTextCallout, orFreeTextTypeWriter. A new annotation reportsUndefined, and passingnullreturns it to that state.setCallout()takes the callout line as an array of two or three{x, y}points. An array of any other length is ignored and the existing callout stays in place;nullremoves the callout.setEndingStyle()takes aLineEndingvalue such asOpenArroworDiamond. A new annotation reportsLineEnding.None.
try (Document doc = new Document()) {
Page page = doc.getPages().add();
DefaultAppearance da = new DefaultAppearance("Helv", 10, Color.BLACK);
FreeTextAnnotation note = new FreeTextAnnotation(page, new Rectangle(50, 50, 200, 100), da);
note.setContents("Check this value");
note.setIntent(FreeTextIntent.FreeTextCallout);
note.setCallout(new double[][] {{10, 10}, {50, 50}, {100, 100}});
note.setEndingStyle(LineEnding.OpenArrow);
page.getAnnotations().add(note);
doc.save("callout.pdf");
}
The saved annotation dictionary stores these values as /IT, /CL, and /LE.
Document-Level Actions
Document.getActions() returns a DocumentActions view of the document catalog.
setOpenAction() stores an action in the catalog’s /OpenAction entry. Five further
triggers, setBeforeClosing(), setBeforeSaving(), setAfterSaving(),
setBeforePrinting(), and setAfterPrinting(), are stored independently in the catalog’s
/AA additional-actions dictionary.
Each getter returns null until its trigger is set. Passing null to a setter removes that
entry, and removing the last trigger also removes the /AA dictionary. Actions are
PdfAction instances; GoToURIAction is an alias of UriAction, and its getType()
returns URI. The DocumentActions object is a live view, so a later call to
getActions() sees changes made through an earlier one, and the triggers are read back
when the saved file is opened again.
try (Document doc = new Document()) {
doc.getPages().add();
DocumentActions actions = doc.getActions();
actions.setOpenAction(new GoToURIAction("https://example.com"));
actions.setBeforeSaving(new GoToURIAction("https://example.com/saving"));
PdfAction open = doc.getActions().getOpenAction();
if (open instanceof UriAction) {
System.out.println(((UriAction) open).getUri());
}
actions.setBeforeSaving(null); // removes /WS; /AA is removed once it is empty
doc.save("actions.pdf");
}
Decode Limits for Stream Filters
PDF streams are stored in encoded form, and a corrupt or malicious stream can expand to far
more than its stored size. DecodeLimits is a guard shared by the FlateDecode, LZWDecode,
and RunLengthDecode filters. When a stream’s decoded output exceeds the cap, the filter
throws DecodeSizeLimitException, a subclass of IOException, instead of decoding until
the heap is exhausted.
The default cap is 256 MB per decoded stream (DecodeLimits.DEFAULT_MAX_DECODED_BYTES).
To change it, set the system property named by DecodeLimits.PROPERTY, which is
aspose.pdf.maxDecodedStreamBytes, to a size in bytes; a value of 0 or less disables the
guard. The property is read on each decode, so it can be changed at runtime.
static byte[] decodeFlate(byte[] encoded) {
// Lower the cap to 16 MB (16777216 bytes) for this process
System.setProperty(DecodeLimits.PROPERTY, "16777216");
try {
return new FlateFilter().decode(encoded, null);
} catch (IOException e) {
// "FlateDecode: decoded output exceeds 16777216 bytes - likely a corrupt stream
// or decompression bomb (override with -Daspose.pdf.maxDecodedStreamBytes)"
return null;
}
}
Streams under the cap decode as usual. FlateFilter and RunLengthFilter both round-trip
data through encode() and decode() when the output stays under the limit.
Diagnostic Logging
The library logs through java.util.logging under the org.aspose.pdf logger and is
silent by default: the level is OFF. AsposePdfLogging turns logging on.
setLevel(Level)sets the level from code;nullreturns the library toOFF.getLevel()reads it back.- The
aspose.pdf.logsystem property (also available asAsposePdfLogging.LOG_PROPERTY) sets it from the command line.configureFromSystemProperty()applies the property and also runs when the class loads. The valuesonandwarningselectWARNING,verboseselectsFINE,debugselectsALL, anyjava.util.logging.Levelname such asSEVEREis accepted, and an unrecognized value falls back toOFF.
WARNING lets the engine’s warnings through; FINE, the verbose setting, also lets the
parser’s recovery details through. AsposePdfLogging changes only the org.aspose.pdf
logger subtree. It does not change the level of the root logger, and the library logger does
not pass records to the root logger’s handlers. If logging is enabled and no handler is
attached to the library logger, a console handler is installed.
// Equivalent to starting the JVM with -Daspose.pdf.log=warning
AsposePdfLogging.setLevel(Level.WARNING);
System.setProperty(AsposePdfLogging.LOG_PROPERTY, "verbose");
AsposePdfLogging.configureFromSystemProperty();
System.out.println(AsposePdfLogging.getLevel()); // FINE
Quick Start
Add the dependency to your build:
<dependency>
<groupId>org.aspose</groupId>
<artifactId>aspose-pdf-foss</artifactId>
<version>26.8.0</version>
</dependency>The following example creates a page, adds a highlight and a free-text callout, and saves the document:
import org.aspose.pdf.*;
import org.aspose.pdf.annotations.*;
try (Document doc = new Document()) {
Page page = doc.getPages().add();
page.getAnnotations().add(new HighlightAnnotation(page, new Rectangle(100, 200, 300, 250)));
FreeTextAnnotation note = new FreeTextAnnotation(page, new Rectangle(50, 50, 200, 100),
new DefaultAppearance("Helv", 10, Color.BLACK));
note.setContents("Check this value");
note.setIntent(FreeTextIntent.FreeTextCallout);
note.setCallout(new double[][] {{10, 10}, {50, 50}, {100, 100}});
page.getAnnotations().add(note);
doc.save("core-features.pdf");
}
Supported Formats
Format support as confirmed by the library’s load and save options and rendering devices:
| Format | Extension | Read | Write |
|---|---|---|---|
| ✓ | ✓ | ||
| HTML | html | ✓ | ✓ |
| DOCX | docx | ✓ | ✓ |
| DOC | doc | ✓ | ✓ |
| XFDF | xfdf | ✓ | ✓ |
| BMP | bmp | — | ✓ |
| GIF | gif | — | ✓ |
| JPEG | jpeg | — | ✓ |
| TIFF | tiff | — | ✓ |
| Text | txt | — | ✓ |
Open Source & Licensing
Aspose.PDF FOSS for Java is released under the MIT license, which permits commercial use, modification, and redistribution. The source code and issue tracker are at github.com/aspose-pdf-foss/Aspose.PDF-FOSS-for-Java. The library targets Java 11 or later.