David PHAM-VAN

Reorganize data types

Showing 57 changed files with 1257 additions and 876 deletions
... ... @@ -11,6 +11,7 @@
- Add support for deleted objects
- Draw page content only if not empty
- Fix Page Content
- Reorganize data types
## 3.9.0
... ...
... ... @@ -21,6 +21,8 @@ export 'src/pdf/document_parser.dart';
export 'src/pdf/exif.dart';
export 'src/pdf/font/font_metrics.dart';
export 'src/pdf/font/ttf_parser.dart';
export 'src/pdf/format/name.dart';
export 'src/pdf/format/object_base.dart' show DeflateCallback, PdfVersion;
export 'src/pdf/graphic_state.dart';
export 'src/pdf/graphics.dart';
export 'src/pdf/obj/annotation.dart';
... ...
/*
* 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 'dart:collection';
import 'dart:convert';
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:meta/meta.dart';
import 'ascii85.dart';
import 'color.dart';
import 'obj/object.dart';
import 'stream.dart';
const _kIndentSize = 2;
abstract class PdfDataType {
const PdfDataType();
void output(PdfStream s, [int? indent]);
PdfStream _toStream() {
final s = PdfStream();
output(s);
return s;
}
@override
String toString() {
return String.fromCharCodes(_toStream().output());
}
@visibleForTesting
Uint8List toList() {
return _toStream().output();
}
}
class PdfBool extends PdfDataType {
const PdfBool(this.value);
final bool value;
@override
void output(PdfStream s, [int? indent]) {
s.putString(value ? 'true' : 'false');
}
@override
bool operator ==(Object other) {
if (other is PdfBool) {
return value == other.value;
}
return false;
}
@override
int get hashCode => value.hashCode;
}
class PdfNum extends PdfDataType {
const PdfNum(this.value)
: assert(value != double.infinity),
assert(value != double.negativeInfinity);
static const int precision = 5;
final num value;
@override
void output(PdfStream s, [int? indent]) {
assert(!value.isNaN);
assert(!value.isInfinite);
if (value is int) {
s.putString(value.toInt().toString());
} else {
var r = value.toStringAsFixed(precision);
if (r.contains('.')) {
var n = r.length - 1;
while (r[n] == '0') {
n--;
}
if (r[n] == '.') {
n--;
}
r = r.substring(0, n + 1);
}
s.putString(r);
}
}
@override
bool operator ==(Object other) {
if (other is PdfNum) {
return value == other.value;
}
return false;
}
PdfNum operator |(PdfNum other) {
return PdfNum(value.toInt() | other.value.toInt());
}
@override
int get hashCode => value.hashCode;
}
class PdfNumList extends PdfDataType {
const PdfNumList(this.values);
final List<num> values;
@override
void output(PdfStream s, [int? indent]) {
for (var n = 0; n < values.length; n++) {
if (n > 0) {
s.putByte(0x20);
}
PdfNum(values[n]).output(s, indent);
}
}
@override
bool operator ==(Object other) {
if (other is PdfNumList) {
return values == other.values;
}
return false;
}
@override
int get hashCode => values.hashCode;
}
enum PdfStringFormat { binary, literal }
class PdfString extends PdfDataType {
const PdfString(this.value, [this.format = PdfStringFormat.literal]);
factory PdfString.fromString(String value) {
return PdfString(_string(value), PdfStringFormat.literal);
}
factory PdfString.fromStream(PdfStream value,
[PdfStringFormat format = PdfStringFormat.literal]) {
return PdfString(value.output(), format);
}
factory PdfString.fromDate(DateTime date) {
return PdfString(_date(date));
}
final Uint8List value;
final PdfStringFormat format;
static Uint8List _string(String value) {
try {
return latin1.encode(value);
} catch (e) {
return Uint8List.fromList(<int>[0xfe, 0xff] + _encodeUtf16be(value));
}
}
static Uint8List _date(DateTime date) {
final utcDate = date.toUtc();
final year = utcDate.year.toString().padLeft(4, '0');
final month = utcDate.month.toString().padLeft(2, '0');
final day = utcDate.day.toString().padLeft(2, '0');
final hour = utcDate.hour.toString().padLeft(2, '0');
final minute = utcDate.minute.toString().padLeft(2, '0');
final second = utcDate.second.toString().padLeft(2, '0');
return _string('D:$year$month$day$hour$minute${second}Z');
}
/// Produce a list of UTF-16BE encoded bytes.
static List<int> _encodeUtf16be(String str) {
const unicodeReplacementCharacterCodePoint = 0xfffd;
const unicodeByteZeroMask = 0xff;
const unicodeByteOneMask = 0xff00;
const unicodeValidRangeMax = 0x10ffff;
const unicodePlaneOneMax = 0xffff;
const unicodeUtf16ReservedLo = 0xd800;
const unicodeUtf16ReservedHi = 0xdfff;
const unicodeUtf16Offset = 0x10000;
const unicodeUtf16SurrogateUnit0Base = 0xd800;
const unicodeUtf16SurrogateUnit1Base = 0xdc00;
const unicodeUtf16HiMask = 0xffc00;
const unicodeUtf16LoMask = 0x3ff;
final encoding = <int>[];
void add(int unit) {
encoding.add((unit & unicodeByteOneMask) >> 8);
encoding.add(unit & unicodeByteZeroMask);
}
for (final unit in str.codeUnits) {
if ((unit >= 0 && unit < unicodeUtf16ReservedLo) ||
(unit > unicodeUtf16ReservedHi && unit <= unicodePlaneOneMax)) {
add(unit);
} else if (unit > unicodePlaneOneMax && unit <= unicodeValidRangeMax) {
final base = unit - unicodeUtf16Offset;
add(unicodeUtf16SurrogateUnit0Base +
((base & unicodeUtf16HiMask) >> 10));
add(unicodeUtf16SurrogateUnit1Base + (base & unicodeUtf16LoMask));
} else {
add(unicodeReplacementCharacterCodePoint);
}
}
return encoding;
}
/// Escape special characters
/// \ddd Character code ddd (octal)
void _putTextBytes(PdfStream s, List<int> b) {
for (final c in b) {
switch (c) {
case 0x0a: // \n Line feed (LF)
s.putByte(0x5c);
s.putByte(0x6e);
break;
case 0x0d: // \r Carriage return (CR)
s.putByte(0x5c);
s.putByte(0x72);
break;
case 0x09: // \t Horizontal tab (HT)
s.putByte(0x5c);
s.putByte(0x74);
break;
case 0x08: // \b Backspace (BS)
s.putByte(0x5c);
s.putByte(0x62);
break;
case 0x0c: // \f Form feed (FF)
s.putByte(0x5c);
s.putByte(0x66);
break;
case 0x28: // \( Left parenthesis
s.putByte(0x5c);
s.putByte(0x28);
break;
case 0x29: // \) Right parenthesis
s.putByte(0x5c);
s.putByte(0x29);
break;
case 0x5c: // \\ Backslash
s.putByte(0x5c);
s.putByte(0x5c);
break;
default:
s.putByte(c);
}
}
}
/// Returns the ASCII/Unicode code unit corresponding to the hexadecimal digit
/// [digit].
int _codeUnitForDigit(int digit) =>
digit < 10 ? digit + 0x30 : digit + 0x61 - 10;
void _output(PdfStream s, Uint8List value) {
switch (format) {
case PdfStringFormat.binary:
s.putByte(0x3c);
for (final byte in value) {
s.putByte(_codeUnitForDigit((byte & 0xF0) >> 4));
s.putByte(_codeUnitForDigit(byte & 0x0F));
}
s.putByte(0x3e);
break;
case PdfStringFormat.literal:
s.putByte(40);
_putTextBytes(s, value);
s.putByte(41);
break;
}
}
@override
void output(PdfStream s, [int? indent]) {
_output(s, value);
}
@override
bool operator ==(Object other) {
if (other is PdfString) {
return value == other.value;
}
return false;
}
@override
int get hashCode => value.hashCode;
}
class PdfSecString extends PdfString {
const PdfSecString(this.object, Uint8List value,
[PdfStringFormat format = PdfStringFormat.binary])
: super(value, format);
factory PdfSecString.fromString(
PdfObject object,
String value, [
PdfStringFormat format = PdfStringFormat.literal,
]) {
return PdfSecString(
object,
PdfString._string(value),
format,
);
}
factory PdfSecString.fromStream(
PdfObject object,
PdfStream value, [
PdfStringFormat format = PdfStringFormat.literal,
]) {
return PdfSecString(
object,
value.output(),
format,
);
}
factory PdfSecString.fromDate(PdfObject object, DateTime date) {
return PdfSecString(
object,
PdfString._date(date),
PdfStringFormat.literal,
);
}
final PdfObject object;
@override
void output(PdfStream s, [int? indent]) {
if (object.pdfDocument.encryption == null) {
return super.output(s, indent);
}
final enc = object.pdfDocument.encryption!.encrypt(value, object);
_output(s, enc);
}
}
class PdfName extends PdfDataType {
const PdfName(this.value);
final String value;
@override
void output(PdfStream s, [int? indent]) {
assert(value[0] == '/');
final bytes = <int>[];
for (final c in value.codeUnits) {
assert(c < 0xff && c > 0x00);
if (c < 0x21 ||
c > 0x7E ||
c == 0x23 ||
(c == 0x2f && bytes.isNotEmpty) ||
c == 0x5b ||
c == 0x5d ||
c == 0x28 ||
c == 0x3c ||
c == 0x3e) {
bytes.add(0x23);
final x = c.toRadixString(16).padLeft(2, '0');
bytes.addAll(x.codeUnits);
} else {
bytes.add(c);
}
}
s.putBytes(bytes);
}
@override
bool operator ==(Object other) {
if (other is PdfName) {
return value == other.value;
}
return false;
}
@override
int get hashCode => value.hashCode;
}
class PdfNull extends PdfDataType {
const PdfNull();
@override
void output(PdfStream s, [int? indent]) {
s.putString('null');
}
@override
bool operator ==(Object other) {
return other is PdfNull;
}
@override
int get hashCode => null.hashCode;
}
class PdfIndirect extends PdfDataType {
const PdfIndirect(this.ser, this.gen);
final int ser;
final int gen;
@override
void output(PdfStream s, [int? indent]) {
s.putString('$ser $gen R');
}
@override
bool operator ==(Object other) {
if (other is PdfIndirect) {
return ser == other.ser && gen == other.gen;
}
return false;
}
@override
int get hashCode => ser.hashCode + gen.hashCode;
}
class PdfArray<T extends PdfDataType> extends PdfDataType {
PdfArray([Iterable<T>? values]) {
if (values != null) {
this.values.addAll(values);
}
}
static PdfArray<PdfIndirect> fromObjects(List<PdfObject> objects) {
return PdfArray(
objects.map<PdfIndirect>((PdfObject e) => e.ref()).toList());
}
static PdfArray<PdfNum> fromNum(List<num> list) {
return PdfArray(list.map<PdfNum>((num e) => PdfNum(e)).toList());
}
final List<T> values = <T>[];
void add(T v) {
values.add(v);
}
@override
void output(PdfStream s, [int? indent]) {
if (indent != null) {
s.putBytes(List<int>.filled(indent, 0x20));
indent += _kIndentSize;
}
s.putString('[');
if (values.isNotEmpty) {
for (var n = 0; n < values.length; n++) {
final val = values[n];
if (indent != null) {
s.putByte(0x0a);
if (val is! PdfDict && val is! PdfArray) {
s.putBytes(List<int>.filled(indent, 0x20));
}
} else {
if (n > 0 &&
!(val is PdfName ||
val is PdfString ||
val is PdfArray ||
val is PdfDict)) {
s.putByte(0x20);
}
}
val.output(s, indent);
}
if (indent != null) {
s.putByte(0x0a);
}
}
if (indent != null) {
indent -= _kIndentSize;
s.putBytes(List<int>.filled(indent, 0x20));
}
s.putString(']');
}
/// Make all values unique, preserving the order
void uniq() {
if (values.length <= 1) {
return;
}
// ignore: prefer_collection_literals
final uniques = LinkedHashMap<T, bool>();
for (final s in values) {
uniques[s] = true;
}
values.clear();
values.addAll(uniques.keys);
}
@override
bool operator ==(Object other) {
if (other is PdfArray) {
return values == other.values;
}
return false;
}
bool get isEmpty => values.isEmpty;
bool get isNotEmpty => values.isNotEmpty;
@override
int get hashCode => values.hashCode;
}
class PdfDict<T extends PdfDataType> extends PdfDataType {
factory PdfDict([Map<String, T>? values]) {
final _values = <String, T>{};
if (values != null) {
_values.addAll(values);
}
return PdfDict.values(_values);
}
const PdfDict.values([this.values = const {}]);
static PdfDict<PdfIndirect> fromObjectMap(Map<String, PdfObject> objects) {
return PdfDict(
objects.map<String, PdfIndirect>(
(String key, PdfObject value) =>
MapEntry<String, PdfIndirect>(key, value.ref()),
),
);
}
final Map<String, T> values;
bool get isNotEmpty => values.isNotEmpty;
operator []=(String k, T v) {
values[k] = v;
}
T? operator [](String k) {
return values[k];
}
@override
void output(PdfStream s, [int? indent]) {
if (indent != null) {
s.putBytes(List<int>.filled(indent, 0x20));
}
s.putBytes(const <int>[0x3c, 0x3c]);
var len = 0;
var n = 1;
if (indent != null) {
s.putByte(0x0a);
indent += _kIndentSize;
len = values.keys.fold<int>(0, (p, e) => math.max(p, e.length));
}
values.forEach((String k, T v) {
if (indent != null) {
s.putBytes(List<int>.filled(indent, 0x20));
n = len - k.length + 1;
}
s.putString(k);
if (indent != null) {
if (v is PdfDict || v is PdfArray) {
s.putByte(0x0a);
} else {
s.putBytes(List<int>.filled(n, 0x20));
}
} else {
if (v is PdfNum || v is PdfBool || v is PdfNull || v is PdfIndirect) {
s.putByte(0x20);
}
}
v.output(s, indent);
if (indent != null) {
s.putByte(0x0a);
}
});
if (indent != null) {
indent -= _kIndentSize;
s.putBytes(List<int>.filled(indent, 0x20));
}
s.putBytes(const <int>[0x3e, 0x3e]);
}
bool containsKey(String key) {
return values.containsKey(key);
}
void merge(PdfDict<T> other) {
for (final key in other.values.keys) {
final value = other[key]!;
final current = values[key];
if (current == null) {
values[key] = value;
} else if (value is PdfArray && current is PdfArray) {
current.values.addAll(value.values);
current.uniq();
} else if (value is PdfDict && current is PdfDict) {
current.merge(value);
} else {
values[key] = value;
}
}
}
void addAll(PdfDict<T> other) {
values.addAll(other.values);
}
@override
bool operator ==(Object other) {
if (other is PdfDict) {
return values == other.values;
}
return false;
}
@override
int get hashCode => values.hashCode;
}
class PdfDictStream extends PdfDict<PdfDataType> {
factory PdfDictStream({
required PdfObject object,
Map<String, PdfDataType>? values,
Uint8List? data,
bool isBinary = false,
bool encrypt = true,
bool compress = true,
}) {
return PdfDictStream.values(
object: object,
values: values ?? {},
data: data ?? Uint8List(0),
encrypt: encrypt,
compress: compress,
isBinary: isBinary,
);
}
PdfDictStream.values({
required this.object,
required Map<String, PdfDataType> values,
required this.data,
this.isBinary = false,
this.encrypt = true,
this.compress = true,
}) : super.values(values);
Uint8List data;
final PdfObject object;
final bool isBinary;
final bool encrypt;
final bool compress;
@override
void output(PdfStream s, [int? indent]) {
final _values = PdfDict(values);
Uint8List? _data;
if (_values.containsKey('/Filter')) {
// The data is already in the right format
_data = data;
} else if (compress && object.pdfDocument.compress) {
// Compress the data
final newData = Uint8List.fromList(object.pdfDocument.deflate!(data));
if (newData.lengthInBytes < data.lengthInBytes) {
_values['/Filter'] = const PdfName('/FlateDecode');
_data = newData;
}
}
if (_data == null) {
if (isBinary) {
// This is an Ascii85 stream
final e = Ascii85Encoder();
_data = e.convert(data);
_values['/Filter'] = const PdfName('/ASCII85Decode');
} else {
// This is a non-deflated stream
_data = data;
}
}
if (encrypt && object.pdfDocument.encryption != null) {
_data = object.pdfDocument.encryption!.encrypt(_data, object);
}
_values['/Length'] = PdfNum(_data.length);
_values.output(s, indent);
if (indent != null) {
s.putByte(0x0a);
}
s.putString('stream\n');
s.putBytes(_data);
s.putString('\nendstream\n');
}
}
class PdfColorType extends PdfDataType {
const PdfColorType(this.color);
final PdfColor color;
@override
void output(PdfStream s, [int? indent]) {
if (color is PdfColorCmyk) {
final k = color as PdfColorCmyk;
PdfArray.fromNum(<double>[
k.cyan,
k.magenta,
k.yellow,
k.black,
]).output(s, indent);
} else {
PdfArray.fromNum(<double>[
color.red,
color.green,
color.blue,
]).output(s, indent);
}
}
@override
bool operator ==(Object other) {
if (other is PdfColorType) {
return color == other.color;
}
return false;
}
@override
int get hashCode => color.hashCode;
}
... ... @@ -20,6 +20,9 @@ import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'document_parser.dart';
import 'format/object_base.dart';
import 'format/stream.dart';
import 'format/xref.dart';
import 'graphic_state.dart';
import 'io/vm.dart' if (dart.library.js) 'io/js.dart';
import 'obj/catalog.dart';
... ... @@ -34,17 +37,6 @@ import 'obj/page_label.dart';
import 'obj/page_list.dart';
import 'obj/signature.dart';
import 'output.dart';
import 'stream.dart';
import 'xref.dart';
/// PDF version to generate
enum PdfVersion {
/// PDF 1.4
pdf_1_4,
/// PDF 1.5 to 1.7
pdf_1_5,
}
/// Display hint for the PDF viewer
enum PdfPageMode {
... ... @@ -66,9 +58,6 @@ enum PdfPageMode {
fullscreen
}
/// Callback used to compress the data
typedef DeflateCallback = List<int> Function(List<int> data);
/// This class is the base of the Pdf generator. A [PdfDocument] class is
/// created for a document, and each page, object, annotation,
/// etc is added to the document.
... ... @@ -163,6 +152,7 @@ class PdfDocument {
bool get compress => deflate != null;
/// Output a PDF document with comments and formatted data
final bool verbose;
/// Generates the document ID
... ...
... ... @@ -17,6 +17,7 @@
import 'dart:typed_data';
import 'document.dart';
import 'format/object_base.dart';
/// Base class for loading an existing PDF document.
abstract class PdfDocumentParserBase {
... ...
/*
* 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 'dart:collection';
import '../color.dart';
import 'base.dart';
import 'dict.dart';
import 'indirect.dart';
import 'name.dart';
import 'num.dart';
import 'object_base.dart';
import 'stream.dart';
import 'string.dart';
class PdfArray<T extends PdfDataType> extends PdfDataType {
PdfArray([Iterable<T>? values]) {
if (values != null) {
this.values.addAll(values);
}
}
static PdfArray<PdfIndirect> fromObjects(List<PdfObjectBase> objects) {
return PdfArray(objects.map<PdfIndirect>((e) => e.ref()).toList());
}
static PdfArray<PdfNum> fromNum(List<num> list) {
return PdfArray(list.map<PdfNum>((num e) => PdfNum(e)).toList());
}
static PdfArray fromColor(PdfColor color) {
if (color is PdfColorCmyk) {
return PdfArray.fromNum(<double>[
color.cyan,
color.magenta,
color.yellow,
color.black,
]);
} else {
return PdfArray.fromNum(<double>[
color.red,
color.green,
color.blue,
]);
}
}
final List<T> values = <T>[];
void add(T v) {
values.add(v);
}
@override
void output(PdfStream s, [int? indent]) {
if (indent != null) {
s.putBytes(List<int>.filled(indent, 0x20));
indent += kIndentSize;
}
s.putString('[');
if (values.isNotEmpty) {
for (var n = 0; n < values.length; n++) {
final val = values[n];
if (indent != null) {
s.putByte(0x0a);
if (val is! PdfDict && val is! PdfArray) {
s.putBytes(List<int>.filled(indent, 0x20));
}
} else {
if (n > 0 &&
!(val is PdfName ||
val is PdfString ||
val is PdfArray ||
val is PdfDict)) {
s.putByte(0x20);
}
}
val.output(s, indent);
}
if (indent != null) {
s.putByte(0x0a);
}
}
if (indent != null) {
indent -= kIndentSize;
s.putBytes(List<int>.filled(indent, 0x20));
}
s.putString(']');
}
/// Make all values unique, preserving the order
void uniq() {
if (values.length <= 1) {
return;
}
// ignore: prefer_collection_literals
final uniques = LinkedHashMap<T, bool>();
for (final s in values) {
uniques[s] = true;
}
values.clear();
values.addAll(uniques.keys);
}
@override
bool operator ==(Object other) {
if (other is PdfArray) {
return values == other.values;
}
return false;
}
bool get isEmpty => values.isEmpty;
bool get isNotEmpty => values.isNotEmpty;
@override
int get hashCode => values.hashCode;
}
... ...
/*
* 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 'dart:typed_data';
import 'package:meta/meta.dart';
import 'stream.dart';
const kIndentSize = 2;
abstract class PdfDataType {
const PdfDataType();
void output(PdfStream s, [int? indent]);
PdfStream _toStream() {
final s = PdfStream();
output(s);
return s;
}
@override
String toString() {
return String.fromCharCodes(_toStream().output());
}
@visibleForTesting
Uint8List toList() {
return _toStream().output();
}
}
... ...
/*
* 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 'base.dart';
import 'stream.dart';
class PdfBool extends PdfDataType {
const PdfBool(this.value);
final bool value;
@override
void output(PdfStream s, [int? indent]) {
s.putString(value ? 'true' : 'false');
}
@override
bool operator ==(Object other) {
if (other is PdfBool) {
return value == other.value;
}
return false;
}
@override
int get hashCode => value.hashCode;
}
... ...
/*
* 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 'dart:math' as math;
import 'array.dart';
import 'base.dart';
import 'bool.dart';
import 'indirect.dart';
import 'null.dart';
import 'num.dart';
import 'object_base.dart';
import 'stream.dart';
class PdfDict<T extends PdfDataType> extends PdfDataType {
factory PdfDict([Map<String, T>? values]) {
final _values = <String, T>{};
if (values != null) {
_values.addAll(values);
}
return PdfDict.values(_values);
}
const PdfDict.values([this.values = const {}]);
static PdfDict<PdfIndirect> fromObjectMap(
Map<String, PdfObjectBase> objects) {
return PdfDict(
objects.map<String, PdfIndirect>(
(key, value) => MapEntry<String, PdfIndirect>(key, value.ref()),
),
);
}
final Map<String, T> values;
bool get isNotEmpty => values.isNotEmpty;
operator []=(String k, T v) {
values[k] = v;
}
T? operator [](String k) {
return values[k];
}
@override
void output(PdfStream s, [int? indent]) {
if (indent != null) {
s.putBytes(List<int>.filled(indent, 0x20));
}
s.putBytes(const <int>[0x3c, 0x3c]);
var len = 0;
var n = 1;
if (indent != null) {
s.putByte(0x0a);
indent += kIndentSize;
len = values.keys.fold<int>(0, (p, e) => math.max(p, e.length));
}
values.forEach((String k, T v) {
if (indent != null) {
s.putBytes(List<int>.filled(indent, 0x20));
n = len - k.length + 1;
}
s.putString(k);
if (indent != null) {
if (v is PdfDict || v is PdfArray) {
s.putByte(0x0a);
} else {
s.putBytes(List<int>.filled(n, 0x20));
}
} else {
if (v is PdfNum || v is PdfBool || v is PdfNull || v is PdfIndirect) {
s.putByte(0x20);
}
}
v.output(s, indent);
if (indent != null) {
s.putByte(0x0a);
}
});
if (indent != null) {
indent -= kIndentSize;
s.putBytes(List<int>.filled(indent, 0x20));
}
s.putBytes(const <int>[0x3e, 0x3e]);
}
bool containsKey(String key) {
return values.containsKey(key);
}
void merge(PdfDict<T> other) {
for (final key in other.values.keys) {
final value = other[key]!;
final current = values[key];
if (current == null) {
values[key] = value;
} else if (value is PdfArray && current is PdfArray) {
current.values.addAll(value.values);
current.uniq();
} else if (value is PdfDict && current is PdfDict) {
current.merge(value);
} else {
values[key] = value;
}
}
}
void addAll(PdfDict<T> other) {
values.addAll(other.values);
}
@override
bool operator ==(Object other) {
if (other is PdfDict) {
return values == other.values;
}
return false;
}
@override
int get hashCode => values.hashCode;
}
... ...
/*
* 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 'dart:typed_data';
import 'ascii85.dart';
import 'base.dart';
import 'dict.dart';
import 'name.dart';
import 'num.dart';
import 'object_base.dart';
import 'stream.dart';
class PdfDictStream extends PdfDict<PdfDataType> {
factory PdfDictStream({
required PdfObjectBase object,
Map<String, PdfDataType>? values,
Uint8List? data,
bool isBinary = false,
bool encrypt = true,
bool compress = true,
}) {
return PdfDictStream.values(
object: object,
values: values ?? {},
data: data ?? Uint8List(0),
encrypt: encrypt,
compress: compress,
isBinary: isBinary,
);
}
PdfDictStream.values({
required this.object,
required Map<String, PdfDataType> values,
required this.data,
this.isBinary = false,
this.encrypt = true,
this.compress = true,
}) : super.values(values);
Uint8List data;
final PdfObjectBase object;
final bool isBinary;
final bool encrypt;
final bool compress;
@override
void output(PdfStream s, [int? indent]) {
final _values = PdfDict(values);
Uint8List? _data;
if (_values.containsKey('/Filter')) {
// The data is already in the right format
_data = data;
} else if (compress && object.deflate != null) {
// Compress the data
final newData = Uint8List.fromList(object.deflate!(data));
if (newData.lengthInBytes < data.lengthInBytes) {
_values['/Filter'] = const PdfName('/FlateDecode');
_data = newData;
}
}
if (_data == null) {
if (isBinary) {
// This is an Ascii85 stream
final e = Ascii85Encoder();
_data = e.convert(data);
_values['/Filter'] = const PdfName('/ASCII85Decode');
} else {
// This is a non-deflated stream
_data = data;
}
}
if (encrypt && object.encryptCallback != null) {
_data = object.encryptCallback!(_data, object);
}
_values['/Length'] = PdfNum(_data.length);
_values.output(s, indent);
if (indent != null) {
s.putByte(0x0a);
}
s.putString('stream\n');
s.putBytes(_data);
s.putString('\nendstream\n');
}
}
... ...
/*
* 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 'base.dart';
import 'stream.dart';
class PdfIndirect extends PdfDataType {
const PdfIndirect(this.ser, this.gen);
final int ser;
final int gen;
@override
void output(PdfStream s, [int? indent]) {
s.putString('$ser $gen R');
}
@override
bool operator ==(Object other) {
if (other is PdfIndirect) {
return ser == other.ser && gen == other.gen;
}
return false;
}
@override
int get hashCode => ser.hashCode + gen.hashCode;
}
... ...
/*
* 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 'base.dart';
import 'stream.dart';
class PdfName extends PdfDataType {
const PdfName(this.value);
final String value;
@override
void output(PdfStream s, [int? indent]) {
assert(value[0] == '/');
final bytes = <int>[];
for (final c in value.codeUnits) {
assert(c < 0xff && c > 0x00);
if (c < 0x21 ||
c > 0x7E ||
c == 0x23 ||
(c == 0x2f && bytes.isNotEmpty) ||
c == 0x5b ||
c == 0x5d ||
c == 0x28 ||
c == 0x3c ||
c == 0x3e) {
bytes.add(0x23);
final x = c.toRadixString(16).padLeft(2, '0');
bytes.addAll(x.codeUnits);
} else {
bytes.add(c);
}
}
s.putBytes(bytes);
}
@override
bool operator ==(Object other) {
if (other is PdfName) {
return value == other.value;
}
return false;
}
@override
int get hashCode => value.hashCode;
}
... ...
/*
* 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 'base.dart';
import 'stream.dart';
class PdfNull extends PdfDataType {
const PdfNull();
@override
void output(PdfStream s, [int? indent]) {
s.putString('null');
}
@override
bool operator ==(Object other) {
return other is PdfNull;
}
@override
int get hashCode => null.hashCode;
}
... ...
/*
* 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 'base.dart';
import 'stream.dart';
class PdfNum extends PdfDataType {
const PdfNum(this.value)
: assert(value != double.infinity),
assert(value != double.negativeInfinity);
static const int precision = 5;
final num value;
@override
void output(PdfStream s, [int? indent]) {
assert(!value.isNaN);
assert(!value.isInfinite);
if (value is int) {
s.putString(value.toInt().toString());
} else {
var r = value.toStringAsFixed(precision);
if (r.contains('.')) {
var n = r.length - 1;
while (r[n] == '0') {
n--;
}
if (r[n] == '.') {
n--;
}
r = r.substring(0, n + 1);
}
s.putString(r);
}
}
@override
bool operator ==(Object other) {
if (other is PdfNum) {
return value == other.value;
}
return false;
}
PdfNum operator |(PdfNum other) {
return PdfNum(value.toInt() | other.value.toInt());
}
@override
int get hashCode => value.hashCode;
}
class PdfNumList extends PdfDataType {
const PdfNumList(this.values);
final List<num> values;
@override
void output(PdfStream s, [int? indent]) {
for (var n = 0; n < values.length; n++) {
if (n > 0) {
s.putByte(0x20);
}
PdfNum(values[n]).output(s, indent);
}
}
@override
bool operator ==(Object other) {
if (other is PdfNumList) {
return values == other.values;
}
return false;
}
@override
int get hashCode => values.hashCode;
}
... ...
/*
* 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 'dart:typed_data';
import 'indirect.dart';
/// Callback used to compress the data
typedef DeflateCallback = List<int> Function(List<int> data);
/// Callback used to encrypt the value of a [PdfDictStream] or a [PdfEncStream]
typedef PdfEncryptCallback = Uint8List Function(
Uint8List input, PdfObjectBase object);
/// PDF version to generate
enum PdfVersion {
/// PDF 1.4
pdf_1_4,
/// PDF 1.5 to 1.7
pdf_1_5,
}
mixin PdfObjectBase {
/// This is the unique serial number for this object.
int get objser;
/// This is the generation number for this object.
int get objgen => 0;
/// Callback used to compress the data
DeflateCallback? get deflate => null;
/// Callback used to encrypt the value of a [PdfDictStream] or a [PdfEncStream]
PdfEncryptCallback? get encryptCallback => null;
/// Output a PDF document with comments and formatted data
bool get verbose => false;
PdfVersion get version => PdfVersion.pdf_1_5;
/// Returns the unique serial number in Pdf format
PdfIndirect ref() => PdfIndirect(objser, objgen);
}
... ...
/*
* 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 'dart:convert';
import 'dart:typed_data';
import 'base.dart';
import 'object_base.dart';
import 'stream.dart';
enum PdfStringFormat { binary, literal }
class PdfString extends PdfDataType {
const PdfString(this.value, [this.format = PdfStringFormat.literal]);
factory PdfString.fromString(String value) {
return PdfString(_string(value), PdfStringFormat.literal);
}
factory PdfString.fromStream(PdfStream value,
[PdfStringFormat format = PdfStringFormat.literal]) {
return PdfString(value.output(), format);
}
factory PdfString.fromDate(DateTime date) {
return PdfString(_date(date));
}
final Uint8List value;
final PdfStringFormat format;
static Uint8List _string(String value) {
try {
return latin1.encode(value);
} catch (e) {
return Uint8List.fromList(<int>[0xfe, 0xff] + _encodeUtf16be(value));
}
}
static Uint8List _date(DateTime date) {
final utcDate = date.toUtc();
final year = utcDate.year.toString().padLeft(4, '0');
final month = utcDate.month.toString().padLeft(2, '0');
final day = utcDate.day.toString().padLeft(2, '0');
final hour = utcDate.hour.toString().padLeft(2, '0');
final minute = utcDate.minute.toString().padLeft(2, '0');
final second = utcDate.second.toString().padLeft(2, '0');
return _string('D:$year$month$day$hour$minute${second}Z');
}
/// Produce a list of UTF-16BE encoded bytes.
static List<int> _encodeUtf16be(String str) {
const unicodeReplacementCharacterCodePoint = 0xfffd;
const unicodeByteZeroMask = 0xff;
const unicodeByteOneMask = 0xff00;
const unicodeValidRangeMax = 0x10ffff;
const unicodePlaneOneMax = 0xffff;
const unicodeUtf16ReservedLo = 0xd800;
const unicodeUtf16ReservedHi = 0xdfff;
const unicodeUtf16Offset = 0x10000;
const unicodeUtf16SurrogateUnit0Base = 0xd800;
const unicodeUtf16SurrogateUnit1Base = 0xdc00;
const unicodeUtf16HiMask = 0xffc00;
const unicodeUtf16LoMask = 0x3ff;
final encoding = <int>[];
void add(int unit) {
encoding.add((unit & unicodeByteOneMask) >> 8);
encoding.add(unit & unicodeByteZeroMask);
}
for (final unit in str.codeUnits) {
if ((unit >= 0 && unit < unicodeUtf16ReservedLo) ||
(unit > unicodeUtf16ReservedHi && unit <= unicodePlaneOneMax)) {
add(unit);
} else if (unit > unicodePlaneOneMax && unit <= unicodeValidRangeMax) {
final base = unit - unicodeUtf16Offset;
add(unicodeUtf16SurrogateUnit0Base +
((base & unicodeUtf16HiMask) >> 10));
add(unicodeUtf16SurrogateUnit1Base + (base & unicodeUtf16LoMask));
} else {
add(unicodeReplacementCharacterCodePoint);
}
}
return encoding;
}
/// Escape special characters
/// \ddd Character code ddd (octal)
void _putTextBytes(PdfStream s, List<int> b) {
for (final c in b) {
switch (c) {
case 0x0a: // \n Line feed (LF)
s.putByte(0x5c);
s.putByte(0x6e);
break;
case 0x0d: // \r Carriage return (CR)
s.putByte(0x5c);
s.putByte(0x72);
break;
case 0x09: // \t Horizontal tab (HT)
s.putByte(0x5c);
s.putByte(0x74);
break;
case 0x08: // \b Backspace (BS)
s.putByte(0x5c);
s.putByte(0x62);
break;
case 0x0c: // \f Form feed (FF)
s.putByte(0x5c);
s.putByte(0x66);
break;
case 0x28: // \( Left parenthesis
s.putByte(0x5c);
s.putByte(0x28);
break;
case 0x29: // \) Right parenthesis
s.putByte(0x5c);
s.putByte(0x29);
break;
case 0x5c: // \\ Backslash
s.putByte(0x5c);
s.putByte(0x5c);
break;
default:
s.putByte(c);
}
}
}
/// Returns the ASCII/Unicode code unit corresponding to the hexadecimal digit
/// [digit].
int _codeUnitForDigit(int digit) =>
digit < 10 ? digit + 0x30 : digit + 0x61 - 10;
void _output(PdfStream s, Uint8List value) {
switch (format) {
case PdfStringFormat.binary:
s.putByte(0x3c);
for (final byte in value) {
s.putByte(_codeUnitForDigit((byte & 0xF0) >> 4));
s.putByte(_codeUnitForDigit(byte & 0x0F));
}
s.putByte(0x3e);
break;
case PdfStringFormat.literal:
s.putByte(40);
_putTextBytes(s, value);
s.putByte(41);
break;
}
}
@override
void output(PdfStream s, [int? indent]) {
_output(s, value);
}
@override
bool operator ==(Object other) {
if (other is PdfString) {
return value == other.value;
}
return false;
}
@override
int get hashCode => value.hashCode;
}
class PdfSecString extends PdfString {
const PdfSecString(this.object, Uint8List value,
[PdfStringFormat format = PdfStringFormat.binary])
: super(value, format);
factory PdfSecString.fromString(
PdfObjectBase object,
String value, [
PdfStringFormat format = PdfStringFormat.literal,
]) {
return PdfSecString(
object,
PdfString._string(value),
format,
);
}
factory PdfSecString.fromStream(
PdfObjectBase object,
PdfStream value, [
PdfStringFormat format = PdfStringFormat.literal,
]) {
return PdfSecString(
object,
value.output(),
format,
);
}
factory PdfSecString.fromDate(PdfObjectBase object, DateTime date) {
return PdfSecString(
object,
PdfString._date(date),
PdfStringFormat.literal,
);
}
final PdfObjectBase object;
@override
void output(PdfStream s, [int? indent]) {
if (object.encryptCallback == null) {
return super.output(s, indent);
}
final enc = object.encryptCallback!(value, object);
_output(s, enc);
}
}
... ...
... ... @@ -17,8 +17,14 @@
import 'dart:math' as math;
import 'dart:typed_data';
import 'data_types.dart';
import 'obj/object.dart';
import 'array.dart';
import 'base.dart';
import 'dict.dart';
import 'dict_stream.dart';
import 'indirect.dart';
import 'name.dart';
import 'num.dart';
import 'object_base.dart';
import 'stream.dart';
enum PdfCrossRefEntryType { free, inUse, compressed }
... ... @@ -49,14 +55,14 @@ class PdfXref {
final PdfCrossRefEntryType type;
/// The xref in the format of the xref section in the Pdf file
String ref() {
String _legacyRef() {
return '${offset.toString().padLeft(10, '0')} ${generation.toString().padLeft(5, '0')}${type == PdfCrossRefEntryType.inUse ? ' n ' : ' f '}';
}
PdfIndirect? get container => object == null ? null : PdfIndirect(object!, 0);
/// The xref in the format of the compressed xref section in the Pdf file
int cref(ByteData o, int ofs, List<int> w) {
int _compressedRef(ByteData o, int ofs, List<int> w) {
assert(w.length >= 3);
void setVal(int l, int v) {
... ... @@ -105,7 +111,7 @@ class PdfXrefTable extends PdfDataType {
s.putString('$firstId ${block.length}\n');
for (final x in block) {
s.putString(x.ref());
s.putString(x._legacyRef());
s.putByte(0x0a);
}
}
... ... @@ -122,15 +128,15 @@ class PdfXrefTable extends PdfDataType {
return s.toString();
}
int outputLegacy(PdfObject object, PdfStream s, PdfDict params) {
int outputLegacy(PdfObjectBase object, PdfStream s, PdfDict params) {
// Now scan through the offsets list. They should be in sequence.
offsets.sort((a, b) => a.id.compareTo(b.id));
assert(() {
if (object.pdfDocument.verbose) {
if (object.verbose) {
s.putComment('');
s.putComment('-' * 78);
s.putComment('$runtimeType ${object.pdfDocument.version.name}\n$this');
s.putComment('$runtimeType ${object.version.name}\n$this');
}
return true;
}());
... ... @@ -169,20 +175,20 @@ class PdfXrefTable extends PdfDataType {
// the trailer object
assert(() {
if (object.pdfDocument.verbose) {
if (object.verbose) {
s.putComment('');
}
return true;
}());
s.putString('trailer\n');
params.output(s, object.pdfDocument.verbose ? 0 : null);
params.output(s, object.verbose ? 0 : null);
s.putByte(0x0a);
return objOffset;
}
/// Output a compressed cross-reference table
int outputCompressed(PdfObject object, PdfStream s, PdfDict params) {
int outputCompressed(PdfObjectBase object, PdfStream s, PdfDict params) {
final offset = s.offset;
// Sort all references
... ... @@ -229,15 +235,15 @@ class PdfXrefTable extends PdfDataType {
ofs += wl;
for (final x in offsets) {
ofs = x.cref(o, ofs, w);
ofs = x._compressedRef(o, ofs, w);
}
// Write the object
assert(() {
if (object.pdfDocument.verbose) {
if (object.verbose) {
s.putComment('');
s.putComment('-' * 78);
s.putComment('$runtimeType ${object.pdfDocument.version.name}\n$this');
s.putComment('$runtimeType ${object.version.name}\n$this');
}
return true;
}());
... ... @@ -252,7 +258,7 @@ class PdfXrefTable extends PdfDataType {
isBinary: false,
encrypt: false,
values: params.values,
).output(s, object.pdfDocument.verbose ? 0 : null);
).output(s, object.verbose ? 0 : null);
s.putString('endobj\n');
return objOffset;
... ...
... ... @@ -18,8 +18,10 @@
import 'package:meta/meta.dart';
import 'data_types.dart';
import 'document.dart';
import 'format/dict.dart';
import 'format/name.dart';
import 'format/num.dart';
import 'obj/function.dart';
import 'obj/object_dict.dart';
import 'obj/smask.dart';
... ...
... ... @@ -22,7 +22,10 @@ import 'package:path_parsing/path_parsing.dart';
import 'package:vector_math/vector_math_64.dart';
import 'color.dart';
import 'data_types.dart';
import 'format/array.dart';
import 'format/name.dart';
import 'format/num.dart';
import 'format/stream.dart';
import 'graphic_state.dart';
import 'obj/font.dart';
import 'obj/graphic_stream.dart';
... ... @@ -31,7 +34,6 @@ import 'obj/page.dart';
import 'obj/pattern.dart';
import 'obj/shading.dart';
import 'rect.dart';
import 'stream.dart';
/// Shape to be used at the corners of paths that are stroked
enum PdfLineJoin {
... ...
... ... @@ -16,7 +16,7 @@
import 'package:archive/archive.dart';
import '../document.dart';
import '../format/object_base.dart';
/// Zip compression function
DeflateCallback defaultDeflate = const ZLibEncoder().encode;
... ...
... ... @@ -16,7 +16,7 @@
import 'dart:io';
import '../document.dart';
import '../format/object_base.dart';
/// Zip compression function
DeflateCallback defaultDeflate = zlib.encode;
... ...
... ... @@ -18,12 +18,18 @@ import 'package:meta/meta.dart';
import 'package:vector_math/vector_math_64.dart';
import '../color.dart';
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/base.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../format/null.dart';
import '../format/num.dart';
import '../format/stream.dart';
import '../format/string.dart';
import '../graphics.dart';
import '../point.dart';
import '../rect.dart';
import '../stream.dart';
import 'border.dart';
import 'font.dart';
import 'graphic_stream.dart';
... ... @@ -295,7 +301,7 @@ abstract class PdfAnnotBase {
}
if (color != null) {
params['/C'] = PdfColorType(color!);
params['/C'] = PdfArray.fromColor(color!);
}
if (subject != null) {
... ... @@ -441,7 +447,7 @@ class PdfAnnotSquare extends PdfAnnotBase {
void build(PdfPage page, PdfObject object, PdfDict params) {
super.build(page, object, params);
if (interiorColor != null) {
params['/IC'] = PdfColorType(interiorColor!);
params['/IC'] = PdfArray.fromColor(interiorColor!);
}
}
}
... ... @@ -474,7 +480,7 @@ class PdfAnnotCircle extends PdfAnnotBase {
void build(PdfPage page, PdfObject object, PdfDict params) {
super.build(page, object, params);
if (interiorColor != null) {
params['/IC'] = PdfColorType(interiorColor!);
params['/IC'] = PdfArray.fromColor(interiorColor!);
}
}
}
... ... @@ -525,7 +531,7 @@ class PdfAnnotPolygon extends PdfAnnotBase {
params['/Vertices'] = PdfArray.fromNum(verticies);
if (interiorColor != null) {
params['/IC'] = PdfColorType(interiorColor!);
params['/IC'] = PdfArray.fromColor(interiorColor!);
}
}
}
... ... @@ -631,11 +637,11 @@ abstract class PdfAnnotWidget extends PdfAnnotBase {
final mk = PdfDict();
if (color != null) {
mk.values['/BC'] = PdfColorType(color!);
mk.values['/BC'] = PdfArray.fromColor(color!);
}
if (backgroundColor != null) {
mk.values['/BG'] = PdfColorType(backgroundColor!);
mk.values['/BG'] = PdfArray.fromColor(backgroundColor!);
}
if (mk.values.isNotEmpty) {
... ...
... ... @@ -14,8 +14,8 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import 'object.dart';
/// An array object
... ...
... ... @@ -14,8 +14,10 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/name.dart';
import '../format/num.dart';
import 'annotation.dart';
import 'object_dict.dart';
... ...
... ... @@ -14,8 +14,11 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../format/num.dart';
import 'annotation.dart';
import 'metadata.dart';
import 'names.dart';
... ...
... ... @@ -2,7 +2,7 @@ import 'dart:math' as math;
import 'package:meta/meta.dart';
import '../stream.dart';
import '../format/stream.dart';
mixin PdfDiagnostic {
static const _maxSize = 300;
... ...
... ... @@ -17,7 +17,7 @@
import 'dart:typed_data';
import '../document.dart';
import 'object.dart';
import '../format/object_base.dart';
import 'object_dict.dart';
/// Encryption object
... ... @@ -26,5 +26,5 @@ abstract class PdfEncryption extends PdfObjectDict {
PdfEncryption(PdfDocument pdfDocument) : super(pdfDocument);
/// Encrypt some data
Uint8List encrypt(Uint8List input, PdfObject object);
Uint8List encrypt(Uint8List input, PdfObjectBase object);
}
... ...
... ... @@ -16,12 +16,13 @@
import 'dart:convert';
import '../data_types.dart';
import '../document.dart';
import '../font/font_metrics.dart';
import '../font/type1_fonts.dart';
import '../format/name.dart';
import '../format/stream.dart';
import '../format/string.dart';
import '../point.dart';
import '../stream.dart';
import 'object_dict.dart';
import 'type1_font.dart';
... ...
... ... @@ -14,7 +14,9 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../format/array.dart';
import '../format/name.dart';
import '../format/num.dart';
import 'object_dict.dart';
import 'object_stream.dart';
import 'ttffont.dart';
... ...
... ... @@ -16,8 +16,10 @@
import 'package:vector_math/vector_math_64.dart';
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/dict.dart';
import '../format/num.dart';
import 'font.dart';
import 'xobject.dart';
... ...
... ... @@ -15,8 +15,9 @@
*/
import '../color.dart';
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/num.dart';
import 'object_dict.dart';
import 'object_stream.dart';
... ...
... ... @@ -14,8 +14,11 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/bool.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../graphic_state.dart';
import 'font.dart';
import 'object_dict.dart';
... ...
... ... @@ -18,9 +18,11 @@ import 'dart:typed_data';
import 'package:image/image.dart' as im;
import '../data_types.dart';
import '../document.dart';
import '../exif.dart';
import '../format/indirect.dart';
import '../format/name.dart';
import '../format/num.dart';
import '../raster.dart';
import 'xobject.dart';
... ...
... ... @@ -14,8 +14,8 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/string.dart';
import 'object_dict.dart';
/// Information object
... ...
... ... @@ -19,8 +19,9 @@ import 'dart:typed_data';
import 'package:xml/xml.dart';
import '../data_types.dart';
import '../document.dart';
import '../format/dict_stream.dart';
import '../format/name.dart';
import 'object.dart';
/// Pdf Metadata
... ...
... ... @@ -14,8 +14,14 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/base.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../format/null.dart';
import '../format/num.dart';
import '../format/string.dart';
import 'object_dict.dart';
import 'page.dart';
... ...
... ... @@ -16,13 +16,15 @@
import 'package:meta/meta.dart';
import '../data_types.dart';
import '../document.dart';
import '../stream.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> with PdfDiagnostic {
abstract class PdfObject<T extends PdfDataType>
with PdfDiagnostic, PdfObjectBase {
/// This is usually called by extensors to this class, and sets the
/// Pdf Object Type
PdfObject(
... ... @@ -37,10 +39,10 @@ abstract class PdfObject<T extends PdfDataType> with PdfDiagnostic {
/// This is the object parameters.
final T params;
/// This is the unique serial number for this object.
@override
final int objser;
/// This is the generation number for this object.
@override
final int objgen;
/// This allows any Pdf object to refer to the document being constructed.
... ... @@ -48,6 +50,18 @@ abstract class PdfObject<T extends PdfDataType> with PdfDiagnostic {
var inUse = true;
@override
DeflateCallback? get deflate => pdfDocument.deflate;
@override
PdfEncryptCallback? get encryptCallback => pdfDocument.encryption?.encrypt;
@override
bool get verbose => pdfDocument.verbose;
@override
PdfVersion get version => pdfDocument.version;
/// Writes the object to the output stream.
void write(PdfStream os) {
prepare();
... ... @@ -67,7 +81,7 @@ abstract class PdfObject<T extends PdfDataType> with PdfDiagnostic {
}
void writeContent(PdfStream os) {
params.output(os, pdfDocument.verbose ? 0 : null);
params.output(os, verbose ? 0 : null);
os.putByte(0x0a);
}
... ... @@ -77,9 +91,6 @@ abstract class PdfObject<T extends PdfDataType> with PdfDiagnostic {
os.putString('endobj\n');
}
/// Returns the unique serial number in Pdf format
PdfIndirect ref() => PdfIndirect(objser, objgen);
@override
String toString() => '$runtimeType $params';
}
... ...
... ... @@ -14,9 +14,10 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../stream.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../format/stream.dart';
import 'object.dart';
/// Object with a PdfDict used in the PDF file
... ...
... ... @@ -14,9 +14,9 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../stream.dart';
import '../format/dict_stream.dart';
import '../format/stream.dart';
import 'object_dict.dart';
/// Stream Object
... ...
... ... @@ -15,8 +15,11 @@
*/
import '../color.dart';
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/name.dart';
import '../format/num.dart';
import '../format/string.dart';
import '../rect.dart';
import 'object_dict.dart';
import 'page.dart';
... ... @@ -125,7 +128,7 @@ class PdfOutline extends PdfObjectDict {
params['/Title'] = PdfSecString.fromString(this, title!);
if (color != null) {
params['/C'] = PdfColorType(color!);
params['/C'] = PdfArray.fromColor(color!);
}
if (style != PdfOutlineStyle.normal) {
... ...
... ... @@ -14,8 +14,10 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/indirect.dart';
import '../format/num.dart';
import '../graphics.dart';
import '../page_format.dart';
import 'annotation.dart';
... ...
import '../../priv.dart';
/*
* 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 '../document.dart';
import '../format/array.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../format/num.dart';
import '../format/string.dart';
import 'object.dart';
import 'object_dict.dart';
enum PdfPageLabelStyle {
... ...
... ... @@ -14,8 +14,9 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/num.dart';
import 'object_dict.dart';
import 'page.dart';
... ...
... ... @@ -16,8 +16,9 @@
import 'package:vector_math/vector_math_64.dart';
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/num.dart';
import '../graphic_state.dart';
import 'object_dict.dart';
import 'shading.dart';
... ...
... ... @@ -14,8 +14,11 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/bool.dart';
import '../format/name.dart';
import '../format/num.dart';
import '../point.dart';
import '../rect.dart';
import 'function.dart';
... ...
... ... @@ -16,9 +16,9 @@
import 'dart:typed_data';
import '../data_types.dart';
import '../document.dart';
import '../stream.dart';
import '../format/dict.dart';
import '../format/stream.dart';
import 'object.dart';
import 'object_dict.dart';
import 'object_stream.dart';
... ...
... ... @@ -16,8 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import '../data_types.dart';
import '../document.dart';
import '../format/array.dart';
import '../format/bool.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../graphics.dart';
import '../rect.dart';
import 'function.dart';
... ...
... ... @@ -17,13 +17,17 @@
import 'dart:convert';
import 'dart:typed_data';
import '../data_types.dart';
import '../document.dart';
import '../font/bidi_utils.dart' as bidi;
import '../font/font_metrics.dart';
import '../font/ttf_parser.dart';
import '../font/ttf_writer.dart';
import '../stream.dart';
import '../format/array.dart';
import '../format/dict.dart';
import '../format/name.dart';
import '../format/num.dart';
import '../format/stream.dart';
import '../format/string.dart';
import 'array.dart';
import 'font.dart';
import 'font_descriptor.dart';
... ...
... ... @@ -14,9 +14,9 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../font/font_metrics.dart';
import '../format/name.dart';
import 'font.dart';
import 'ttffont.dart';
... ...
... ... @@ -14,8 +14,8 @@
* limitations under the License.
*/
import '../data_types.dart';
import '../document.dart';
import '../format/name.dart';
import 'object_stream.dart';
class PdfXObject extends PdfObjectStream {
... ...
... ... @@ -14,16 +14,19 @@
* limitations under the License.
*/
import 'data_types.dart';
import 'document.dart';
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';
import 'stream.dart';
import 'xref.dart';
/// PDF document writer
class PdfOutput with PdfDiagnostic {
... ...
... ... @@ -14,8 +14,19 @@
* limitations under the License.
*/
export 'pdf/data_types.dart';
export 'pdf/format/array.dart';
export 'pdf/format/ascii85.dart';
export 'pdf/format/base.dart';
export 'pdf/format/bool.dart';
export 'pdf/format/dict.dart';
export 'pdf/format/dict_stream.dart';
export 'pdf/format/indirect.dart';
export 'pdf/format/name.dart';
export 'pdf/format/null.dart';
export 'pdf/format/num.dart';
export 'pdf/format/object_base.dart' hide DeflateCallback, PdfVersion;
export 'pdf/format/stream.dart';
export 'pdf/format/string.dart';
export 'pdf/format/xref.dart';
export 'pdf/obj/object.dart';
export 'pdf/obj/object_stream.dart';
export 'pdf/stream.dart';
export 'pdf/xref.dart';
... ...
... ... @@ -20,7 +20,6 @@ import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart';
import '../../pdf.dart';
import '../pdf/data_types.dart';
import 'basic.dart';
import 'border_radius.dart';
import 'box_border.dart';
... ...
... ... @@ -16,7 +16,7 @@
import 'dart:typed_data';
import 'package:pdf/src/pdf/data_types.dart';
import 'package:pdf/src/priv.dart';
import 'package:test/test.dart';
void main() {
... ...
... ... @@ -14,22 +14,87 @@
* limitations under the License.
*/
import 'dart:convert';
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/src/priv.dart';
import 'package:test/test.dart';
class BasicObject with PdfObjectBase {
const BasicObject(this.objser);
@override
final int objser;
@override
bool get verbose => true;
void write(PdfStream os, PdfDataType value) {
os.putString('$objser $objgen obj\n');
value.output(os, verbose ? 0 : null);
os.putByte(0x0a);
os.putString('endobj\n');
}
}
void main() {
test('Pdf Minimal', () async {
final pdf = PdfDocument(compress: false);
final page = PdfPage(pdf, pageFormat: PdfPageFormat.a4);
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(
object: const BasicObject(1),
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),
});
final os = PdfStream();
final xref = PdfXrefTable();
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 g = page.getGraphics();
g.drawLine(
30, page.pageFormat.height - 30.0, 200, page.pageFormat.height - 200.0);
g.strokePath();
os.putString('startxref\n$xrefOffset\n%%EOF\n');
final file = File('minimal.pdf');
await file.writeAsBytes(await pdf.save());
await file.writeAsBytes(os.output());
});
}
... ...