Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
dart_pdf
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
David PHAM-VAN
2023-03-16 13:32:09 -0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
5469c14832216980295152bd835ea3caf551288e
5469c148
1 parent
fc927ed4
Simplify PDF generation
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
247 additions
and
339 deletions
pdf/lib/src/pdf/document.dart
pdf/lib/src/pdf/format/base.dart
pdf/lib/src/pdf/obj/diagnostic.dart → pdf/lib/src/pdf/format/diagnostic.dart
pdf/lib/src/pdf/format/dict_stream.dart
pdf/lib/src/pdf/format/object_base.dart
pdf/lib/src/pdf/format/xref.dart
pdf/lib/src/pdf/obj/object.dart
pdf/lib/src/pdf/obj/object_dict.dart
pdf/lib/src/pdf/obj/object_stream.dart
pdf/lib/src/pdf/obj/signature.dart
pdf/lib/src/pdf/output.dart
pdf/lib/src/priv.dart
pdf/test/minimal_test.dart
pdf/lib/src/pdf/document.dart
View file @
5469c14
...
...
@@ -20,8 +20,11 @@ import 'dart:typed_data';
import
'package:crypto/crypto.dart'
;
import
'document_parser.dart'
;
import
'format/array.dart'
;
import
'format/num.dart'
;
import
'format/object_base.dart'
;
import
'format/stream.dart'
;
import
'format/string.dart'
;
import
'format/xref.dart'
;
import
'graphic_state.dart'
;
import
'io/vm.dart'
if
(
dart
.
library
.
js
)
'io/js.dart'
;
...
...
@@ -36,7 +39,6 @@ import 'obj/page.dart';
import
'obj/page_label.dart'
;
import
'obj/page_list.dart'
;
import
'obj/signature.dart'
;
import
'output.dart'
;
/// Display hint for the PDF viewer
enum
PdfPageMode
{
...
...
@@ -200,24 +202,36 @@ class PdfDocument {
/// This writes the document to an OutputStream.
Future
<
void
>
_write
(
PdfStream
os
)
async
{
final
pos
=
PdfOutput
(
os
,
version
,
verbose
);
// Write each object to the [PdfStream]. We call via the output
// as that builds the xref table
objects
.
where
((
e
)
=>
e
.
inUse
).
forEach
(
pos
.
write
);
var
lastFree
=
0
;
for
(
final
obj
in
objects
.
where
((
e
)
=>
!
e
.
inUse
))
{
pos
.
xref
.
add
(
PdfXref
(
obj
.
objser
,
lastFree
,
generation:
obj
.
objgen
,
type:
PdfCrossRefEntryType
.
free
,
));
lastFree
=
obj
.
objser
;
PdfSignature
?
signature
;
final
xref
=
PdfXrefTable
();
for
(
final
ob
in
objects
.
where
((
e
)
=>
e
.
inUse
))
{
ob
.
prepare
();
if
(
ob
is
PdfInfo
)
{
xref
.
params
[
'/Info'
]
=
ob
.
ref
();
}
else
if
(
ob
is
PdfEncryption
)
{
xref
.
params
[
'/Encrypt'
]
=
ob
.
ref
();
}
else
if
(
ob
is
PdfSignature
)
{
assert
(
signature
==
null
,
'Only one document signature is allowed'
);
signature
=
ob
;
}
xref
.
objects
.
add
(
ob
);
}
// Finally close the output, which writes the xref table.
await
pos
.
close
();
final
id
=
PdfString
(
documentID
,
format:
PdfStringFormat
.
binary
,
encrypted:
false
);
xref
.
params
[
'/ID'
]
=
PdfArray
([
id
,
id
]);
if
(
prev
!=
null
)
{
xref
.
params
[
'/Prev'
]
=
PdfNum
(
prev
!.
xrefOffset
);
}
xref
.
output
(
catalog
,
os
);
if
(
signature
!=
null
)
{
await
signature
.
writeSignature
(
os
);
}
}
/// Generate the PDF document as a memory file
...
...
pdf/lib/src/pdf/format/base.dart
View file @
5469c14
...
...
@@ -30,7 +30,7 @@ abstract class PdfDataType {
PdfStream
_toStream
()
{
final
s
=
PdfStream
();
output
(
const
PdfObjectBase
(
objser:
0
),
s
);
output
(
PdfObjectBase
(
objser:
0
,
params:
this
),
s
);
return
s
;
}
...
...
pdf/lib/src/pdf/
obj
/diagnostic.dart → pdf/lib/src/pdf/
format
/diagnostic.dart
View file @
5469c14
...
...
@@ -2,7 +2,7 @@ import 'dart:math' as math;
import
'package:meta/meta.dart'
;
import
'
../format/
stream.dart'
;
import
'stream.dart'
;
mixin
PdfDiagnostic
{
static
const
_maxSize
=
300
;
...
...
@@ -15,6 +15,8 @@ mixin PdfDiagnostic {
int
get
elapsedStopwatch
=>
_stopwatch
?.
elapsedMicroseconds
??
0
;
int
size
=
0
;
@protected
@mustCallSuper
void
debugFill
(
String
value
)
{
...
...
@@ -29,10 +31,11 @@ mixin PdfDiagnostic {
}());
}
void
setInsertion
(
PdfStream
os
)
{
void
setInsertion
(
PdfStream
os
,
[
int
size
=
_maxSize
]
)
{
assert
(()
{
this
.
size
=
size
;
_offset
=
os
.
offset
;
os
.
putComment
(
' '
*
_maxS
ize
);
os
.
putComment
(
' '
*
s
ize
);
return
true
;
}());
}
...
...
@@ -45,7 +48,7 @@ mixin PdfDiagnostic {
final
b
=
o
.
output
();
os
.
setBytes
(
_offset
!,
b
.
sublist
(
0
,
math
.
min
(
_maxS
ize
+
2
,
b
.
lengthInBytes
-
1
)),
b
.
sublist
(
0
,
math
.
min
(
s
ize
+
2
,
b
.
lengthInBytes
-
1
)),
);
}
return
true
;
...
...
pdf/lib/src/pdf/format/dict_stream.dart
View file @
5469c14
...
...
@@ -99,6 +99,6 @@ class PdfDictStream extends PdfDict<PdfDataType> {
}
s
.
putString
(
'stream
\n
'
);
s
.
putBytes
(
_data
);
s
.
putString
(
'
\n
endstream
\n
'
);
s
.
putString
(
'
\n
endstream'
);
}
}
...
...
pdf/lib/src/pdf/format/object_base.dart
View file @
5469c14
...
...
@@ -16,7 +16,10 @@
import
'dart:typed_data'
;
import
'base.dart'
;
import
'diagnostic.dart'
;
import
'indirect.dart'
;
import
'stream.dart'
;
/// Callback used to compress the data
typedef
DeflateCallback
=
List
<
int
>
Function
(
List
<
int
>
data
);
...
...
@@ -34,10 +37,11 @@ enum PdfVersion {
pdf_1_5
,
}
class
PdfObjectBase
{
const
PdfObjectBase
({
class
PdfObjectBase
<
T
extends
PdfDataType
>
with
PdfDiagnostic
{
PdfObjectBase
({
required
this
.
objser
,
this
.
objgen
=
0
,
required
this
.
params
,
});
/// This is the unique serial number for this object.
...
...
@@ -46,6 +50,8 @@ class PdfObjectBase {
/// This is the generation number for this object.
final
int
objgen
;
final
T
params
;
/// Callback used to compress the data
DeflateCallback
?
get
deflate
=>
null
;
...
...
@@ -59,4 +65,15 @@ class PdfObjectBase {
/// Returns the unique serial number in Pdf format
PdfIndirect
ref
()
=>
PdfIndirect
(
objser
,
objgen
);
void
output
(
PdfStream
s
)
{
s
.
putString
(
'
$objser
$objgen
obj
\n
'
);
writeContent
(
s
);
s
.
putString
(
'endobj
\n
'
);
}
void
writeContent
(
PdfStream
s
)
{
params
.
output
(
this
,
s
,
verbose
?
0
:
null
);
s
.
putByte
(
0x0a
);
}
}
...
...
pdf/lib/src/pdf/format/xref.dart
View file @
5469c14
...
...
@@ -19,6 +19,7 @@ import 'dart:typed_data';
import
'array.dart'
;
import
'base.dart'
;
import
'diagnostic.dart'
;
import
'dict.dart'
;
import
'dict_stream.dart'
;
import
'indirect.dart'
;
...
...
@@ -95,16 +96,17 @@ class PdfXref {
int
get
hashCode
=>
offset
;
}
class
PdfXrefTable
extends
PdfDataType
{
class
PdfXrefTable
extends
PdfDataType
with
PdfDiagnostic
{
PdfXrefTable
();
/// Contains offsets of each object
final
offsets
=
<
PdfXref
>[];
/// Document root point
final
params
=
PdfDict
();
/// Add a cross reference element to the set
void
add
(
PdfXref
xref
)
{
offsets
.
add
(
xref
);
}
/// List of objects to write
final
objects
=
<
PdfObjectBase
>{};
/// Contains the offset of each objects
final
_offsets
=
<
PdfXref
>[];
/// Writes a block of references to the Pdf file
void
_writeBlock
(
PdfStream
s
,
int
firstId
,
List
<
PdfXref
>
block
)
{
...
...
@@ -117,26 +119,109 @@ class PdfXrefTable extends PdfDataType {
}
@override
void
output
(
PdfObjectBase
o
,
PdfStream
s
,
[
int
?
indent
])
{}
void
output
(
PdfObjectBase
o
,
PdfStream
s
,
[
int
?
indent
])
{
String
v
;
switch
(
o
.
version
)
{
case
PdfVersion
.
pdf_1_4
:
v
=
'1.4'
;
break
;
case
PdfVersion
.
pdf_1_5
:
v
=
'1.5'
;
break
;
}
s
.
putString
(
'%PDF-
$v
\n
'
);
s
.
putBytes
(
const
<
int
>[
0x25
,
0xC2
,
0xA5
,
0xC2
,
0xB1
,
0xC3
,
0xAB
,
0x0A
]);
assert
(()
{
if
(
o
.
verbose
)
{
setInsertion
(
s
);
startStopwatch
();
debugFill
(
'Verbose dart_pdf'
);
debugFill
(
'Producer https://github.com/DavBfr/dart_pdf'
);
debugFill
(
'Creation date:
${DateTime.now()}
'
);
}
return
true
;
}());
for
(
final
ob
in
objects
)
{
assert
(()
{
if
(
ob
.
verbose
)
{
ob
.
setInsertion
(
s
,
150
);
ob
.
startStopwatch
();
}
return
true
;
}());
_offsets
.
add
(
PdfXref
(
ob
.
objser
,
s
.
offset
,
generation:
ob
.
objgen
));
ob
.
output
(
s
);
assert
(()
{
if
(
ob
.
verbose
)
{
ob
.
stopStopwatch
();
ob
.
debugFill
(
'Creation time:
${ob.elapsedStopwatch / Duration.microsecondsPerSecond}
seconds'
);
ob
.
writeDebug
(
s
);
}
return
true
;
}());
}
final
int
xrefOffset
;
params
[
'/Root'
]
=
o
.
ref
();
switch
(
o
.
version
)
{
case
PdfVersion
.
pdf_1_4
:
xrefOffset
=
outputLegacy
(
o
,
s
);
break
;
case
PdfVersion
.
pdf_1_5
:
xrefOffset
=
outputCompressed
(
o
,
s
);
break
;
}
assert
(()
{
if
(
o
.
verbose
)
{
s
.
putComment
(
''
);
s
.
putComment
(
'-'
*
78
);
s
.
putComment
(
'
$runtimeType
'
);
}
return
true
;
}());
// the reference to the xref object
s
.
putString
(
'startxref
\n
$xrefOffset
\n
%%EOF
\n
'
);
assert
(()
{
if
(
o
.
verbose
)
{
stopStopwatch
();
debugFill
(
'Creation time:
${elapsedStopwatch / Duration.microsecondsPerSecond}
seconds'
);
debugFill
(
'File size:
${s.offset}
bytes'
);
// debugFill('Pages: ${rootID!.pdfDocument.pdfPageList.pages.length}');
debugFill
(
'Objects:
${objects.length}
'
);
writeDebug
(
s
);
}
return
true
;
}());
}
@override
String
toString
()
{
final
s
=
StringBuffer
();
for
(
final
x
in
offsets
)
{
for
(
final
x
in
_
offsets
)
{
s
.
writeln
(
'
$x
'
);
}
return
s
.
toString
();
}
int
outputLegacy
(
PdfObjectBase
o
bject
,
PdfStream
s
,
PdfDict
param
s
)
{
int
outputLegacy
(
PdfObjectBase
o
,
PdfStream
s
)
{
// Now scan through the offsets list. They should be in sequence.
offsets
.
sort
((
a
,
b
)
=>
a
.
id
.
compareTo
(
b
.
id
));
_offsets
.
sort
((
a
,
b
)
=>
a
.
id
.
compareTo
(
b
.
id
));
final
size
=
_offsets
.
last
.
id
+
1
;
assert
(()
{
if
(
o
bject
.
verbose
)
{
if
(
o
.
verbose
)
{
s
.
putComment
(
''
);
s
.
putComment
(
'-'
*
78
);
s
.
putComment
(
'
$runtimeType
${o
bject
.version.name}
\n
$this
'
);
s
.
putComment
(
'
$runtimeType
${o.version.name}
\n
$this
'
);
}
return
true
;
}());
...
...
@@ -156,7 +241,7 @@ class PdfXrefTable extends PdfDataType {
final
objOffset
=
s
.
offset
;
s
.
putString
(
'xref
\n
'
);
for
(
final
x
in
offsets
)
{
for
(
final
x
in
_
offsets
)
{
// check to see if block is in range
if
(
x
.
id
!=
(
lastId
+
1
))
{
// no, so write this block, and reset
...
...
@@ -175,31 +260,33 @@ class PdfXrefTable extends PdfDataType {
// the trailer object
assert
(()
{
if
(
o
bject
.
verbose
)
{
if
(
o
.
verbose
)
{
s
.
putComment
(
''
);
}
return
true
;
}());
s
.
putString
(
'trailer
\n
'
);
params
.
output
(
object
,
s
,
object
.
verbose
?
0
:
null
);
params
[
'/Size'
]
=
PdfNum
(
size
);
params
.
output
(
o
,
s
,
o
.
verbose
?
0
:
null
);
s
.
putByte
(
0x0a
);
return
objOffset
;
}
/// Output a compressed cross-reference table
int
outputCompressed
(
PdfObjectBase
o
bject
,
PdfStream
s
,
PdfDict
param
s
)
{
int
outputCompressed
(
PdfObjectBase
o
,
PdfStream
s
)
{
final
offset
=
s
.
offset
;
// Sort all references
offsets
.
sort
((
a
,
b
)
=>
a
.
id
.
compareTo
(
b
.
id
));
_
offsets
.
sort
((
a
,
b
)
=>
a
.
id
.
compareTo
(
b
.
id
));
// Write this object too
final
id
=
offsets
.
last
.
id
+
1
;
offsets
.
add
(
PdfXref
(
id
,
offset
));
final
id
=
_offsets
.
last
.
id
+
1
;
final
size
=
id
+
1
;
_offsets
.
add
(
PdfXref
(
id
,
offset
));
params
[
'/Type'
]
=
const
PdfName
(
'/XRef'
);
params
[
'/Size'
]
=
PdfNum
(
id
+
1
);
params
[
'/Size'
]
=
PdfNum
(
size
);
var
firstId
=
0
;
// First id in block
var
lastId
=
0
;
// The last id used
...
...
@@ -208,7 +295,7 @@ class PdfXrefTable extends PdfDataType {
// We need block 0 to exist
blocks
.
add
(
firstId
);
for
(
final
x
in
offsets
)
{
for
(
final
x
in
_
offsets
)
{
// check to see if block is in range
if
(
x
.
id
!=
(
lastId
+
1
))
{
// no, so store this block, and reset
...
...
@@ -220,7 +307,7 @@ class PdfXrefTable extends PdfDataType {
}
blocks
.
add
(
lastId
-
firstId
+
1
);
if
(!(
blocks
.
length
==
2
&&
blocks
[
0
]
==
0
&&
blocks
[
1
]
==
id
+
1
))
{
if
(!(
blocks
.
length
==
2
&&
blocks
[
0
]
==
0
&&
blocks
[
1
]
==
size
))
{
params
[
'/Index'
]
=
PdfArray
.
fromNum
(
blocks
);
}
...
...
@@ -229,21 +316,21 @@ class PdfXrefTable extends PdfDataType {
params
[
'/W'
]
=
PdfArray
.
fromNum
(
w
);
final
wl
=
w
.
reduce
((
a
,
b
)
=>
a
+
b
);
final
o
=
ByteData
((
offsets
.
length
+
1
)
*
wl
);
final
binOffsets
=
ByteData
((
_
offsets
.
length
+
1
)
*
wl
);
var
ofs
=
0
;
// Write offset zero, all zeros
ofs
+=
wl
;
for
(
final
x
in
offsets
)
{
ofs
=
x
.
_compressedRef
(
o
,
ofs
,
w
);
for
(
final
x
in
_offsets
)
{
ofs
=
x
.
_compressedRef
(
binOffsets
,
ofs
,
w
);
}
// Write the object
assert
(()
{
if
(
o
bject
.
verbose
)
{
if
(
o
.
verbose
)
{
s
.
putComment
(
''
);
s
.
putComment
(
'-'
*
78
);
s
.
putComment
(
'
$runtimeType
${o
bject
.version.name}
\n
$this
'
);
s
.
putComment
(
'
$runtimeType
${o.version.name}
\n
$this
'
);
}
return
true
;
}());
...
...
@@ -253,13 +340,13 @@ class PdfXrefTable extends PdfDataType {
s
.
putString
(
'
$id
0 obj
\n
'
);
PdfDictStream
(
data:
o
.
buffer
.
asUint8List
(),
data:
binOffsets
.
buffer
.
asUint8List
(),
isBinary:
false
,
encrypt:
false
,
values:
params
.
values
,
).
output
(
o
bject
,
s
,
object
.
verbose
?
0
:
null
);
).
output
(
o
,
s
,
o
.
verbose
?
0
:
null
);
s
.
putString
(
'endobj
\n
'
);
s
.
putString
(
'
\n
endobj
\n
'
);
return
objOffset
;
}
}
...
...
pdf/lib/src/pdf/obj/object.dart
View file @
5469c14
...
...
@@ -19,26 +19,24 @@ import 'package:meta/meta.dart';
import
'../document.dart'
;
import
'../format/base.dart'
;
import
'../format/object_base.dart'
;
import
'../format/stream.dart'
;
import
'diagnostic.dart'
;
/// Base Object used in the PDF file
abstract
class
PdfObject
<
T
extends
PdfDataType
>
extends
PdfObjectBase
with
PdfDiagnostic
{
abstract
class
PdfObject
<
T
extends
PdfDataType
>
extends
PdfObjectBase
<
T
>
{
/// This is usually called by extensors to this class, and sets the
/// Pdf Object Type
PdfObject
(
this
.
pdfDocument
,
{
required
this
.
params
,
required
T
params
,
int
objgen
=
0
,
int
?
objser
,
})
:
super
(
objser:
objser
??
pdfDocument
.
genSerial
(),
objgen:
objgen
)
{
})
:
super
(
objser:
objser
??
pdfDocument
.
genSerial
(),
objgen:
objgen
,
params:
params
,
)
{
pdfDocument
.
objects
.
add
(
this
);
}
/// This is the object parameters.
final
T
params
;
/// This allows any Pdf object to refer to the document being constructed.
final
PdfDocument
pdfDocument
;
...
...
@@ -56,35 +54,10 @@ abstract class PdfObject<T extends PdfDataType> extends PdfObjectBase
@override
PdfVersion
get
version
=>
pdfDocument
.
version
;
/// Writes the object to the output stream.
void
write
(
PdfStream
os
)
{
prepare
();
_writeStart
(
os
);
writeContent
(
os
);
_writeEnd
(
os
);
}
/// Prepare the object to be written to the stream
@mustCallSuper
void
prepare
()
{}
/// The write method should call this before writing anything to the
/// OutputStream. This will send the standard header for each object.
void
_writeStart
(
PdfStream
os
)
{
os
.
putString
(
'
$objser
$objgen
obj
\n
'
);
}
void
writeContent
(
PdfStream
os
)
{
params
.
output
(
this
,
os
,
verbose
?
0
:
null
);
os
.
putByte
(
0x0a
);
}
/// The write method should call this after writing anything to the
/// OutputStream. This will send the standard footer for each object.
void
_writeEnd
(
PdfStream
os
)
{
os
.
putString
(
'endobj
\n
'
);
}
@override
String
toString
()
=>
'
$runtimeType
$params
'
;
}
...
...
pdf/lib/src/pdf/obj/object_dict.dart
View file @
5469c14
...
...
@@ -36,10 +36,10 @@ class PdfObjectDict extends PdfObject<PdfDict> {
}
@override
void
writeContent
(
PdfStream
o
s
)
{
void
writeContent
(
PdfStream
s
)
{
if
(
params
.
isNotEmpty
)
{
params
.
output
(
this
,
os
,
pdfDocument
.
verbose
?
0
:
null
);
os
.
putByte
(
0x0a
);
params
.
output
(
this
,
s
,
pdfDocument
.
verbose
?
0
:
null
);
s
.
putByte
(
0x0a
);
}
}
}
...
...
pdf/lib/src/pdf/obj/object_stream.dart
View file @
5469c14
...
...
@@ -35,11 +35,12 @@ class PdfObjectStream extends PdfObjectDict {
final
bool
isBinary
;
@override
void
writeContent
(
PdfStream
o
s
)
{
void
writeContent
(
PdfStream
s
)
{
PdfDictStream
.
values
(
isBinary:
isBinary
,
values:
params
.
values
,
data:
buf
.
output
(),
).
output
(
this
,
os
,
pdfDocument
.
verbose
?
0
:
null
);
).
output
(
this
,
s
,
pdfDocument
.
verbose
?
0
:
null
);
s
.
putByte
(
0x0a
);
}
}
...
...
pdf/lib/src/pdf/obj/signature.dart
View file @
5469c14
...
...
@@ -82,12 +82,12 @@ class PdfSignature extends PdfObjectDict {
int
?
_offsetEnd
;
@override
void
write
(
PdfStream
o
s
)
{
void
output
(
PdfStream
s
)
{
value
.
preSign
(
this
,
params
);
_offsetStart
=
os
.
offset
+
'
$objser
$objgen
obj
\n
'
.
length
;
super
.
write
(
os
);
_offsetEnd
=
os
.
offset
;
_offsetStart
=
s
.
offset
+
'
$objser
$objgen
obj
\n
'
.
length
;
super
.
output
(
s
);
_offsetEnd
=
s
.
offset
;
}
Future
<
void
>
writeSignature
(
PdfStream
os
)
async
{
...
...
pdf/lib/src/pdf/output.dart
deleted
100644 → 0
View file @
fc927ed
/*
* Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
'format/array.dart'
;
import
'format/dict.dart'
;
import
'format/num.dart'
;
import
'format/object_base.dart'
;
import
'format/stream.dart'
;
import
'format/string.dart'
;
import
'format/xref.dart'
;
import
'obj/catalog.dart'
;
import
'obj/diagnostic.dart'
;
import
'obj/encryption.dart'
;
import
'obj/info.dart'
;
import
'obj/object.dart'
;
import
'obj/signature.dart'
;
/// PDF document writer
class
PdfOutput
with
PdfDiagnostic
{
/// This creates a Pdf [PdfStream]
PdfOutput
(
this
.
os
,
this
.
version
,
this
.
verbose
)
{
String
v
;
switch
(
version
)
{
case
PdfVersion
.
pdf_1_4
:
v
=
'1.4'
;
break
;
case
PdfVersion
.
pdf_1_5
:
v
=
'1.5'
;
break
;
}
os
.
putString
(
'%PDF-
$v
\n
'
);
os
.
putBytes
(
const
<
int
>[
0x25
,
0xC2
,
0xA5
,
0xC2
,
0xB1
,
0xC3
,
0xAB
,
0x0A
]);
assert
(()
{
if
(
verbose
)
{
setInsertion
(
os
);
startStopwatch
();
debugFill
(
'Verbose dart_pdf'
);
debugFill
(
'Producer https://github.com/DavBfr/dart_pdf'
);
debugFill
(
'Creation date:
${DateTime.now()}
'
);
}
return
true
;
}());
}
/// Pdf version to output
final
PdfVersion
version
;
/// This is the actual [PdfStream] used to write to.
final
PdfStream
os
;
/// Cross reference table
final
xref
=
PdfXrefTable
();
/// This is used to track the /Root object (catalog)
PdfCatalog
?
rootID
;
/// This is used to track the /Info object (info)
PdfInfo
?
infoID
;
/// This is used to track the /Encrypt object (encryption)
PdfEncryption
?
encryptID
;
/// This is used to track the /Sign object (signature)
PdfSignature
?
signatureID
;
/// Generate a compressed cross reference table
bool
get
isCompressed
=>
version
.
index
>
PdfVersion
.
pdf_1_4
.
index
;
/// Verbose output
final
bool
verbose
;
/// This method writes a [PdfObject] to the stream.
void
write
(
PdfObject
ob
)
{
// Check the object to see if it's one that is needed later
if
(
ob
is
PdfCatalog
)
{
rootID
=
ob
;
}
else
if
(
ob
is
PdfInfo
)
{
infoID
=
ob
;
}
else
if
(
ob
is
PdfEncryption
)
{
encryptID
=
ob
;
}
else
if
(
ob
is
PdfSignature
)
{
assert
(
signatureID
==
null
,
'Only one document signature is allowed'
);
signatureID
=
ob
;
}
assert
(()
{
if
(
verbose
)
{
ob
.
setInsertion
(
os
);
ob
.
startStopwatch
();
}
return
true
;
}());
xref
.
add
(
PdfXref
(
ob
.
objser
,
os
.
offset
,
generation:
ob
.
objgen
));
ob
.
write
(
os
);
assert
(()
{
if
(
verbose
)
{
ob
.
stopStopwatch
();
ob
.
debugFill
(
'Creation time:
${ob.elapsedStopwatch / Duration.microsecondsPerSecond}
seconds'
);
ob
.
writeDebug
(
os
);
}
return
true
;
}());
}
/// This closes the Stream, writing the xref table
Future
<
void
>
close
()
async
{
if
(
rootID
==
null
)
{
throw
Exception
(
'Root object is not present in document'
);
}
final
params
=
PdfDict
();
// the number of entries (REQUIRED)
params
[
'/Size'
]
=
PdfNum
(
rootID
!.
pdfDocument
.
objser
);
// the /Root catalog indirect reference (REQUIRED)
params
[
'/Root'
]
=
rootID
!.
ref
();
final
id
=
PdfString
(
rootID
!.
pdfDocument
.
documentID
,
format:
PdfStringFormat
.
binary
,
encrypted:
false
);
params
[
'/ID'
]
=
PdfArray
([
id
,
id
]);
// the /Info reference (OPTIONAL)
if
(
infoID
!=
null
)
{
params
[
'/Info'
]
=
infoID
!.
ref
();
}
// the /Encrypt reference (OPTIONAL)
if
(
encryptID
!=
null
)
{
params
[
'/Encrypt'
]
=
encryptID
!.
ref
();
}
if
(
rootID
!.
pdfDocument
.
prev
!=
null
)
{
params
[
'/Prev'
]
=
PdfNum
(
rootID
!.
pdfDocument
.
prev
!.
xrefOffset
);
}
final
_xref
=
isCompressed
?
xref
.
outputCompressed
(
rootID
!,
os
,
params
)
:
xref
.
outputLegacy
(
rootID
!,
os
,
params
);
assert
(()
{
if
(
verbose
)
{
os
.
putComment
(
''
);
os
.
putComment
(
'-'
*
78
);
os
.
putComment
(
'
$runtimeType
'
);
}
return
true
;
}());
// the reference to the xref object
os
.
putString
(
'startxref
\n
$_xref
\n
%%EOF
\n
'
);
assert
(()
{
if
(
verbose
)
{
stopStopwatch
();
debugFill
(
'Creation time:
${elapsedStopwatch / Duration.microsecondsPerSecond}
seconds'
);
debugFill
(
'File size:
${os.offset}
bytes'
);
debugFill
(
'Pages:
${rootID!.pdfDocument.pdfPageList.pages.length}
'
);
debugFill
(
'Objects:
${xref.offsets.length}
'
);
writeDebug
(
os
);
}
return
true
;
}());
if
(
signatureID
!=
null
)
{
await
signatureID
!.
writeSignature
(
os
);
}
}
}
pdf/lib/src/priv.dart
View file @
5469c14
...
...
@@ -18,6 +18,7 @@ export 'pdf/format/array.dart';
export
'pdf/format/ascii85.dart'
;
export
'pdf/format/base.dart'
;
export
'pdf/format/bool.dart'
;
export
'pdf/format/diagnostic.dart'
;
export
'pdf/format/dict.dart'
;
export
'pdf/format/dict_stream.dart'
;
export
'pdf/format/indirect.dart'
;
...
...
pdf/test/minimal_test.dart
View file @
5469c14
...
...
@@ -17,78 +17,74 @@
import
'dart:convert'
;
import
'dart:io'
;
import
'package:pdf/pdf.dart'
;
import
'package:pdf/src/priv.dart'
;
import
'package:test/test.dart'
;
class
BasicObject
extends
PdfObjectBase
{
const
BasicObject
(
int
objser
)
:
super
(
objser:
objser
);
class
BasicObject
<
T
extends
PdfDataType
>
extends
PdfObjectBase
<
T
>
{
BasicObject
({
required
super
.
objser
,
required
super
.
params
});
@override
bool
get
verbose
=>
true
;
void
write
(
PdfStream
os
,
PdfDataType
value
)
{
os
.
putString
(
'
$objser
$objgen
obj
\n
'
);
value
.
output
(
this
,
os
,
verbose
?
0
:
null
);
os
.
putByte
(
0x0a
);
os
.
putString
(
'endobj
\n
'
);
}
@override
PdfVersion
get
version
=>
PdfVersion
.
pdf_1_4
;
@override
DeflateCallback
?
get
deflate
=>
zlib
.
encode
;
}
void
main
(
)
{
test
(
'Pdf Minimal'
,
()
async
{
final
pages
=
PdfDict
({
'/Type'
:
const
PdfName
(
'/Pages'
),
'/Count'
:
const
PdfNum
(
1
),
});
final
page
=
PdfDict
({
'/Type'
:
const
PdfName
(
'/Page'
),
'/Parent'
:
const
PdfIndirect
(
2
,
0
),
'/MediaBox'
:
PdfArray
.
fromNum
([
0
,
0
,
595.27559
,
841.88976
]),
'/Resources'
:
PdfDict
({
'/ProcSet'
:
PdfArray
([
const
PdfName
(
'/PDF'
),
]),
}),
'/Contents'
:
const
PdfIndirect
(
4
,
0
),
});
final
content
=
PdfDictStream
(
data:
latin1
.
encode
(
'30 811.88976 m 200 641.88976 l S'
),
);
pages
[
'/Kids'
]
=
PdfArray
([
const
PdfIndirect
(
3
,
0
)]);
final
catalog
=
PdfDict
({
'/Type'
:
const
PdfName
(
'/Catalog'
),
'/Pages'
:
const
PdfIndirect
(
2
,
0
),
});
var
objser
=
1
;
final
os
=
PdfStream
();
final
pages
=
BasicObject
(
objser:
objser
++,
params:
PdfDict
({
'/Type'
:
const
PdfName
(
'/Pages'
),
'/Count'
:
const
PdfNum
(
1
),
}));
final
xref
=
PdfXrefTable
();
final
content
=
BasicObject
(
objser:
objser
++,
params:
PdfDictStream
(
data:
latin1
.
encode
(
'30 811.88976 m 200 641.88976 l S'
),
));
final
page
=
BasicObject
(
objser:
objser
++,
params:
PdfDict
({
'/Type'
:
const
PdfName
(
'/Page'
),
'/Parent'
:
pages
.
ref
(),
'/MediaBox'
:
PdfArray
.
fromNum
([
0
,
0
,
595.27559
,
841.88976
]),
'/Resources'
:
PdfDict
({
'/ProcSet'
:
PdfArray
([
const
PdfName
(
'/PDF'
),
]),
}),
'/Contents'
:
content
.
ref
(),
}));
pages
.
params
[
'/Kids'
]
=
PdfArray
([
page
.
ref
()]);
os
.
putString
(
'%PDF-1.4
\n
'
);
os
.
putBytes
(
const
<
int
>[
0x25
,
0xC2
,
0xA5
,
0xC2
,
0xB1
,
0xC3
,
0xAB
,
0x0A
]);
xref
.
add
(
PdfXref
(
1
,
os
.
offset
));
final
cat
=
const
BasicObject
(
1
)..
write
(
os
,
catalog
);
xref
.
add
(
PdfXref
(
2
,
os
.
offset
));
const
BasicObject
(
2
).
write
(
os
,
pages
);
xref
.
add
(
PdfXref
(
3
,
os
.
offset
));
const
BasicObject
(
3
).
write
(
os
,
page
);
xref
.
add
(
PdfXref
(
4
,
os
.
offset
));
const
BasicObject
(
4
).
write
(
os
,
content
);
final
xrefOffset
=
xref
.
outputLegacy
(
cat
,
os
,
PdfDict
({
'/Size'
:
PdfNum
(
xref
.
offsets
.
length
+
1
),
'/Root'
:
const
PdfIndirect
(
1
,
0
),
final
catalog
=
BasicObject
(
objser:
objser
++,
params:
PdfDict
({
'/Type'
:
const
PdfName
(
'/Catalog'
),
'/Pages'
:
pages
.
ref
(),
}));
os
.
putString
(
'startxref
\n
$xrefOffset
\n
%%EOF
\n
'
);
final
os
=
PdfStream
();
final
xref
=
PdfXrefTable
();
xref
.
objects
.
addAll
([
catalog
,
pages
,
page
,
content
,
]);
xref
.
output
(
catalog
,
os
);
final
file
=
File
(
'minimal.pdf'
);
await
file
.
writeAsBytes
(
os
.
output
());
...
...
Please
register
or
login
to post a comment