alignmentOffsets function

List<({double dx, double dy})> alignmentOffsets(
  1. List<PdfRect> rects,
  2. PdfAlignment alignment
)

Computes the translation to apply to each rect in rects to satisfy alignment, returned as (dx, dy) offsets in PDF user space, parallel to (and in the same order as) rects.

Returns all-zero offsets when there are too few rects for the operation (PdfAlignment.minimumCount). The caller applies the offsets by translating each annotation's /Rect (and its coordinate arrays) - see PdfEditor.moveAnnotation.

Implementation

List<({double dx, double dy})> alignmentOffsets(
    List<PdfRect> rects, PdfAlignment alignment) {
  final n = rects.length;
  final offsets =
      List<({double dx, double dy})>.filled(n, (dx: 0.0, dy: 0.0));
  if (n < alignment.minimumCount) return offsets;

  // The group's overall bounding box.
  var minLeft = rects.first.left;
  var minBottom = rects.first.bottom;
  var maxRight = rects.first.right;
  var maxTop = rects.first.top;
  for (final r in rects) {
    if (r.left < minLeft) minLeft = r.left;
    if (r.bottom < minBottom) minBottom = r.bottom;
    if (r.right > maxRight) maxRight = r.right;
    if (r.top > maxTop) maxTop = r.top;
  }

  switch (alignment) {
    case PdfAlignment.left:
      for (var i = 0; i < n; i++) {
        offsets[i] = (dx: minLeft - rects[i].left, dy: 0);
      }
    case PdfAlignment.right:
      for (var i = 0; i < n; i++) {
        offsets[i] = (dx: maxRight - rects[i].right, dy: 0);
      }
    case PdfAlignment.horizontalCenter:
      final cx = (minLeft + maxRight) / 2;
      for (var i = 0; i < n; i++) {
        final c = (rects[i].left + rects[i].right) / 2;
        offsets[i] = (dx: cx - c, dy: 0);
      }
    case PdfAlignment.top:
      for (var i = 0; i < n; i++) {
        offsets[i] = (dx: 0, dy: maxTop - rects[i].top);
      }
    case PdfAlignment.bottom:
      for (var i = 0; i < n; i++) {
        offsets[i] = (dx: 0, dy: minBottom - rects[i].bottom);
      }
    case PdfAlignment.verticalCenter:
      final cy = (minBottom + maxTop) / 2;
      for (var i = 0; i < n; i++) {
        final c = (rects[i].bottom + rects[i].top) / 2;
        offsets[i] = (dx: 0, dy: cy - c);
      }
    case PdfAlignment.distributeHorizontal:
      // Hold the group's left and right edges; equalise the gaps between
      // rects taken in left-to-right order. Total free space is the span
      // minus the summed widths, divided across the n-1 gaps.
      final order = [for (var i = 0; i < n; i++) i]
        ..sort((a, b) => rects[a].left.compareTo(rects[b].left));
      var summed = 0.0;
      for (final r in rects) {
        summed += r.width;
      }
      final gap = (maxRight - minLeft - summed) / (n - 1);
      var cursor = minLeft;
      for (final i in order) {
        offsets[i] = (dx: cursor - rects[i].left, dy: 0);
        cursor += rects[i].width + gap;
      }
    case PdfAlignment.distributeVertical:
      final order = [for (var i = 0; i < n; i++) i]
        ..sort((a, b) => rects[a].bottom.compareTo(rects[b].bottom));
      var summed = 0.0;
      for (final r in rects) {
        summed += r.height;
      }
      final gap = (maxTop - minBottom - summed) / (n - 1);
      var cursor = minBottom;
      for (final i in order) {
        offsets[i] = (dx: 0, dy: cursor - rects[i].bottom);
        cursor += rects[i].height + gap;
      }
  }
  return offsets;
}