diff --git a/lib/src/model/pdf_constants.dart b/lib/src/model/pdf_constants.dart index 5c216bd..8f60e9e 100644 --- a/lib/src/model/pdf_constants.dart +++ b/lib/src/model/pdf_constants.dart @@ -4,9 +4,13 @@ import 'package:meta/meta.dart'; /// Collection of names used in PDF files @immutable final class PDFNames { + // coverage:ignore-start PDFNames._(); + // coverage:ignore-end + static const bm = PDFName('BM'); + // ignore: constant_identifier_names static const CA = PDFName('CA'); static const pdf = PDFName('PDF'); diff --git a/lib/src/model/pdf_outline.dart b/lib/src/model/pdf_outline.dart index 86141ad..0d51f82 100644 --- a/lib/src/model/pdf_outline.dart +++ b/lib/src/model/pdf_outline.dart @@ -1,5 +1,4 @@ import 'package:dart_pdf_reader/dart_pdf_reader.dart'; -import 'package:dart_pdf_reader/src/model/pdf_constants.dart'; import 'package:meta/meta.dart'; /// Action types for pdf outlines diff --git a/lib/src/parser/pdf_object_parser.dart b/lib/src/parser/pdf_object_parser.dart index 78556ae..a1ada68 100644 --- a/lib/src/parser/pdf_object_parser.dart +++ b/lib/src/parser/pdf_object_parser.dart @@ -1,5 +1,4 @@ import 'package:dart_pdf_reader/dart_pdf_reader.dart'; -import 'package:dart_pdf_reader/src/model/pdf_constants.dart'; import 'package:dart_pdf_reader/src/parser/indirect_object_parser.dart'; import 'package:dart_pdf_reader/src/parser/token_stream.dart'; import 'package:dart_pdf_reader/src/utils/list_extensions.dart'; diff --git a/lib/src/utils/filter/stream_filter.dart b/lib/src/utils/filter/stream_filter.dart index 7e1997b..b774a18 100644 --- a/lib/src/utils/filter/stream_filter.dart +++ b/lib/src/utils/filter/stream_filter.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:dart_pdf_reader/dart_pdf_reader.dart'; -import 'package:dart_pdf_reader/src/model/pdf_constants.dart'; import 'package:dart_pdf_reader/src/utils/filter/direct_byte_stream.dart'; import 'package:dart_pdf_reader/src/utils/list_extensions.dart'; diff --git a/test/parser/pdf_array_parser.dart b/test/parser/pdf_array_parser.dart new file mode 100644 index 0000000..b882c49 --- /dev/null +++ b/test/parser/pdf_array_parser.dart @@ -0,0 +1,46 @@ +part of 'pdf_object_parser_test.dart'; + +void arrayParserTests() { + group('Array parser tests', () { + test('Test normal', () async { + expect(await createParserFromString('[1 2 3]').parse(), + const PDFArray([PDFNumber(1), PDFNumber(2), PDFNumber(3)])); + }); + test('Test mixed', () async { + expect( + await createParserFromString('[1 (2) 3]').parse(), + PDFArray([ + const PDFNumber(1), + PDFLiteralString(Uint8List.fromList(utf8.encode('2'))), + const PDFNumber(3) + ])); + }); + test('Test mixed nested', () async { + expect( + await createParserFromString('[1 (2) [3 4]]').parse(), + PDFArray([ + const PDFNumber(1), + PDFLiteralString(Uint8List.fromList(utf8.encode('2'))), + const PDFArray([PDFNumber(3), PDFNumber(4)]) + ])); + }); + test('Test mixed newlines', () async { + expect( + await createParserFromString('[1\n(2)\n3\n]').parse(), + PDFArray([ + const PDFNumber(1), + PDFLiteralString(Uint8List.fromList(utf8.encode('2'))), + const PDFNumber(3) + ])); + }); + test('Test mixed newlines with comments', () async { + expect( + await createParserFromString('[1%cmd\n(2)\n%cmd\n3\n]').parse(), + PDFArray([ + const PDFNumber(1), + PDFLiteralString(Uint8List.fromList(utf8.encode('2'))), + const PDFNumber(3) + ])); + }); + }); +} diff --git a/test/parser/pdf_basic_parser.dart b/test/parser/pdf_basic_parser.dart new file mode 100644 index 0000000..a32e68b --- /dev/null +++ b/test/parser/pdf_basic_parser.dart @@ -0,0 +1,39 @@ +part of 'pdf_object_parser_test.dart'; + +void basicParserTests() { + group('Basic parser tests', () { + test('Test boolean', () async { + expect( + await createParserFromString('true').parse(), const PDFBoolean(true)); + expect(await createParserFromString('false').parse(), + const PDFBoolean(false)); + }); + test('Test null', () async { + expect(await createParserFromString('null').parse(), const PDFNull()); + }); + test('Test number', () async { + expect(await createParserFromString('1').parse(), const PDFNumber(1)); + expect(await createParserFromString('2.0').parse(), const PDFNumber(2.0)); + expect( + await createParserFromString('-3.0').parse(), const PDFNumber(-3.0)); + expect( + await createParserFromString('+4.0').parse(), const PDFNumber(4.0)); + expect(await createParserFromString('+4.0920029').parse(), + const PDFNumber(4.0920029)); + expect(await createParserFromString('-4.0920029').parse(), + const PDFNumber(-4.0920029)); + expect(await createParserFromString('.0920029').parse(), + const PDFNumber(.0920029)); + expect(() => createParserFromString('9.2a0dd').parse(), + throwsA(isA())); + }); + test('Test command', () async { + expect(await createParserFromString('someCommand').parse(), + const PDFCommand('someCommand')); + expect(await createParserFromString('someCommand secondCommand').parse(), + const PDFCommand('someCommand')); + expect(await createParserFromString('someCommand% secondCommand').parse(), + const PDFCommand('someCommand')); + }); + }); +} diff --git a/test/parser/pdf_object_parser_test.dart b/test/parser/pdf_object_parser_test.dart index f143560..34ded48 100644 --- a/test/parser/pdf_object_parser_test.dart +++ b/test/parser/pdf_object_parser_test.dart @@ -9,17 +9,36 @@ import 'package:dart_pdf_reader/src/parser/pdf_object_parser.dart'; import 'package:dart_pdf_reader/src/parser/xref_reader.dart'; import 'package:dart_pdf_reader/src/utils/byte_stream.dart'; import 'package:dart_pdf_reader/src/utils/random_access_stream.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; +part 'pdf_array_parser.dart'; + +part 'pdf_basic_parser.dart'; + part 'pdf_dictionary_parser.dart'; + +part 'pdf_object_ref_parser.dart'; + part 'pdf_object_string_parser.dart'; -PDFObjectParser createParserFromString(String string) { - return createParser(ByteStream(Uint8List.fromList(utf8.encode(string)))); +part 'pdf_stream_parser.dart'; + +PDFObjectParser createParserFromString( + String string, [ + IndirectObjectParser? indirectObjectParser, +]) { + return createParser( + ByteStream(Uint8List.fromList(utf8.encode(string))), + indirectObjectParser, + ); } -PDFObjectParser createParser(RandomAccessStream stream) { - final indirectObjectParser = +PDFObjectParser createParser( + RandomAccessStream stream, [ + IndirectObjectParser? indirectObjectParser, +]) { + indirectObjectParser ??= IndirectObjectParser(stream, IndirectObjectTable(const XRefTable([]))); return PDFObjectParser(stream, indirectObjectParser); } @@ -28,5 +47,9 @@ void main() { group('PDF Object Parser', () { stringParserTests(); dictionaryParserTests(); + basicParserTests(); + arrayParserTests(); + objectRefParserTests(); + streamParserTests(); }); } diff --git a/test/parser/pdf_object_ref_parser.dart b/test/parser/pdf_object_ref_parser.dart new file mode 100644 index 0000000..824186c --- /dev/null +++ b/test/parser/pdf_object_ref_parser.dart @@ -0,0 +1,14 @@ +part of 'pdf_object_parser_test.dart'; + +void objectRefParserTests() { + group('Object ref parser tests', () { + test('Test normal', () async { + expect(await createParserFromString('12 22 R').parse(), + const PDFObjectReference(objectId: 12, generationNumber: 22)); + }); + test('Test ending with comment', () async { + expect(await createParserFromString('12 22 R%comment').parse(), + const PDFObjectReference(objectId: 12, generationNumber: 22)); + }); + }); +} diff --git a/test/parser/pdf_object_string_parser.dart b/test/parser/pdf_object_string_parser.dart index 5d168b3..ba66ff5 100644 --- a/test/parser/pdf_object_string_parser.dart +++ b/test/parser/pdf_object_string_parser.dart @@ -27,10 +27,11 @@ void stringParserTests() { }); test('Test escaping', () async { final parsed = - await createParserFromString('(\\n\\r\\t\\(\\)\\\\\\045)').parse(); + await createParserFromString('(\\f\\n\\r\\t\\(\\)\\\\\\045)') + .parse(); expect(parsed, isA()); parsed as PDFLiteralString; - expect(parsed.asString(), '\n\r\t()\\%'); + expect(parsed.asString(), '\f\n\r\t()\\%'); }); test('Test new line in string', () async { final parsed = @@ -47,6 +48,18 @@ void stringParserTests() { parsed as PDFHexString; expect(parsed.asString(), 'Hello'); }); + test('Test parse uneven hex string', () async { + final parsed = await createParserFromString('<48656c6c6>').parse(); + expect(parsed, isA()); + parsed as PDFHexString; + expect(parsed.asString(), 'Hell`'); + }); + test('Test parse hex with whitespace', () async { + final parsed = await createParserFromString('<48656c 6c\n6f>').parse(); + expect(parsed, isA()); + parsed as PDFHexString; + expect(parsed.asString(), 'Hello'); + }); test('Test parse hex string with comments', () async { final parsed = await createParserFromString('<48%Very\n656c6c6f>').parse(); diff --git a/test/parser/pdf_stream_parser.dart b/test/parser/pdf_stream_parser.dart new file mode 100644 index 0000000..95fc913 --- /dev/null +++ b/test/parser/pdf_stream_parser.dart @@ -0,0 +1,56 @@ +part of 'pdf_object_parser_test.dart'; + +class _MockIndirectObjectParser extends Mock implements IndirectObjectParser {} + +void streamParserTests() { + group('Stream parser tests', () { + test('Test empty stream', () async { + final stream = + await createParserFromString('<>stream\nendstream') + .parse(); + expect(stream, isA()); + + stream as PDFStreamObject; + expect(stream.dictionary.entries, { + const PDFName('Length'): const PDFNumber(0), + }); + expect(stream.length, 0); + expect(stream.offset, 20); + }); + test('Test empty stream length indirect', () async { + final mockParser = _MockIndirectObjectParser(); + when(() => mockParser.getObjectFor( + const PDFObjectReference(objectId: 1, generationNumber: 0))) + .thenAnswer((invocation) async => const PDFIndirectObject( + objectId: 1, generationNumber: 0, object: PDFNumber(0))); + final stream = await createParserFromString( + '<>stream\nendstream', + mockParser, + ).parse(); + expect(stream, isA()); + + stream as PDFStreamObject; + expect(stream.dictionary.entries, { + const PDFName('Length'): + const PDFObjectReference(objectId: 1, generationNumber: 0), + }); + expect(stream.length, 0); + expect(stream.offset, 24); + }); + test('Test stream with data', () async { + final stream = + await createParserFromString('<>stream\nabcendstream') + .parse(); + expect(stream, isA()); + + stream as PDFStreamObject; + expect(stream.dictionary.entries, { + const PDFName('Length'): const PDFNumber(3), + }); + expect(stream.length, 3); + expect(stream.offset, 20); + final raw = await stream.readRaw(); + expect(raw, [97, 98, 99]); + }); + }); +}