duplicate method

PdfPage duplicate(
  1. int index, {
  2. int? at,
})

Duplicates the page at index, inserting the copy at at — by default right after the original.

The copy is shallow, on purpose. The new page references the same content objects, the same /Resources, the same fonts and the same XObjects as the original: there is no renumbering and no stream copy, and duplicating a 4 MB page costs one dictionary. That is correct because nothing in this library rewrites an existing content stream in place; once that exists, duplicating will need a real deep copy.

To bring in a page from another document there is insertImported, which reuses the deep import of PdfDocumentMerger. It would be of no use here: source and target are the same document, and the object numbers already hold.

The annotations are not copied: an annotation belongs to a single page through /P, and sharing it between two would produce exactly the inconsistency this phase exists to avoid. The loss becomes a warning in warnings.

Implementation

PdfPage duplicate(int index, {int? at}) {
  _checkIndex(index);
  final source = _pages[index];
  final target = at ?? index + 1;
  _checkInsertIndex(target, 'at');

  final before = List<PdfPage>.of(_pages);

  // The constructor registers the page at the requested position; inserting
  // it again afterwards would make it show up twice in `/Kids`.
  final copy = PdfPage(
    document,
    pageFormat: source.pageFormat,
    rotate: source.rotate,
    index: target,
  );
  final box = source.mediaBoxOverride;
  if (box != null) copy.mediaBoxOverride = List<double>.of(box);

  for (final entry in source.params.values.entries) {
    if (_notCopiedOnDuplicate.contains(entry.key)) continue;
    copy.params[entry.key] = _copyValue(entry.value);
  }

  // The resources of a page created in this session only enter `/Resources`
  // in prepare(); they have to be copied in the model, not in the dictionary.
  copy.fonts.addAll(source.fonts);
  copy.shading.addAll(source.shading);
  copy.patterns.addAll(source.patterns);
  copy.xObjects.addAll(source.xObjects);
  copy.isolatedTransparency = source.isolatedTransparency;
  copy.knockoutTransparency = source.knockoutTransparency;

  final contents = _contentReferencesOf(source);
  if (contents.isNotEmpty) {
    copy.altered = true;
    final list = PdfArray<PdfDataType>(contents);
    list.uniq();
    copy.params[PdfNameTokens.contents] =
        list.values.length == 1 ? list.values.first : list;
  }

  if (source.params.containsKey(PdfNameTokens.annots) ||
      source.annotations.isNotEmpty) {
    _warn('a cópia da página ${index + 1} não leva as anotações do original: '
        'uma anotação pertence a uma única página pelo /P');
  }

  _afterMutation(before);
  return copy;
}