flattenAnnotations method
Implementation
Future<int> flattenAnnotations({List<int>? pages, bool widgetsOnly = false}) async {
int flattened = 0;
final List<int> targets = pages ?? List<int>.generate(document.pageCount, (int index) => index);
for (final int index in targets) {
final UPdfPage? page = await document.page(index);
final UPdfDict? mutable = await mutablePage(index);
if (page == null || mutable == null) continue;
final Object? list = await document.resolve(mutable["Annots"]);
if (list is! List<Object?>) continue;
final List<Object?> kept = <Object?>[];
final Map<String, Object?> stamps = <String, Object?>{};
final StringBuffer content = StringBuffer();
int counter = 0;
for (final Object? entry in list) {
final Object? annotation = await document.resolve(entry);
if (annotation is! UPdfDict) continue;
final Object? subtype = annotation["Subtype"];
final String type = subtype is UPdfName ? subtype.value : "";
if (type == "Link" || type == "Popup") {
kept.add(entry);
continue;
}
if (widgetsOnly && type != "Widget") {
kept.add(entry);
continue;
}
final Object? appearance = await document.resolve(annotation["AP"]);
if (appearance is! UPdfDict) continue;
Object? normal = await document.resolve(appearance["N"]);
if (normal is UPdfDict && normal is! UPdfStream) {
final Object? stateObject = await document.resolve(annotation["AS"]);
final String state = stateObject is UPdfName ? stateObject.value : "";
final UPdfDict states = normal;
normal = await document.resolve(state.isNotEmpty ? states[state] : (states.keys.isEmpty ? null : states[states.keys.first]));
}
if (normal is! UPdfStream) continue;
final Object? rectObject = await document.resolve(annotation["Rect"]);
final Rect rect = uPdfRectFromArray(rectObject is List<Object?> ? rectObject : null, fallback: Rect.zero);
if (rect.width <= 0 || rect.height <= 0) continue;
final Object? bboxObject = await document.resolve(normal.dict["BBox"]);
final Rect bbox = uPdfRectFromArray(bboxObject is List<Object?> ? bboxObject : null, fallback: rect);
final double scaleX = bbox.width <= 0 ? 1 : rect.width / bbox.width;
final double scaleY = bbox.height <= 0 ? 1 : rect.height / bbox.height;
final String name = "UFlat$counter";
counter++;
final int number = allocate();
this.put(number, normal);
stamps[name] = UPdfRef(number, 0);
content.writeln(
"q ${UPdfSerializer.number(scaleX)} 0 0 ${UPdfSerializer.number(scaleY)} "
"${UPdfSerializer.number(rect.left - bbox.left * scaleX)} ${UPdfSerializer.number(rect.top - bbox.top * scaleY)} cm /$name Do Q",
);
flattened++;
}
if (stamps.isEmpty) continue;
mutable["Annots"] = kept;
await appendContent(index, content.toString(), extraResources: <String, Object?>{"XObject": UPdfDict(stamps)});
}
if (!widgetsOnly || flattened == 0) return flattened;
final Object? catalogRef = document.xref.trailer?["Root"];
if (catalogRef is UPdfRef) {
final Object? catalog = await current(catalogRef);
if (catalog is UPdfDict) {
final UPdfDict copy = catalog.copy();
copy.entries.remove("AcroForm");
this.put(catalogRef.number, copy);
}
}
return flattened;
}