flattenAnnotations method

Future<int> flattenAnnotations({
  1. List<int>? pages,
  2. bool widgetsOnly = false,
})

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;
}