build static method

UDocTextPage build(
  1. int pageIndex,
  2. Size size,
  3. List<UPdfPositionedGlyph> glyphs
)

Implementation

static UDocTextPage build(int pageIndex, Size size, List<UPdfPositionedGlyph> glyphs) {
  if (glyphs.isEmpty) return UDocTextPage(pageIndex: pageIndex, runs: const <UDocTextRun>[], text: "", size: size);
  final List<UPdfPositionedGlyph> usable = <UPdfPositionedGlyph>[];
  final List<UPdfPositionedGlyph> marks = <UPdfPositionedGlyph>[];
  for (final UPdfPositionedGlyph glyph in glyphs) {
    if (!glyph.rect.isFinite || glyph.rect.height <= 0.2) continue;
    (_isAttachable(glyph.text) || _isSeparator(glyph.text) || glyph.text.trim().isEmpty ? marks : usable).add(glyph);
  }
  usable.sort((UPdfPositionedGlyph a, UPdfPositionedGlyph b) => a.rect.center.dy.compareTo(b.rect.center.dy));
  final List<List<UPdfPositionedGlyph>> lines = <List<UPdfPositionedGlyph>>[];
  List<UPdfPositionedGlyph> current = <UPdfPositionedGlyph>[];
  double center = 0;
  double height = 0;
  for (final UPdfPositionedGlyph glyph in usable) {
    if (current.isNotEmpty) {
      final double tolerance = max(height, glyph.rect.height) * 0.45;
      if ((glyph.rect.center.dy - center).abs() <= tolerance) {
        current.add(glyph);
        center = (center * (current.length - 1) + glyph.rect.center.dy) / current.length;
        height = max(height, glyph.rect.height);
        continue;
      }
      lines.add(current);
    }
    current = <UPdfPositionedGlyph>[glyph];
    center = glyph.rect.center.dy;
    height = glyph.rect.height;
  }
  if (current.isNotEmpty) lines.add(current);
  // Diacritics and zero-width joiners may come from a fallback font with a
  // different box: attach each to the line it overlaps instead of its own line.
  for (final UPdfPositionedGlyph mark in marks) {
    List<UPdfPositionedGlyph>? best;
    double bestScore = double.infinity;
    for (final List<UPdfPositionedGlyph> line in lines) {
      double top = double.infinity;
      double bottom = double.negativeInfinity;
      double left = double.infinity;
      double right = double.negativeInfinity;
      for (final UPdfPositionedGlyph glyph in line) {
        top = min(top, glyph.rect.top);
        bottom = max(bottom, glyph.rect.bottom);
        left = min(left, glyph.rect.left);
        right = max(right, glyph.rect.right);
      }
      if (mark.rect.center.dx < left - 2 || mark.rect.center.dx > right + 2) continue;
      final double score = (mark.rect.center.dy - (top + bottom) / 2).abs() / max(1, max(bottom - top, mark.rect.height));
      if (score < bestScore) {
        bestScore = score;
        best = line;
      }
    }
    if (best != null && bestScore < 1.2) best.add(mark);
  }

  final List<UDocTextRun> runs = <UDocTextRun>[];
  final StringBuffer pageText = StringBuffer();
  int lineIndex = 0;
  for (final List<UPdfPositionedGlyph> line in lines) {
    line.sort((UPdfPositionedGlyph a, UPdfPositionedGlyph b) => a.rect.left.compareTo(b.rect.left));
    bool emitted = false;
    for (final List<UPdfPositionedGlyph> segment in _segments(line)) {
      final UDocTextRun? run = _run(segment, lineIndex);
      if (run == null) continue;
      runs.add(run);
      pageText.writeln(run.text);
      emitted = true;
    }
    if (emitted) lineIndex++;
  }
  return UDocTextPage(pageIndex: pageIndex, runs: runs, text: pageText.toString().trimRight(), size: size);
}