PdfContent.parse constructor

PdfContent.parse(
  1. Uint8List bytes, {
  2. required PdfInflate inflate,
})

Reads bytes, a whole PDF file with exactly one page.

Objects are found by a sequential scan for N G obj … endobj. The scan never looks inside a stream's data, so the xref table is not needed.

Implementation

factory PdfContent.parse(Uint8List bytes, {required PdfInflate inflate}) {
  final file = _PdfFile(latin1.decode(bytes), inflate);
  final pages = [
    for (final o in file.objects.values)
      if (o.value case final Map<String, Object?> d
          when d['/Type'] == const PdfRawName('/Page'))
        d,
  ];
  if (pages.length != 1) {
    throw FormatException('expected one /Page, found ${pages.length}');
  }
  final page = pages.single;
  final mediaBox = [
    for (final v in file.resolve(page['/MediaBox']) as List<Object?>)
      (file.resolve(v) as num).toDouble(),
  ];
  // A page with nothing painted has no /Contents at all: the pdf package
  // drops a content stream that painted nothing.
  final contents = file.resolve(page['/Contents']);
  final parts = <String>[];
  for (final c in contents == null
      ? const <Object?>[]
      : contents is List<Object?>
          ? contents
          : [contents]) {
    parts.add(latin1.decode(file.streamData(file.resolve(c))));
  }
  final resources =
      file.resolve(page['/Resources']) as Map<String, Object?>? ?? const {};
  final interpreter = _Interpreter(file, resources);
  // Content streams of one page are one stream for parsing (ISO 32000-1,
  // 7.8.2), separated by whitespace.
  interpreter.run(parts.join('\n'));
  return PdfContent._(
    mediaBox,
    interpreter.operators,
    interpreter.paths,
    interpreter.texts,
  );
}