David PHAM-VAN

Use PdfDataTypes for internal structures

... ... @@ -4,6 +4,7 @@
- Improve Annotations
- Implement table row vertical alignment
- Improve Internal data structure
## 1.5.0
... ...
... ... @@ -38,6 +38,7 @@ part 'src/catalog.dart';
part 'src/color.dart';
part 'src/colors.dart';
part 'src/compatibility.dart';
part 'src/data_types.dart';
part 'src/document.dart';
part 'src/encryption.dart';
part 'src/exif.dart';
... ...
... ... @@ -86,7 +86,7 @@ class PdfAnnot extends PdfObject {
@override
void _prepare() {
super._prepare();
annot.build(pdfPage, params);
annot.build(pdfPage, this, params);
}
}
... ... @@ -145,44 +145,44 @@ abstract class PdfAnnotBase {
@protected
@mustCallSuper
void build(PdfPage page, Map<String, PdfStream> params) {
params['/Subtype'] = PdfStream.string(subtype);
params['/Rect'] = PdfStream()
..putNumArray(<double>[rect.left, rect.bottom, rect.right, rect.top]);
void build(PdfPage page, PdfObject object, PdfDict params) {
params['/Subtype'] = PdfName(subtype);
params['/Rect'] = PdfArray.fromNum(
<double>[rect.left, rect.bottom, rect.right, rect.top]);
params['/P'] = page.ref();
// handle the border
if (border == null) {
params['/Border'] = PdfStream.string('[0 0 0]');
params['/Border'] = PdfArray.fromNum(const <int>[0, 0, 0]);
} else {
params['/BS'] = border.ref();
}
if (content != null) {
params['/Contents'] = PdfStream()..putLiteral(content);
params['/Contents'] = PdfSecString.fromString(object, content);
}
if (name != null) {
params['/NM'] = PdfStream()..putLiteral(name);
params['/NM'] = PdfSecString.fromString(object, name);
}
if (flags != null) {
params['/F'] = PdfStream.intNum(flagValue);
params['/F'] = PdfNum(flagValue);
}
if (date != null) {
params['/M'] = PdfStream()..putDate(date);
params['/M'] = PdfSecString.fromDate(object, date);
}
if (color != null) {
if (color is PdfColorCmyk) {
final PdfColorCmyk k = color;
params['/C'] = PdfStream()
..putNumList(<double>[k.cyan, k.magenta, k.yellow, k.black]);
params['/C'] =
PdfArray.fromNum(<double>[k.cyan, k.magenta, k.yellow, k.black]);
} else {
params['/C'] = PdfStream()
..putNumList(<double>[color.red, color.green, color.blue]);
params['/C'] =
PdfArray.fromNum(<double>[color.red, color.green, color.blue]);
}
}
}
... ... @@ -231,13 +231,12 @@ class PdfAnnotNamedLink extends PdfAnnotBase {
final String dest;
@override
void build(PdfPage page, Map<String, PdfStream> params) {
super.build(page, params);
params['/A'] = PdfStream()
..putDictionary(
<String, PdfStream>{
'/S': PdfStream()..putString('/GoTo'),
'/D': PdfStream()..putText(dest),
void build(PdfPage page, PdfObject object, PdfDict params) {
super.build(page, object, params);
params['/A'] = PdfDict(
<String, PdfDataType>{
'/S': const PdfName('/GoTo'),
'/D': PdfSecString.fromString(object, dest),
},
);
}
... ... @@ -264,13 +263,12 @@ class PdfAnnotUrlLink extends PdfAnnotBase {
final String url;
@override
void build(PdfPage page, Map<String, PdfStream> params) {
super.build(page, params);
params['/A'] = PdfStream()
..putDictionary(
<String, PdfStream>{
'/S': PdfStream()..putString('/URI'),
'/URI': PdfStream()..putText(url),
void build(PdfPage page, PdfObject object, PdfDict params) {
super.build(page, object, params);
params['/A'] = PdfDict(
<String, PdfDataType>{
'/S': const PdfName('/URI'),
'/URI': PdfSecString.fromString(object, url),
},
);
}
... ... @@ -289,7 +287,6 @@ abstract class PdfAnnotWidget extends PdfAnnotBase {
DateTime date,
PdfColor color,
this.highlighting,
this.value,
}) : super(
subtype: '/Widget',
rect: rect,
... ... @@ -305,20 +302,14 @@ abstract class PdfAnnotWidget extends PdfAnnotBase {
final PdfAnnotHighlighting highlighting;
final PdfStream value;
@override
void build(PdfPage page, Map<String, PdfStream> params) {
super.build(page, params);
void build(PdfPage page, PdfObject object, PdfDict params) {
super.build(page, object, params);
params['/FT'] = PdfStream.string(fieldType);
params['/FT'] = PdfName(fieldType);
if (fieldName != null) {
params['/T'] = PdfStream()..putLiteral(fieldName);
}
if (value != null) {
params['/V'] = value;
params['/T'] = PdfSecString.fromString(object, fieldName);
}
}
}
... ... @@ -344,8 +335,8 @@ class PdfAnnotSign extends PdfAnnotWidget {
);
@override
void build(PdfPage page, Map<String, PdfStream> params) {
super.build(page, params);
void build(PdfPage page, PdfObject object, PdfDict params) {
super.build(page, object, params);
assert(page.pdfDocument.sign != null);
params['/V'] = page.pdfDocument.sign.ref();
}
... ...
... ... @@ -19,17 +19,17 @@ part of pdf;
class PdfArrayObject extends PdfObject {
PdfArrayObject(
PdfDocument pdfDocument,
this.values,
) : assert(values != null),
this.array,
) : assert(array != null),
super(pdfDocument);
final List<String> values;
final PdfArray array;
@override
void _writeContent(PdfStream os) {
super._writeContent(os);
os.putStringArray(values);
array.output(os);
os.putBytes(<int>[0x0a]);
}
}
... ...
... ... @@ -66,18 +66,15 @@ class PdfBorder extends PdfObject {
/// @param os OutputStream to send the object to
@override
void _writeContent(PdfStream os) {
super._writeContent(os);
void _prepare() {
super._prepare();
params['/S'] =
PdfName('/' + 'SDBIU'.substring(style.index, style.index + 1));
params['/W'] = PdfNum(width);
final List<PdfStream> data = <PdfStream>[];
data.add(PdfStream.string('/S'));
data.add(PdfStream.string(
'/' + 'SDBIU'.substring(style.index, style.index + 1)));
data.add(PdfStream.string('/W $width'));
if (dash != null) {
data.add(PdfStream.string('/D'));
data.add(PdfStream.array(dash.map((double d) => PdfStream.num(d))));
params['/D'] = PdfArray.fromNum(dash);
}
os.putArray(data);
}
}
... ...
... ... @@ -52,7 +52,7 @@ class PdfCatalog extends PdfObject {
super._prepare();
/// the PDF specification version, overrides the header version starting from 1.4
params['/Version'] = PdfStream.string('/${pdfDocument.version}');
params['/Version'] = PdfName('/${pdfDocument.version}');
params['/Pages'] = pdfPageList.ref();
... ... @@ -65,11 +65,10 @@ class PdfCatalog extends PdfObject {
params['/Names'] = names.ref();
// the /PageMode setting
params['/PageMode'] =
PdfStream.string(PdfDocument._PdfPageModes[pageMode.index]);
params['/PageMode'] = PdfName(PdfDocument._PdfPageModes[pageMode.index]);
if (pdfDocument.sign != null) {
params['/Perms'] = PdfStream.dictionary(<String, PdfStream>{
params['/Perms'] = PdfDict(<String, PdfDataType>{
'/DocMDP': pdfDocument.sign.ref(),
});
}
... ... @@ -84,9 +83,9 @@ class PdfCatalog extends PdfObject {
}
if (widgets.isNotEmpty) {
params['/AcroForm'] = PdfStream.dictionary(<String, PdfStream>{
'/SigFlags': PdfStream.intNum(pdfDocument.sign?.flagsValue ?? 0),
'/Fields': PdfStream()..putObjectArray(widgets),
params['/AcroForm'] = PdfDict(<String, PdfDataType>{
'/SigFlags': PdfNum(pdfDocument.sign?.flagsValue ?? 0),
'/Fields': PdfArray.fromObjects(widgets),
});
}
}
... ...
... ... @@ -73,7 +73,7 @@ class PDFAnnot extends PdfAnnot {
@deprecated
class PDFArrayObject extends PdfArrayObject {
PDFArrayObject(PdfDocument pdfDocument, List<String> values)
: super(pdfDocument, values);
: super(pdfDocument, null);
}
@deprecated
... ...
/*
* 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.
*/
// ignore_for_file: omit_local_variable_types
// ignore_for_file: avoid_unused_constructor_parameters
part of pdf;
abstract class PdfDataType {
const PdfDataType();
void output(PdfStream s);
PdfStream toStream() {
final PdfStream s = PdfStream();
output(s);
return s;
}
@override
String toString() {
return String.fromCharCodes(toStream().output());
}
List<int> toList() {
return toStream().output();
}
}
class PdfBool extends PdfDataType {
const PdfBool(this.value);
final bool value;
@override
void output(PdfStream s) {
s.putString(value ? 'true' : 'false');
}
}
class PdfNum extends PdfDataType {
const PdfNum(this.value);
final num value;
@override
void output(PdfStream s) {
if (value is int) {
s.putString(value.toInt().toString());
} else {
s.putNum(value);
}
}
}
enum PdfStringFormat { binary, litteral }
class PdfString extends PdfDataType {
const PdfString(this.value, [this.format = PdfStringFormat.litteral]);
factory PdfString.fromString(String value) {
try {
return PdfString(latin1.encode(value), PdfStringFormat.litteral);
} catch (e) {
return PdfString(
Uint8List.fromList(<int>[0xfe, 0xff] + encodeUtf16be(value)),
PdfStringFormat.litteral,
);
}
}
factory PdfString.fromDate(DateTime date) {
final DateTime utcDate = date.toUtc();
final String year = utcDate.year.toString().padLeft(4, '0');
final String month = utcDate.month.toString().padLeft(2, '0');
final String day = utcDate.day.toString().padLeft(2, '0');
final String hour = utcDate.hour.toString().padLeft(2, '0');
final String minute = utcDate.minute.toString().padLeft(2, '0');
final String second = utcDate.second.toString().padLeft(2, '0');
return PdfString.fromString('D:$year$month$day$hour$minute${second}Z');
}
final Uint8List value;
final PdfStringFormat format;
/// Returns the ASCII/Unicode code unit corresponding to the hexadecimal digit
/// [digit].
int _codeUnitForDigit(int digit) =>
digit < 10 ? digit + 0x30 : digit + 0x61 - 10;
@override
void output(PdfStream s) {
switch (format) {
case PdfStringFormat.binary:
s.putByte(0x3c);
for (int byte in value) {
s.putByte(_codeUnitForDigit((byte & 0xF0) >> 4));
s.putByte(_codeUnitForDigit(byte & 0x0F));
}
s.putByte(0x3e);
break;
case PdfStringFormat.litteral:
s.putByte(40);
s.putTextBytes(value);
s.putByte(41);
break;
}
}
}
class PdfSecString extends PdfString {
const PdfSecString(this.object, Uint8List value,
[PdfStringFormat format = PdfStringFormat.binary])
: super(value, format);
factory PdfSecString.fromString(PdfObject object, String value) {
try {
return PdfSecString(
object, latin1.encode(value), PdfStringFormat.litteral);
} catch (e) {
return PdfSecString(
object,
Uint8List.fromList(<int>[0xfe, 0xff] + encodeUtf16be(value)),
PdfStringFormat.litteral,
);
}
}
factory PdfSecString.fromDate(PdfObject object, DateTime date) {
final DateTime utcDate = date.toUtc();
final String year = utcDate.year.toString().padLeft(4, '0');
final String month = utcDate.month.toString().padLeft(2, '0');
final String day = utcDate.day.toString().padLeft(2, '0');
final String hour = utcDate.hour.toString().padLeft(2, '0');
final String minute = utcDate.minute.toString().padLeft(2, '0');
final String second = utcDate.second.toString().padLeft(2, '0');
return PdfSecString.fromString(
object, 'D:$year$month$day$hour$minute${second}Z');
}
final PdfObject object;
@override
void output(PdfStream s) {
if (object.pdfDocument.encryption == null) {
return super.output(s);
}
final List<int> enc = object.pdfDocument.encryption.encrypt(value, object);
switch (format) {
case PdfStringFormat.binary:
s.putByte(0x3c);
for (int byte in enc) {
s.putByte(_codeUnitForDigit((byte & 0xF0) >> 4));
s.putByte(_codeUnitForDigit(byte & 0x0F));
}
s.putByte(0x3e);
break;
case PdfStringFormat.litteral:
s.putByte(40);
s.putTextBytes(enc);
s.putByte(41);
break;
}
}
}
class PdfName extends PdfDataType {
const PdfName(this.value);
final String value;
@override
void output(PdfStream s) {
assert(value[0] == '/');
s.putString(value);
}
}
class PdfNull extends PdfDataType {
const PdfNull();
@override
void output(PdfStream s) {
s.putString('null');
}
}
class PdfIndirect extends PdfDataType {
const PdfIndirect(this.ser, this.gen);
final int ser;
final int gen;
@override
void output(PdfStream s) {
s.putString('$ser $gen R');
}
}
class PdfArray extends PdfDataType {
PdfArray([Iterable<PdfDataType> values]) {
if (values != null) {
this.values.addAll(values);
}
}
factory PdfArray.fromObjects(List<PdfObject> objects) {
return PdfArray(
objects.map<PdfIndirect>((PdfObject e) => e.ref()).toList());
}
factory PdfArray.fromNum(List<num> list) {
return PdfArray(list.map<PdfNum>((num e) => PdfNum(e)).toList());
}
// factory PdfArray.fromStrings(List<String> list) {
// return PdfArray(
// list.map<PdfString>((String e) => PdfString.fromString(e)).toList());
// }
final List<PdfDataType> values = <PdfDataType>[];
void add(PdfDataType v) {
values.add(v);
}
@override
void output(PdfStream s) {
s.putString('[');
if (values.isNotEmpty) {
for (int n = 0; n < values.length - 1; n++) {
final PdfDataType val = values[n];
val.output(s);
s.putString(' ');
}
values.last.output(s);
}
s.putString(']');
}
}
class PdfDict extends PdfDataType {
PdfDict([Map<String, PdfDataType> values]) {
if (values != null) {
this.values.addAll(values);
}
}
factory PdfDict.fromObjectMap(Map<String, PdfObject> objects) {
return PdfDict(
objects.map<String, PdfIndirect>(
(String key, PdfObject value) =>
MapEntry<String, PdfIndirect>(key, value.ref()),
),
);
}
final Map<String, PdfDataType> values = <String, PdfDataType>{};
bool get isNotEmpty => values.isNotEmpty;
operator []=(String k, PdfDataType v) {
values[k] = v;
}
@override
void output(PdfStream s) {
s.putString('<< ');
values.forEach((String k, PdfDataType v) {
s.putString('$k ');
v.output(s);
s.putString('\n');
});
s.putString('>>');
}
bool containsKey(String key) {
return values.containsKey(key);
}
}
... ...
... ... @@ -119,9 +119,9 @@ abstract class PdfFont extends PdfObject {
void _prepare() {
super._prepare();
params['/Subtype'] = PdfStream.string(subtype);
params['/Name'] = PdfStream.string(name);
params['/Encoding'] = PdfStream.string('/WinAnsiEncoding');
params['/Subtype'] = PdfName(subtype);
params['/Name'] = PdfName(name);
params['/Encoding'] = const PdfName('/WinAnsiEncoding');
}
@Deprecated('Use `glyphMetrics` instead')
... ... @@ -166,9 +166,9 @@ See https://github.com/DavBfr/dart_pdf/wiki/Fonts-Management
PdfStream putText(String text) {
try {
return PdfStream()
..putBytes(latin1.encode('('))
..putByte(40)
..putTextBytes(latin1.encode(text))
..putBytes(latin1.encode(')'));
..putByte(41);
} catch (e) {
assert(false, '''\n---------------------------------------------
Can not decode the string to Latin1.
... ...
... ... @@ -32,20 +32,19 @@ class PdfFontDescriptor extends PdfObject {
void _prepare() {
super._prepare();
params['/FontName'] = PdfStream.string('/' + ttfFont.fontName);
params['/FontName'] = PdfName('/' + ttfFont.fontName);
params['/FontFile2'] = file.ref();
params['/Flags'] = PdfStream.intNum(ttfFont.font.unicode ? 4 : 32);
params['/FontBBox'] = PdfStream()
..putIntArray(<int>[
params['/Flags'] = PdfNum(ttfFont.font.unicode ? 4 : 32);
params['/FontBBox'] = PdfArray.fromNum(<int>[
(ttfFont.font.xMin / ttfFont.font.unitsPerEm * 1000).toInt(),
(ttfFont.font.yMin / ttfFont.font.unitsPerEm * 1000).toInt(),
(ttfFont.font.xMax / ttfFont.font.unitsPerEm * 1000).toInt(),
(ttfFont.font.yMax / ttfFont.font.unitsPerEm * 1000).toInt()
]);
params['/Ascent'] = PdfStream.intNum((ttfFont.ascent * 1000).toInt());
params['/Descent'] = PdfStream.intNum((ttfFont.descent * 1000).toInt());
params['/ItalicAngle'] = PdfStream.intNum(0);
params['/CapHeight'] = PdfStream.intNum(10);
params['/StemV'] = PdfStream.intNum(79);
params['/Ascent'] = PdfNum((ttfFont.ascent * 1000).toInt());
params['/Descent'] = PdfNum((ttfFont.descent * 1000).toInt());
params['/ItalicAngle'] = const PdfNum(0);
params['/CapHeight'] = const PdfNum(10);
params['/StemV'] = const PdfNum(79);
}
}
... ...
... ... @@ -20,8 +20,8 @@ part of pdf;
class PdfFormXObject extends PdfXObject {
PdfFormXObject(PdfDocument pdfDocument) : super(pdfDocument, '/Form') {
params['/FormType'] = PdfStream.string('1');
params['/BBox'] = PdfStream.string('[0 0 1000 1000]');
params['/FormType'] = const PdfNum(1);
params['/BBox'] = PdfArray.fromNum(const <int>[0, 0, 1000, 1000]);
}
/// The fonts associated with this page
... ... @@ -34,7 +34,7 @@ class PdfFormXObject extends PdfXObject {
void setMatrix(Matrix4 t) {
final Float64List s = t.storage;
params['/Matrix'] =
PdfStream.string('[${s[0]} ${s[1]} ${s[4]} ${s[5]} ${s[12]} ${s[13]}]');
PdfArray.fromNum(<double>[s[0], s[1], s[4], s[5], s[12], s[13]]);
}
@override
... ... @@ -43,20 +43,20 @@ class PdfFormXObject extends PdfXObject {
// Now the resources
/// This holds any resources for this FormXObject
final Map<String, PdfStream> resources = <String, PdfStream>{};
final PdfDict resources = PdfDict();
// fonts
if (fonts.isNotEmpty) {
resources['/Font'] = PdfStream()..putObjectDictionary(fonts);
resources['/Font'] = PdfDict.fromObjectMap(fonts);
}
// Now the XObjects
if (xobjects.isNotEmpty) {
resources['/XObject'] = PdfStream()..putObjectDictionary(xobjects);
resources['/XObject'] = PdfDict.fromObjectMap(xobjects);
}
if (resources.isNotEmpty) {
params['/Resources'] = PdfStream.dictionary(resources);
params['/Resources'] = resources;
}
}
}
... ...
... ... @@ -27,15 +27,15 @@ class PdfGraphicState {
final double opacity;
@protected
PdfStream _output() {
final Map<String, PdfStream> params = <String, PdfStream>{};
PdfDict _output() {
final PdfDict params = PdfDict();
if (opacity != null) {
params['/CA'] = PdfStream.num(opacity);
params['/ca'] = PdfStream.num(opacity);
params['/CA'] = PdfNum(opacity);
params['/ca'] = PdfNum(opacity);
}
return PdfStream.dictionary(params);
return params;
}
@override
... ...
... ... @@ -78,10 +78,10 @@ class PdfImage extends PdfXObject {
_height = height,
super(pdfDocument, '/Image', isBinary: true) {
_name = '/Image$objser';
params['/Width'] = PdfStream.string(width.toString());
params['/Height'] = PdfStream.string(height.toString());
params['/BitsPerComponent'] = PdfStream.intNum(8);
params['/Name'] = PdfStream.string(_name);
params['/Width'] = PdfNum(width);
params['/Height'] = PdfNum(height);
params['/BitsPerComponent'] = const PdfNum(8);
params['/Name'] = PdfName(_name);
if (alphaChannel == false && alpha) {
final PdfImage _sMask = PdfImage._(
... ... @@ -95,17 +95,17 @@ class PdfImage extends PdfXObject {
jpeg: jpeg,
orientation: orientation,
);
params['/SMask'] = PdfStream.string('${_sMask.objser} 0 R');
params['/SMask'] = PdfIndirect(_sMask.objser, 0);
}
if (isRGB) {
params['/ColorSpace'] = PdfStream.string('/DeviceRGB');
params['/ColorSpace'] = const PdfName('/DeviceRGB');
} else {
params['/ColorSpace'] = PdfStream.string('/DeviceGray');
params['/ColorSpace'] = const PdfName('/DeviceGray');
}
if (jpeg) {
params['/Intent'] = PdfStream.string('/RelativeColorimetric');
params['/Intent'] = const PdfName('/RelativeColorimetric');
}
}
... ... @@ -163,7 +163,7 @@ class PdfImage extends PdfXObject {
void _prepare() {
if (jpeg) {
buf.putBytes(image);
params['/Filter'] = PdfStream.string('/DCTDecode');
params['/Filter'] = const PdfName('/DCTDecode');
super._prepare();
return;
}
... ...
... ... @@ -27,28 +27,28 @@ class PdfInfo extends PdfObject {
this.producer})
: super(pdfDocument, null) {
if (author != null) {
params['/Author'] = PdfStream()..putLiteral(author);
params['/Author'] = PdfSecString.fromString(this, author);
}
if (creator != null) {
params['/Creator'] = PdfStream()..putLiteral(creator);
params['/Creator'] = PdfSecString.fromString(this, creator);
}
if (title != null) {
params['/Title'] = PdfStream()..putLiteral(title);
params['/Title'] = PdfSecString.fromString(this, title);
}
if (subject != null) {
params['/Subject'] = PdfStream()..putLiteral(subject);
params['/Subject'] = PdfSecString.fromString(this, subject);
}
if (keywords != null) {
params['/Keywords'] = PdfStream()..putLiteral(keywords);
params['/Keywords'] = PdfSecString.fromString(this, keywords);
}
if (producer != null) {
params['/Producer'] = PdfStream()
..putLiteral('$producer ($_libraryName)');
params['/Producer'] =
PdfSecString.fromString(this, '$producer ($_libraryName)');
} else {
params['/Producer'] = PdfStream()..putLiteral(_libraryName);
params['/Producer'] = PdfSecString.fromString(this, _libraryName);
}
params['/CreationDate'] = PdfStream()..putDate(DateTime.now());
params['/CreationDate'] = PdfSecString.fromDate(this, DateTime.now());
}
static const String _libraryName = 'https://github.com/DavBfr/dart_pdf';
... ...
... ... @@ -20,7 +20,7 @@ class PdfNames extends PdfObject {
/// This constructs a Pdf Name object
PdfNames(PdfDocument pdfDocument) : super(pdfDocument);
final List<PdfStream> _dests = <PdfStream>[];
final PdfArray _dests = PdfArray();
void addDest(
String name,
... ... @@ -32,16 +32,14 @@ class PdfNames extends PdfObject {
assert(page.pdfDocument == pdfDocument);
assert(name != null);
_dests.add(PdfStream()..putText(name));
_dests.add(PdfStream()
..putDictionary(<String, PdfStream>{
'/D': PdfStream()
..putArray(<PdfStream>[
_dests.add(PdfSecString.fromString(this, name));
_dests.add(PdfDict(<String, PdfDataType>{
'/D': PdfArray(<PdfDataType>[
page.ref(),
PdfStream.string('/XYZ'),
if (posX == null) PdfStream.string('null') else PdfStream.num(posX),
if (posY == null) PdfStream.string('null') else PdfStream.num(posY),
if (posZ == null) PdfStream.string('null') else PdfStream.num(posZ),
const PdfName('/XYZ'),
if (posX == null) const PdfNull() else PdfNum(posX),
if (posY == null) const PdfNull() else PdfNum(posY),
if (posZ == null) const PdfNull() else PdfNum(posZ),
]),
}));
}
... ... @@ -50,9 +48,6 @@ class PdfNames extends PdfObject {
void _prepare() {
super._prepare();
params['/Dests'] = PdfStream()
..putDictionary(<String, PdfStream>{
'/Names': PdfStream()..putArray(_dests),
});
params['/Dests'] = PdfDict(<String, PdfDataType>{'/Names': _dests});
}
}
... ...
... ... @@ -24,14 +24,14 @@ class PdfObject {
: assert(pdfDocument != null),
objser = pdfDocument._genSerial() {
if (type != null) {
params['/Type'] = PdfStream.string(type);
params['/Type'] = PdfName(type);
}
pdfDocument.objects.add(this);
}
/// This is the object parameters.
final Map<String, PdfStream> params = <String, PdfStream>{};
final PdfDict params = PdfDict();
/// This is the unique serial number for this object.
final int objser;
... ... @@ -72,7 +72,7 @@ class PdfObject {
void _writeContent(PdfStream os) {
if (params.isNotEmpty) {
os.putDictionary(params);
params.output(os);
os.putString('\n');
}
}
... ... @@ -89,5 +89,5 @@ class PdfObject {
/// Returns the unique serial number in Pdf format
/// @return the serial number in Pdf format
PdfStream ref() => PdfStream.string('$objser $objgen R');
PdfIndirect ref() => PdfIndirect(objser, objgen);
}
... ...
... ... @@ -46,12 +46,12 @@ class PdfObjectStream extends PdfObject {
_data = buf.output();
} else if (pdfDocument.deflate != null) {
_data = pdfDocument.deflate(buf.output());
params['/Filter'] = PdfStream.string('/FlateDecode');
params['/Filter'] = const PdfName('/FlateDecode');
} else if (isBinary) {
// This is a Ascii85 stream
final Ascii85Encoder e = Ascii85Encoder();
_data = e.convert(buf.output());
params['/Filter'] = PdfStream.string('/ASCII85Decode');
params['/Filter'] = const PdfName('/ASCII85Decode');
} else {
// This is a non-deflated stream
_data = buf.output();
... ... @@ -59,7 +59,7 @@ class PdfObjectStream extends PdfObject {
if (pdfDocument.encryption != null) {
_data = pdfDocument.encryption.encrypt(_data, this);
}
params['/Length'] = PdfStream.intNum(_data.length);
params['/Length'] = PdfNum(_data.length);
}
@override
... ...
... ... @@ -87,24 +87,27 @@ class PdfOutline extends PdfObject {
// These are for kids only
if (parent != null) {
params['/Title'] = PdfStream.string(title);
final List<PdfStream> dests = <PdfStream>[];
params['/Title'] = PdfSecString.fromString(this, title);
final PdfArray dests = PdfArray();
dests.add(dest.ref());
if (destMode == PdfOutlineMode.fitpage) {
dests.add(PdfStream.string('/Fit'));
dests.add(const PdfName('/Fit'));
} else {
dests.add(PdfStream.string(
'/FitR ${rect.left} ${rect.bottom} ${rect.right} ${rect.top}'));
dests.add(const PdfName('/FitR'));
dests.add(PdfNum(rect.left));
dests.add(PdfNum(rect.bottom));
dests.add(PdfNum(rect.right));
dests.add(PdfNum(rect.top));
}
params['/Parent'] = parent.ref();
params['/Dest'] = PdfStream.array(dests);
params['/Dest'] = dests;
// were a descendent, so by default we are closed. Find out how many
// entries are below us
final int c = descendants();
if (c > 0) {
params['/Count'] = PdfStream.intNum(-c);
params['/Count'] = PdfNum(-c);
}
final int index = parent.getIndex(this);
... ... @@ -120,7 +123,7 @@ class PdfOutline extends PdfObject {
} else {
// the number of outlines in this document
// were the top level node, so all are open by default
params['/Count'] = PdfStream.intNum(outlines.length);
params['/Count'] = PdfNum(outlines.length);
}
// These only valid if we have children
... ...
... ... @@ -113,16 +113,17 @@ class PdfOutput {
// now the trailer object
os.putString('trailer\n');
final Map<String, PdfStream> params = <String, PdfStream>{};
final PdfDict params = PdfDict();
// the number of entries (REQUIRED)
params['/Size'] = PdfStream.intNum(offsets.length + 1);
params['/Size'] = PdfNum(offsets.length + 1);
// the /Root catalog indirect reference (REQUIRED)
if (rootID != null) {
params['/Root'] = rootID.ref();
final PdfStream id = PdfStream.binary(rootID.pdfDocument.documentID);
params['/ID'] = PdfStream.array(<PdfStream>[id, id]);
final PdfString id =
PdfString(rootID.pdfDocument.documentID, PdfStringFormat.binary);
params['/ID'] = PdfArray(<PdfDataType>[id, id]);
} else {
throw Exception('Root object is not present in document');
}
... ... @@ -138,7 +139,7 @@ class PdfOutput {
}
// end the trailer object
os.putDictionary(params);
params.output(os);
os.putString('\nstartxref\n$xref\n%%EOF\n');
if (signatureID != null) {
... ...
... ... @@ -94,8 +94,8 @@ class PdfPage extends PdfObject {
params['/Parent'] = pdfDocument.pdfPageList.ref();
// the /MediaBox for the page size
params['/MediaBox'] = PdfStream()
..putNumArray(<double>[0, 0, pageFormat.width, pageFormat.height]);
params['/MediaBox'] =
PdfArray.fromNum(<double>[0, 0, pageFormat.width, pageFormat.height]);
// Rotation (if not zero)
// if(rotate!=0) {
... ... @@ -109,39 +109,38 @@ class PdfPage extends PdfObject {
if (contents.length == 1) {
params['/Contents'] = contents.first.ref();
} else {
params['/Contents'] = PdfStream()..putObjectArray(contents);
params['/Contents'] = PdfArray.fromObjects(contents);
}
}
// Now the resources
/// This holds any resources for this page
final Map<String, PdfStream> resources = <String, PdfStream>{};
final PdfDict resources = PdfDict();
// fonts
if (fonts.isNotEmpty) {
resources['/Font'] = PdfStream()..putObjectDictionary(fonts);
resources['/Font'] = PdfDict.fromObjectMap(fonts);
}
// Now the XObjects
if (xObjects.isNotEmpty) {
resources['/XObject'] = PdfStream()..putObjectDictionary(xObjects);
resources['/XObject'] = PdfDict.fromObjectMap(xObjects);
}
if (pdfDocument.hasGraphicStates) {
// Declare Transparency Group settings
params['/Group'] = PdfStream()
..putDictionary(<String, PdfStream>{
'/Type': PdfStream.string('/Group'),
'/S': PdfStream.string('/Transparency'),
'/CS': PdfStream.string('/DeviceRGB'),
'/I': PdfStream()..putBool(isolatedTransparency),
'/K': PdfStream()..putBool(knockoutTransparency),
params['/Group'] = PdfDict(<String, PdfDataType>{
'/Type': const PdfName('/Group'),
'/S': const PdfName('/Transparency'),
'/CS': const PdfName('/DeviceRGB'),
'/I': PdfBool(isolatedTransparency),
'/K': PdfBool(knockoutTransparency),
});
resources['/ExtGState'] = pdfDocument.graphicStates.ref();
}
params['/Resources'] = PdfStream.dictionary(resources);
params['/Resources'] = resources;
// The thumbnail
if (thumbnail != null) {
... ... @@ -150,7 +149,7 @@ class PdfPage extends PdfObject {
// The /Annots object
if (annotations.isNotEmpty) {
params['/Annots'] = PdfStream()..putObjectArray(annotations);
params['/Annots'] = PdfArray.fromObjects(annotations);
}
}
}
... ...
... ... @@ -32,7 +32,7 @@ class PdfPageList extends PdfObject {
void _prepare() {
super._prepare();
params['/Kids'] = PdfStream()..putObjectArray(pages);
params['/Count'] = PdfStream.intNum(pages.length);
params['/Kids'] = PdfArray.fromObjects(pages);
params['/Count'] = PdfNum(pages.length);
}
}
... ...
... ... @@ -42,7 +42,7 @@ class PdfSignature extends PdfObject {
@override
void _write(PdfStream os) {
crypto.preSign(params);
crypto.preSign(this, params);
_offsetStart = os.offset + '$objser $objgen obj\n'.length;
super._write(os);
... ... @@ -53,13 +53,13 @@ class PdfSignature extends PdfObject {
assert(_offsetStart != null && _offsetEnd != null,
'Must reserve the object space before signing the document');
crypto.sign(os, params, _offsetStart, _offsetEnd);
crypto.sign(this, os, params, _offsetStart, _offsetEnd);
}
}
abstract class PdfSignatureBase {
void preSign(Map<String, PdfStream> params);
void preSign(PdfObject object, PdfDict params);
void sign(PdfStream os, Map<String, PdfStream> params, int offsetStart,
void sign(PdfObject object, PdfStream os, PdfDict params, int offsetStart,
int offsetEnd);
}
... ...
... ... @@ -36,13 +36,8 @@ class PdfStream {
}
}
static PdfStream string(String s) => PdfStream()..putString(s);
void putStringUtf16(String s) {
for (int codeUnit in s.codeUnits) {
_stream.add(codeUnit & 0xff);
_stream.add((codeUnit >> 8) & 0xff);
}
void putByte(int s) {
_stream.add(s);
}
void putBytes(List<int> s) {
... ... @@ -61,13 +56,6 @@ class PdfStream {
}).join(' '));
}
void putIntList(List<int> d) {
putString(d.map((int v) => v.toString()).join(' '));
}
static PdfStream num(double d) => PdfStream()..putNum(d);
static PdfStream intNum(int i) => PdfStream()..putString(i.toString());
/// Escape special characters
/// \ddd Character code ddd (octal)
void putTextBytes(List<int> s) {
... ... @@ -111,105 +99,6 @@ class PdfStream {
}
}
void putText(String s) {
putBytes(latin1.encode('('));
putTextBytes(latin1.encode(s));
putBytes(latin1.encode(')'));
}
void putLiteral(String s) {
putBytes(latin1.encode('('));
putBytes(<int>[0xfe, 0xff]);
putTextBytes(encodeUtf16be(s));
putBytes(latin1.encode(')'));
}
void putBool(bool value) {
putString(value ? 'true' : 'false');
}
/// Returns the ASCII/Unicode code unit corresponding to the hexadecimal digit
/// [digit].
int _codeUnitForDigit(int digit) =>
digit < 10 ? digit + 0x30 : digit + 0x61 - 10;
void putBinary(List<int> s) {
_stream.add(0x3c);
for (int byte in s) {
_stream.add(_codeUnitForDigit((byte & 0xF0) >> 4));
_stream.add(_codeUnitForDigit(byte & 0x0F));
}
_stream.add(0x3e);
}
static PdfStream binary(List<int> s) => PdfStream()..putBinary(s);
void putArray(List<PdfStream> values) {
putString('[');
for (PdfStream val in values) {
putStream(val);
putString(' ');
}
putString(']');
}
void putObjectArray(List<PdfObject> values) {
putString('[');
for (PdfObject val in values) {
putStream(val.ref());
putString(' ');
}
putString(']');
}
void putStringArray(List<String> values) {
putString('[' + values.join(' ') + ']');
}
void putDate(DateTime date) {
final DateTime utcDate = date.toUtc();
final String year = utcDate.year.toString().padLeft(4, '0');
final String month = utcDate.month.toString().padLeft(2, '0');
final String day = utcDate.day.toString().padLeft(2, '0');
final String hour = utcDate.hour.toString().padLeft(2, '0');
final String minute = utcDate.minute.toString().padLeft(2, '0');
final String second = utcDate.second.toString().padLeft(2, '0');
putText('D:$year$month$day$hour$minute${second}Z');
}
void putNumArray(List<double> values) {
putString('[');
putNumList(values);
putString(']');
}
void putIntArray(List<int> values) {
putString('[');
putIntList(values);
putString(']');
}
static PdfStream array(List<PdfStream> values) =>
PdfStream()..putArray(values);
void putDictionary(Map<String, PdfStream> values) {
putString('<< ');
values.forEach((String k, PdfStream v) {
putString('$k ');
putStream(v);
putString('\n');
});
putString('>>');
}
static PdfStream dictionary(Map<String, PdfStream> values) =>
PdfStream()..putDictionary(values);
void putObjectDictionary(Map<String, PdfObject> values) {
putDictionary(values.map((String string, PdfObject object) =>
MapEntry<String, PdfStream>(string, object.ref())));
}
int get offset => _stream.length;
List<int> output() => _stream;
... ...
... ... @@ -26,7 +26,7 @@ class PdfTtfFont extends PdfFont {
file = PdfObjectStream(pdfDocument, isBinary: true);
unicodeCMap = PdfUnicodeCmap(pdfDocument, protect);
descriptor = PdfFontDescriptor(this, file);
widthsObject = PdfArrayObject(pdfDocument, <String>[]);
widthsObject = PdfArrayObject(pdfDocument, PdfArray());
}
@override
... ... @@ -62,67 +62,64 @@ class PdfTtfFont extends PdfFont {
return font.glyphInfoMap[g] ?? PdfFontMetrics.zero;
}
void _buildTrueType(Map<String, PdfStream> params) {
void _buildTrueType(PdfDict params) {
int charMin;
int charMax;
file.buf.putBytes(font.bytes.buffer.asUint8List());
file.params['/Length1'] = PdfStream.intNum(font.bytes.lengthInBytes);
file.params['/Length1'] = PdfNum(font.bytes.lengthInBytes);
params['/BaseFont'] = PdfStream.string('/' + fontName);
params['/BaseFont'] = PdfName('/' + fontName);
params['/FontDescriptor'] = descriptor.ref();
charMin = 32;
charMax = 255;
for (int i = charMin; i <= charMax; i++) {
widthsObject.values
.add((glyphMetrics(i).advanceWidth * 1000.0).toInt().toString());
widthsObject.array
.add(PdfNum((glyphMetrics(i).advanceWidth * 1000.0).toInt()));
}
params['/FirstChar'] = PdfStream.intNum(charMin);
params['/LastChar'] = PdfStream.intNum(charMax);
params['/FirstChar'] = PdfNum(charMin);
params['/LastChar'] = PdfNum(charMax);
params['/Widths'] = widthsObject.ref();
}
void _buildType0(Map<String, PdfStream> params) {
void _buildType0(PdfDict params) {
int charMin;
int charMax;
final TtfWriter ttfWriter = TtfWriter(font);
final Uint8List data = ttfWriter.withChars(unicodeCMap.cmap);
file.buf.putBytes(data);
file.params['/Length1'] = PdfStream.intNum(data.length);
file.params['/Length1'] = PdfNum(data.length);
final PdfStream descendantFont = PdfStream.dictionary(<String, PdfStream>{
'/Type': PdfStream.string('/Font'),
'/BaseFont': PdfStream.string('/' + fontName),
final PdfDict descendantFont = PdfDict(<String, PdfDataType>{
'/Type': const PdfName('/Font'),
'/BaseFont': PdfName('/' + fontName),
'/FontFile2': file.ref(),
'/FontDescriptor': descriptor.ref(),
'/W': PdfStream.array(<PdfStream>[
PdfStream.intNum(0),
'/W': PdfArray(<PdfDataType>[
const PdfNum(0),
widthsObject.ref(),
]),
'/CIDToGIDMap': PdfStream.string('/Identity'),
'/DW': PdfStream.string('1000'),
'/Subtype': PdfStream.string('/CIDFontType2'),
'/CIDSystemInfo': PdfStream.dictionary(<String, PdfStream>{
'/Supplement': PdfStream.intNum(0),
'/Registry': PdfStream()..putText('Adobe'),
'/Ordering': PdfStream()..putText('Identity-H'),
'/CIDToGIDMap': const PdfName('/Identity'),
'/DW': const PdfNum(1000),
'/Subtype': const PdfName('/CIDFontType2'),
'/CIDSystemInfo': PdfDict(<String, PdfDataType>{
'/Supplement': const PdfNum(0),
'/Registry': PdfSecString.fromString(this, 'Adobe'),
'/Ordering': PdfSecString.fromString(this, 'Identity-H'),
})
});
params['/BaseFont'] = PdfStream.string('/' + fontName);
params['/Encoding'] = PdfStream.string('/Identity-H');
params['/DescendantFonts'] = PdfStream()
..putArray(<PdfStream>[descendantFont]);
params['/BaseFont'] = PdfName('/' + fontName);
params['/Encoding'] = const PdfName('/Identity-H');
params['/DescendantFonts'] = PdfArray(<PdfDataType>[descendantFont]);
params['/ToUnicode'] = unicodeCMap.ref();
charMin = 0;
charMax = unicodeCMap.cmap.length - 1;
for (int i = charMin; i <= charMax; i++) {
widthsObject.values.add(
(glyphMetrics(unicodeCMap.cmap[i]).advanceWidth * 1000.0)
.toInt()
.toString());
widthsObject.array.add(PdfNum(
(glyphMetrics(unicodeCMap.cmap[i]).advanceWidth * 1000.0).toInt()));
}
}
... ... @@ -145,7 +142,7 @@ class PdfTtfFont extends PdfFont {
final Runes runes = text.runes;
final PdfStream stream = PdfStream();
stream.putBytes(latin1.encode('<'));
stream.putByte(0x3c);
for (int rune in runes) {
int char = unicodeCMap.cmap.indexOf(rune);
if (char == -1) {
... ... @@ -155,7 +152,7 @@ class PdfTtfFont extends PdfFont {
stream.putBytes(latin1.encode(char.toRadixString(16).padLeft(4, '0')));
}
stream.putBytes(latin1.encode('>'));
stream.putByte(0x3e);
return stream;
}
... ...
... ... @@ -44,7 +44,7 @@ class PdfType1Font extends PdfFont {
void _prepare() {
super._prepare();
params['/BaseFont'] = PdfStream.string('/' + fontName);
params['/BaseFont'] = PdfName('/' + fontName);
}
@override
... ...
... ... @@ -19,6 +19,6 @@ part of pdf;
class PdfXObject extends PdfObjectStream {
PdfXObject(PdfDocument pdfDocument, String subtype, {bool isBinary = false})
: super(pdfDocument, type: '/XObject', isBinary: isBinary) {
params['/Subtype'] = PdfStream.string(subtype);
params['/Subtype'] = PdfName(subtype);
}
}
... ...
... ... @@ -20,6 +20,7 @@ import '../example/main.dart' as example;
import 'annotations_test.dart' as annotations;
import 'colors_test.dart' as colors;
import 'complex_test.dart' as complex;
import 'data_types_test.dart' as data_types;
import 'isolate_test.dart' as isolate;
import 'jpeg_test.dart' as jpeg;
import 'metrics_test.dart' as metrics;
... ... @@ -47,6 +48,7 @@ void main() {
annotations.main();
colors.main();
complex.main();
data_types.main();
example.main();
isolate.main();
jpeg.main();
... ...
/*
* 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.
*/
// ignore_for_file: omit_local_variable_types
import 'dart:typed_data';
import 'package:pdf/pdf.dart';
import 'package:test/test.dart';
void main() {
test('PdfDataTypes Bool ', () {
expect(const PdfBool(true).toString(), 'true');
expect(const PdfBool(false).toString(), 'false');
});
test('PdfDataTypes Num', () {
expect(const PdfNum(0).toString(), '0');
expect(const PdfNum(.5).toString(), '0.50000');
expect(const PdfNum(50).toString(), '50');
expect(const PdfNum(50.1).toString(), '50.10000');
});
test('PdfDataTypes String', () {
expect(PdfString.fromString('test').toString(), '(test)');
expect(PdfString.fromString('Zoé').toString(), '(Zoé)');
expect(PdfString.fromString('\r\n\t\b\f)()(\\').toString(),
r'(\r\n\t\b\f\)\(\)\(\\)');
expect(
PdfString.fromString('你好').toList(),
<int>[40, 254, 255, 79, 96, 89, 125, 41],
);
expect(
PdfString.fromDate(DateTime.fromMillisecondsSinceEpoch(1583606302000))
.toString(),
'(D:20200307183822Z)',
);
expect(
PdfString(
Uint8List.fromList(const <int>[0, 1, 2, 3, 4, 5, 6]),
PdfStringFormat.binary,
).toString(),
'<00010203040506>',
);
});
test('PdfDataTypes Name', () {
expect(const PdfName('/Hello').toString(), '/Hello');
});
test('PdfDataTypes Null', () {
expect(const PdfNull().toString(), 'null');
});
test('PdfDataTypes Indirect', () {
expect(const PdfIndirect(30, 4).toString(), '30 4 R');
});
test('PdfDataTypes Array', () {
expect(PdfArray(<PdfDataType>[]).toString(), '[]');
expect(
PdfArray(<PdfDataType>[const PdfNum(1), const PdfNum(2)]).toString(),
'[1 2]',
);
});
test('PdfDataTypes Dict', () {
expect(PdfDict(<String, PdfDataType>{}).toString(), '<< >>');
expect(
PdfDict(<String, PdfDataType>{
'/Name': const PdfName('/Value'),
'/Bool': const PdfBool(true),
'/Num': const PdfNum(42),
'/String': PdfString.fromString('hello'),
'/Null': const PdfNull(),
'/Indirect': const PdfIndirect(55, 0),
'/Array': PdfArray(<PdfDataType>[]),
'/Dict': PdfDict(<String, PdfDataType>{}),
}).toString(),
'<< /Name /Value\n/Bool true\n/Num 42\n/String (hello)\n/Null null\n/Indirect 55 0 R\n/Array []\n/Dict << >>\n>>',
);
});
}
... ...