David PHAM-VAN

Implement PdfPageLabels

... ... @@ -4,6 +4,7 @@
- Update xml dependency range
- Implement PointDataSet for Chart
- Implement PdfPageLabels
## 3.7.4
... ...
... ... @@ -33,6 +33,7 @@ export 'src/pdf/obj/info.dart';
export 'src/pdf/obj/metadata.dart';
export 'src/pdf/obj/outline.dart';
export 'src/pdf/obj/page.dart';
export 'src/pdf/obj/page_label.dart';
export 'src/pdf/obj/pattern.dart';
export 'src/pdf/obj/shading.dart';
export 'src/pdf/obj/signature.dart';
... ...
... ... @@ -30,6 +30,7 @@ import 'obj/names.dart';
import 'obj/object.dart';
import 'obj/outline.dart';
import 'obj/page.dart';
import 'obj/page_label.dart';
import 'obj/page_list.dart';
import 'obj/signature.dart';
import 'output.dart';
... ... @@ -83,10 +84,8 @@ class PdfDocument {
}) : deflate = compress ? (deflate ?? defaultDeflate) : null,
prev = null,
_objser = 1 {
// Now create some standard objects
pdfPageList = PdfPageList(this);
pdfNames = PdfNames(this);
catalog = PdfCatalog(this, pdfPageList, pageMode, pdfNames);
// create the catalog
catalog = PdfCatalog(this, PdfPageList(this), pageMode);
}
PdfDocument.load(
... ... @@ -99,9 +98,7 @@ class PdfDocument {
_objser = prev!.size,
version = prev.version {
// Now create some standard objects
pdfPageList = PdfPageList(this);
pdfNames = PdfNames(this);
catalog = PdfCatalog(this, pdfPageList, pageMode, pdfNames);
catalog = PdfCatalog(this, PdfPageList(this), pageMode);
// Import the existing document
prev!.mergeDocument(this);
... ... @@ -118,7 +115,7 @@ class PdfDocument {
final Set<PdfObject> objects = <PdfObject>{};
/// This is the Catalog object, which is required by each Pdf Document
late PdfCatalog catalog;
late final PdfCatalog catalog;
/// PDF version to generate
final PdfVersion version;
... ... @@ -129,13 +126,13 @@ class PdfDocument {
PdfInfo? info;
/// This is the Pages object, which is required by each Pdf Document
late PdfPageList pdfPageList;
/// The name dictionary
late PdfNames pdfNames;
PdfPageList get pdfPageList => catalog.pdfPageList;
/// This is the Outline object, which is optional
PdfOutline? _outline;
/// The anchor names dictionary
PdfNames get pdfNames {
catalog.names ??= PdfNames(this);
return catalog.names!;
}
/// This holds a [PdfObject] describing the default border for annotations.
/// It's only used when the document is being written.
... ... @@ -191,11 +188,14 @@ class PdfDocument {
/// The root outline
PdfOutline get outline {
if (_outline == null) {
_outline = PdfOutline(this);
catalog.outlines = _outline;
}
return _outline!;
catalog.outlines ??= PdfOutline(this);
return catalog.outlines!;
}
/// The root page labels
PdfPageLabels get pageLabels {
catalog.pageLabels ??= PdfPageLabels(this);
return catalog.pageLabels!;
}
/// Graphic states for opacity and transfer modes
... ...
... ... @@ -21,6 +21,7 @@ import 'metadata.dart';
import 'names.dart';
import 'object_dict.dart';
import 'outline.dart';
import 'page_label.dart';
import 'page_list.dart';
/// Pdf Catalog object
... ... @@ -30,7 +31,6 @@ class PdfCatalog extends PdfObjectDict {
PdfDocument pdfDocument,
this.pdfPageList,
this.pageMode,
this.names,
) : super(pdfDocument, type: '/Catalog');
/// The pages of the document
... ... @@ -45,8 +45,11 @@ class PdfCatalog extends PdfObjectDict {
/// The initial page mode
final PdfPageMode pageMode;
/// The initial page mode
final PdfNames names;
/// The anchor names
PdfNames? names;
/// The page labels of the document
PdfPageLabels? pageLabels;
/// These map the page modes just defined to the pagemodes setting of Pdf.
static const List<String> _pdfPageModes = <String>[
... ... @@ -75,7 +78,14 @@ class PdfCatalog extends PdfObjectDict {
}
// the Names object
params['/Names'] = names.ref();
if (names != null) {
params['/Names'] = names!.ref();
}
// the PageLabels object
if (pageLabels != null && pageLabels!.labels.isNotEmpty) {
params['/PageLabels'] = pageLabels!.ref();
}
// the /PageMode setting
params['/PageMode'] = PdfName(_pdfPageModes[pageMode.index]);
... ...
... ... @@ -58,7 +58,7 @@ class PdfOutline extends PdfObjectDict {
this.color,
this.destMode = PdfOutlineMode.fitPage,
this.style = PdfOutlineStyle.normal,
int? page,
String? page,
}) : assert(anchor == null || (dest == null && rect == null)),
_page = page,
super(pdfDocument);
... ... @@ -76,10 +76,12 @@ class PdfOutline extends PdfObjectDict {
PdfPage? dest;
/// Page number
int? get page =>
String? get page =>
_page ??
(dest != null ? pdfDocument.pdfPageList.pages.indexOf(dest!) + 1 : null);
final int? _page;
(dest != null
? (pdfDocument.pdfPageList.pages.indexOf(dest!) + 1).toString()
: null);
final String? _page;
/// The region on the destination page
final PdfRect? rect;
... ...
import '../../priv.dart';
import '../document.dart';
import 'object_dict.dart';
enum PdfPageLabelStyle {
arabic,
romanUpper,
romanLower,
lettersUpper,
lettersLower
}
class PdfPageLabel {
PdfPageLabel(this.prefix, {this.style, this.subsequent});
PdfPageLabel.arabic({this.prefix, this.subsequent})
: style = PdfPageLabelStyle.arabic;
PdfPageLabel.romanUpper({this.prefix, this.subsequent})
: style = PdfPageLabelStyle.romanUpper;
PdfPageLabel.romanLower({this.prefix, this.subsequent})
: style = PdfPageLabelStyle.romanLower;
PdfPageLabel.lettersUpper({this.prefix, this.subsequent})
: style = PdfPageLabelStyle.lettersUpper;
PdfPageLabel.lettersLower({this.prefix, this.subsequent})
: style = PdfPageLabelStyle.lettersLower;
final PdfPageLabelStyle? style;
final String? prefix;
final int? subsequent;
PdfDict toDict(PdfObject obj) {
final PdfName? s;
switch (style) {
case PdfPageLabelStyle.arabic:
s = const PdfName('/D');
break;
case PdfPageLabelStyle.romanUpper:
s = const PdfName('/R');
break;
case PdfPageLabelStyle.romanLower:
s = const PdfName('/r');
break;
case PdfPageLabelStyle.lettersUpper:
s = const PdfName('/A');
break;
case PdfPageLabelStyle.lettersLower:
s = const PdfName('/a');
break;
case null:
s = null;
}
return PdfDict({
if (s != null) '/S': s,
if (prefix != null && prefix!.isNotEmpty)
'/P': PdfSecString.fromString(obj, prefix!),
if (subsequent != null) '/St': PdfNum(subsequent!)
});
}
String _toRoman(int decimal) {
const dictionary = {
1000: 'M',
900: 'CM',
500: 'D',
400: 'CD,',
100: 'C',
90: 'XC',
50: 'L',
40: 'XL',
10: 'X',
9: 'IX',
5: 'V',
4: 'IV',
1: 'I'
};
assert(decimal > 0 && decimal < 3999,
'Roman numerals are limited to the inclusive range of 1 to 3999.');
var result = '';
dictionary.forEach((k, v) {
while (decimal >= k) {
decimal -= k;
result += v;
}
});
return result;
}
String _toLetters(int decimal) {
final n = String.fromCharCode(0x41 + decimal % 26);
final r = decimal ~/ 26 + 1;
return n * r;
}
String asString([int index = 0]) {
final i = subsequent == null ? index : index + subsequent!;
final String suffix;
switch (style) {
case PdfPageLabelStyle.arabic:
suffix = (i + 1).toString();
break;
case PdfPageLabelStyle.romanUpper:
suffix = _toRoman(i + 1);
break;
case PdfPageLabelStyle.romanLower:
suffix = _toRoman(i + 1).toLowerCase();
break;
case PdfPageLabelStyle.lettersUpper:
suffix = _toLetters(i);
break;
case PdfPageLabelStyle.lettersLower:
suffix = _toLetters(i).toLowerCase();
break;
case null:
suffix = '';
}
return '${prefix ?? ''}$suffix';
}
}
/// Pdf PageLabels object
class PdfPageLabels extends PdfObjectDict {
/// Constructs a Pdf PageLabels object.
PdfPageLabels(PdfDocument pdfDocument) : super(pdfDocument);
final labels = <int, PdfPageLabel>{};
String pageLabel(int index) {
final n = labels.keys.toList()..sort();
var current = PdfPageLabel.arabic();
var s = 0;
for (final i in n) {
if (index >= i) {
current = labels[i]!;
s = i;
}
}
return current.asString(index - s);
}
Iterable<String> get names sync* {
final n = labels.keys.toList()..sort();
var l = PdfPageLabel.arabic();
final len = pdfDocument.pdfPageList.pages.length;
var c = 0;
var b = c < n.length ? n[c] : len;
var s = 0;
for (var i = 0; i < len; i++) {
if (i >= b) {
l = labels[b]!;
c++;
b = c < n.length ? n[c] : len;
s = i;
}
yield l.asString(i - s);
}
}
@override
void prepare() {
super.prepare();
final nums = PdfArray();
for (final entry in labels.entries) {
nums.add(PdfNum(entry.key));
nums.add(entry.value.toDict(this));
}
params['/Nums'] = nums;
}
}
... ...
... ... @@ -622,7 +622,7 @@ class Outline extends Anchor {
anchor: name,
color: color,
style: style,
page: context.pageNumber,
page: context.pageLabel,
)..effectiveLevel = level;
final root = context.document.outline;
... ...
... ... @@ -62,7 +62,16 @@ class Context {
final PdfDocument document;
int get pageNumber => document.pdfPageList.pages.indexOf(page) + 1;
int get _pageNumber => document.pdfPageList.pages.indexOf(page);
int get pageNumber => _pageNumber + 1;
String get pageLabel => document.catalog.pageLabels == null
? pageNumber.toString()
: document.pageLabels.pageLabel(_pageNumber);
set pageLabel(String value) =>
document.pageLabels.labels[_pageNumber] = PdfPageLabel(value);
/// Number of pages in the document.
/// This value is not available in a MultiPage body and will be equal to pageNumber.
... ...
/*
* 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 'package:pdf/pdf.dart';
import 'package:test/test.dart';
void main() {
test('Page label', () async {
final pdf = PdfDocument();
for (var i = 0; i < 5; i++) {
PdfPage(pdf);
}
pdf.pageLabels.labels[0] = PdfPageLabel('Hello');
pdf.pageLabels.labels[1] = PdfPageLabel.lettersUpper();
pdf.pageLabels.labels[3] = PdfPageLabel.romanLower(prefix: 'appendix ');
expect(pdf.pageLabels.pageLabel(0), 'Hello');
expect(pdf.pageLabels.pageLabel(1), 'A');
expect(pdf.pageLabels.pageLabel(2), 'B');
expect(pdf.pageLabels.pageLabel(3), 'appendix i');
expect(pdf.pageLabels.pageLabel(4), 'appendix ii');
});
test('Roman labels', () async {
final pdf = PdfDocument();
for (var i = 0; i < 500; i++) {
PdfPage(pdf);
}
pdf.pageLabels.labels[3] = PdfPageLabel.romanLower();
expect(pdf.pageLabels.pageLabel(0), '1');
expect(pdf.pageLabels.pageLabel(300), 'ccxcviii');
expect(pdf.pageLabels.names.toList()[300], 'ccxcviii');
});
test('No of labels', () async {
final pdf = PdfDocument();
for (var i = 0; i < 500; i++) {
PdfPage(pdf);
}
expect(pdf.pageLabels.pageLabel(0), '1');
expect(pdf.pageLabels.pageLabel(300), '301');
expect(pdf.pageLabels.names.toList()[300], '301');
});
test('Letter labels', () async {
final pdf = PdfDocument();
for (var i = 0; i < 500; i++) {
PdfPage(pdf);
}
pdf.pageLabels.labels[30] = PdfPageLabel.lettersLower();
expect(pdf.pageLabels.pageLabel(0), '1');
expect(pdf.pageLabels.pageLabel(30), 'a');
expect(pdf.pageLabels.names.toList()[30], 'a');
expect(pdf.pageLabels.pageLabel(40), 'k');
expect(pdf.pageLabels.names.toList()[40], 'k');
expect(pdf.pageLabels.pageLabel(60), 'ee');
expect(pdf.pageLabels.names.toList()[60], 'ee');
});
}
... ...