3
Copyright (c) 2006-2007, BBR Inc. All rights reserved.
5
Permission is hereby granted, free of charge, to any person obtaining
6
a copy of this software and associated documentation files (the
7
"Software"), to deal in the Software without restriction, including
8
without limitation the rights to use, copy, modify, merge, publish,
9
distribute, sublicense, and/or sell copies of the Software, and to
10
permit persons to whom the Software is furnished to do so, subject to
11
the following conditions:
13
The above copyright notice and this permission notice shall be included
14
in all copies or substantial portions of the Software.
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
#include "P2PPageTree.h"
34
#include "P2POutput.h"
40
#define NUP_PAGE_MARGIN 4
44
P2PPage::P2PPage(Page *orgPageA, XRef *xrefA)
49
orgPages = new OrgPage [1];
50
orgPages[0].page = orgPageA;
54
mediaBox = *orgPageA->getMediaBox();
55
cropBox = *orgPageA->getCropBox();
56
haveCropBox = orgPageA->isCropped();
57
bleedBox = *orgPageA->getBleedBox();
58
trimBox = *orgPageA->getTrimBox();
59
artBox = *orgPageA->getArtBox();
62
if ((rotateTag = orgPageA->getRotate()) != 0) {
63
/* Note: rotate tag of PDF is clockwise and
64
* orientation-requested attrobute of IPP is anti-clockwise
87
/* when 0, use original resource dictionary of orgPages[0] */
91
/* Constructor for a empty page */
92
P2PPage::P2PPage(PDFRectangle *mediaBoxA, XRef *xrefA)
95
orgPages = new OrgPage [1];
100
if (mediaBoxA != 0) {
101
mediaBox = *mediaBoxA;
103
mediaBox.x2 = mediaBox.y2 = 1;
105
haveCropBox = gFalse;
110
/* when 0, use original resource dictionary of orgPages[0] */
114
void P2PPage::nupCalcLayout(int n, unsigned int layout, PDFRectangle *box,
117
double aw = box->x2 - box->x1;
118
double ah = box->y2 - box->y1;
121
double advx, advy, sx, sy, x, y;
134
/* should be h >= w here */
162
w = (box->x2 - box->x1)/nx;
163
h = (box->y2 - box->y1)/ny;
164
if ((layout & PDFTOPDF_LAYOUT_NEGATEX) != 0) {
171
if ((layout & PDFTOPDF_LAYOUT_NEGATEY) != 0) {
178
if ((layout & PDFTOPDF_LAYOUT_VERTICAL) != 0) {
180
for (x = sx, ix = 0;ix < nx;ix++) {
181
for (y = sy, iy = 0;iy < ny;iy++) {
182
rects[i].x1 = x+NUP_MARGIN;
183
rects[i].x2 = x+w-NUP_MARGIN;
184
rects[i].y1 = y+NUP_MARGIN;
185
rects[i].y2 = y+h-NUP_MARGIN;
193
for (y = sy, iy = 0;iy < ny;iy++) {
194
for (x = sx, ix = 0;ix < nx;ix++) {
195
rects[i].x1 = x+NUP_MARGIN;
196
rects[i].x2 = x+w-NUP_MARGIN;
197
rects[i].y1 = y+NUP_MARGIN;
198
rects[i].y2 = y+h-NUP_MARGIN;
207
/* Construct nup page */
208
P2PPage::P2PPage(int n, P2PPage **pages, int len, PDFRectangle *box,
209
unsigned int borderFlagA,
210
unsigned int layout, XRef *xrefA, int xpos, int ypos)
213
PDFRectangle rects[16];
217
orgPages = new OrgPage [len];
220
haveCropBox = gFalse;
226
if (n == 8 || n == 6 || n == 2) {
227
/* Rotating is reqiured, layout change is needed.
228
rotating is done in fit method, only change layout here */
231
t = layout ^ (PDFTOPDF_LAYOUT_NEGATEX | PDFTOPDF_LAYOUT_VERTICAL);
233
layout = ((t << 1) & 2) | ((t >> 1) & 1) | (t & 4);
235
nupCalcLayout(n,layout,&mediaBox,rects);
236
resources = new P2PResources(xref);
237
for (i = 0;i < len;i++) {
240
orgPages[i] = pages[i]->orgPages[0]; /* copy */
241
orgPages[i].frame = rects[i];
242
orgPages[i].borderFlag = borderFlagA;
243
/* fixup rect to fit inner rectangle */
244
rects[i].x1 += NUP_PAGE_MARGIN;
245
rects[i].y1 += NUP_PAGE_MARGIN;
246
rects[i].x2 -= NUP_PAGE_MARGIN;
247
rects[i].y2 -= NUP_PAGE_MARGIN;
249
/* do fitting page contents */
250
orgPages[i].fit(&rects[i],&(pages[i]->mediaBox),1.0,&rBox);
251
orgPages[i].position(&rects[i],&rBox,xpos,ypos);
253
/* merge resources */
254
if (pages[i]->resources != 0) {
255
orgPages[i].mappingTable
256
= resources->merge(pages[i]->resources);
257
} else if (orgPages[i].page != 0) {
258
orgPages[i].mappingTable
259
= resources->merge(orgPages[i].page->getResourceDict());
266
if (fontResource != 0) {
270
if (resources != 0) {
279
void P2PPage::outputPDFRectangle(PDFRectangle *rect, P2POutputStream *str)
281
str->printf("[ %f %f %f %f ]",rect->x1,rect->y1,rect->x2,rect->y2);
284
void P2PPage::outputPDFRectangleForPath(PDFRectangle *rect, P2POutputStream *str)
288
if (rect->x1 > rect->x2) {
290
w = rect->x1 - rect->x2;
293
w = rect->x2 - rect->x1;
295
if (rect->y1 > rect->y2) {
297
h = rect->y1 - rect->y2;
300
h = rect->y2 - rect->y1;
302
str->printf("%f %f %f %f",x,y,w,h);
305
void P2PPage::transPDFRectangle(PDFRectangle *rect, P2PMatrix *matA)
307
matA->apply(rect->x1, rect->y1, &(rect->x1), &(rect->y1));
308
matA->apply(rect->x2, rect->y2, &(rect->x2), &(rect->y2));
311
void P2PPage::outputSelf(P2POutputStream *str, P2PPageTree *tree,
312
P2PObject *copiedObj)
314
if (copiedObj == 0) {
317
copiedObj->outputBegin(str);
319
str->puts("<< /Type /Page /Parent ");
320
tree->outputRef(str);
321
str->puts("\n/MediaBox ");
322
outputPDFRectangle(&mediaBox,str);
323
if (haveCropBox && !eqPDFRectangle(&mediaBox,&cropBox)) {
324
str->puts("\n/CropBox ");
325
outputPDFRectangle(&cropBox,str);
327
if (!eqPDFRectangle(&cropBox,&bleedBox)) {
328
str->puts("\n/BleedBox ");
329
outputPDFRectangle(&bleedBox,str);
331
if (eqPDFRectangle(&trimBox,&artBox)) {
332
str->puts("\n/TrimBox ");
333
outputPDFRectangle(&trimBox,str);
335
str->puts("\n/ArtBox ");
336
outputPDFRectangle(&artBox,str);
339
if (numOrgPages > 1 || orgPages[0].page != 0) {
341
P2PXRef::put(&contents);
342
str->puts("\n/Contents ");
343
contents.outputRef(str);
345
str->puts("\n/Resources ");
346
if (resources != 0) {
347
resources->output(str);
349
Dict *dict = orgPages[0].page->getResourceDict();
352
if (fontResource != 0 && fontResource->getNDicts() > 0) {
353
/* replace font resource */
354
const char *p = "Font";
355
P2PObject *objp = fontResource;
357
P2POutput::outputDict(dict,&p,&objp,1,str,xref);
359
P2POutput::outputDict(dict,str,xref);
367
str->puts("\n/Resources << >>");
370
if (copiedObj == 0) {
373
copiedObj->outputEnd(str);
377
void P2PPage::outputContents(P2POutputStream *str)
384
/* if empty, do nothing */
385
if (orgPages[0].page == 0) return;
387
P2PGfx output(xref,str,fontResource,resources);
388
P2PObj *pobj = new P2PObj();
390
contents.outputBegin(str);
391
str->puts("<< /Length ");
393
pobj->outputRef(str);
394
if (P2PDoc::options.contentsCompress && str->canDeflate()) {
395
str->puts(" /Filter /FlateDecode ");
398
str->puts("stream\n");
399
start = str->getPosition();
400
if (P2PDoc::options.contentsCompress) str->startDeflate();
401
for (i = 0;i < numOrgPages;i++) {
405
save = orgPages[i].clipFrame || orgPages[i].trans;
409
if (orgPages[i].page->getContents(&contentsObj) != 0
410
&& !contentsObj.isNull()) {
411
if (orgPages[i].clipFrame) {
412
outputPDFRectangleForPath(&orgPages[i].frame,str);
413
str->puts(" re W n ");
415
if (orgPages[i].borderFlag != 0) {
416
PDFRectangle inner = *(orgPages[i].page->getMediaBox());
417
PDFRectangle ident(0,0,1,1);
420
transPDFRectangle(&ident,&(orgPages[i].mat));
421
xs = ident.x2-ident.x1 > 0 ? 1: -1;
422
ys = ident.y2-ident.y1 > 0 ? 1: -1;
425
if ((orgPages[i].borderFlag & PDFTOPDF_BORDERHAIRLINE) != 0) {
428
if ((orgPages[i].borderFlag & PDFTOPDF_BORDERDOUBLE) != 0) {
429
PDFRectangle outer = *(orgPages[i].page->getMediaBox());
431
transPDFRectangle(&outer,&(orgPages[i].mat));
436
outputPDFRectangleForPath(&outer,str);
440
transPDFRectangle(&inner,&(orgPages[i].mat));
445
outputPDFRectangleForPath(&inner,str);
446
str->puts(" re S Q ");
449
if (orgPages[i].trans) {
450
orgPages[i].mat.output(str);
454
output.outputContents(&contentsObj,orgPages[i].mappingTable,
455
orgPages[i].page->getResourceDict(),&(orgPages[i].mat));
462
if (P2PDoc::options.contentsCompress) str->endDeflate();
463
len = str->getPosition()-start;
464
str->puts("\nendstream\n");
465
contents.outputEnd(str);
466
/* set length object value */
468
pobj->setObj(&lenobj);
470
pobj->output(str,xref);
473
void P2PPage::output(P2POutputStream *str, P2PPageTree *tree, P2PObject *copiedObj)
475
if (fontResource != 0) {
479
if (resources == 0 && orgPages[0].page != 0/* not empty page */) {
480
/* make P2PResource for pattern handling */
481
/* when number-upped, page must have P2PResource already.
482
so, we handling one page case */
483
resources = new P2PResources(xref);
484
orgPages[0].mappingTable
485
= resources->merge(orgPages[0].page->getResourceDict());
487
if (copiedObj == 0) {
488
if (P2PDoc::options.fontEmbedding && orgPages[0].page != 0) {
489
/* only not empty page */
490
fontResource = new P2PFontResource();
491
if (resources != 0) {
492
fontResource->setup(resources,xref);
494
fontResource->setup(orgPages[0].page->getResourceDict(),xref);
498
if (resources != 0) {
499
/* setup pattern dict for translation */
500
resources->setupPattern();
502
resources->setP2PFontResource(fontResource);
506
outputSelf(str,tree,copiedObj);
509
void P2PPage::fit(PDFRectangle *box, double zoom)
512
/* only first orginal page is fitted */
513
orgPages[0].fit(box,&mediaBox,zoom,&rBox);
517
haveCropBox = gFalse;
524
void P2PPage::mirror()
526
P2PMatrix mat(-1,0,0,1,mediaBox.x2-mediaBox.x1,0);
528
/* only first orginale page is mirrored */
529
orgPages[0].mat.trans(&mat);
530
orgPages[0].trans = gTrue;
533
void P2PPage::rotate(int orientation)
537
/* only first orginal page */
538
orgPages[0].rotate(&mediaBox,orientation,&rBox);
542
haveCropBox = gFalse;
548
void P2PPage::position(PDFRectangle *box, int xpos, int ypos)
550
/* only first orginal page */
551
orgPages[0].position(box,&mediaBox,xpos,ypos);
555
haveCropBox = gFalse;
561
void P2PPage::scale(double zoom)
565
/* only first orginal page */
566
orgPages[0].scale(&mediaBox,zoom,&rBox);
570
haveCropBox = gFalse;
576
void P2PPage::autoRotate(PDFRectangle *box)
578
double mediaWidth = box->x2 - box->x1;
579
if (mediaWidth < 0) mediaWidth = -mediaWidth;
580
double mediaHeight = box->y2 - box->y1;
581
if (mediaHeight < 0) mediaHeight = -mediaHeight;
582
/* only proccess when the page has one original page. */
583
double pageWidth = mediaBox.x2 - mediaBox.x1;
584
if (pageWidth < 0) pageWidth = -pageWidth;
585
double pageHeight = mediaBox.y2 - mediaBox.y1;
586
if (pageHeight < 0) pageHeight = -pageHeight;
588
if ((mediaWidth >= pageWidth && mediaHeight >= pageHeight)
589
|| (mediaWidth < pageHeight || mediaHeight < pageWidth)) {
590
/* the page is inside the media or rotated page is not inside */
597
P2PPage::OrgPage::OrgPage()
606
P2PPage::OrgPage::~OrgPage()
608
if (mappingTable != 0) {
613
void P2PPage::OrgPage::position(PDFRectangle *box, PDFRectangle *oldBox,
618
mx = box->x1 - oldBox->x1;
619
my = box->y1 - oldBox->y1;
624
mx += (box->x2-box->x1 - (oldBox->x2-oldBox->x1))/2;
631
mx += (box->x2-box->x1 - (oldBox->x2-oldBox->x1));
638
my += (box->y2-box->y1 - (oldBox->y2-oldBox->y1))/2;
645
my += (box->y2-box->y1 - (oldBox->y2-oldBox->y1));
652
void P2PPage::OrgPage::fit(PDFRectangle *box, PDFRectangle *oldBox,
653
double zoom, PDFRectangle *rBox)
656
GBool landscape, oLandscape;
664
ow = oldBox->x2-oldBox->x1;
665
if (ow < 0) ow = -ow;
666
oh = oldBox->y2-oldBox->y1;
667
if (oh < 0) oh = -oh;
669
oLandscape = (ow > oh);
670
if (landscape != oLandscape) {
672
mat.move(-oldBox->x1,-oldBox->y1);
676
rBox->x1 = rBox->y1 = 0;
683
if (scalex > scaley) {
698
mat.move(box->x1,box->y1);
710
if (scalex > scaley) {
713
rBox->x1 = oldBox->x1*scaley;
714
rBox->y1 = oldBox->y1*scaley;
715
rBox->x2 = oldBox->x2*scaley;
716
rBox->y2 = oldBox->y2*scaley;
721
rBox->x1 = oldBox->x1*scalex;
722
rBox->y1 = oldBox->y1*scalex;
723
rBox->x2 = oldBox->x2*scalex;
724
rBox->y2 = oldBox->y2*scalex;
727
mx = -rBox->x1+box->x1;
728
my = -rBox->y1+box->y1;
740
void P2PPage::OrgPage::rotate(PDFRectangle *box,
741
int orientation, PDFRectangle *rBox)
746
/* move to origin point */
747
mat.move(-box->x1, -box->y1);
748
rBox->x1 = rBox->y1 = 0;
749
rBox->x2 = box->x2-box->x1;
750
rBox->y2 = box->y2-box->y1;
752
switch (orientation) {
759
mat.move(rBox->y2,0);
767
mat.move(rBox->x2,rBox->y2);
772
mat.move(0,rBox->x2);
782
void P2PPage::OrgPage::scale(PDFRectangle *box, double zoom, PDFRectangle *rBox)
784
mat.scale(zoom,zoom);
785
rBox->x1 = box->x1*zoom;
786
rBox->y1 = box->y1*zoom;
787
rBox->x2 = box->x2*zoom;
788
rBox->y2 = box->y2*zoom;