|
|
|
/*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Page.h"
|
|
|
|
#include "Document.h"
|
|
|
|
|
|
|
|
#include "ContentHandler.h"
|
|
|
|
#include "Exception.h"
|
|
|
|
#include "MediaBoxElementHandler.h"
|
|
|
|
#include "CropBoxElementHandler.h"
|
|
|
|
#include "TypeElementHandler.h"
|
|
|
|
#include "RemoveHimSelfHandler.h"
|
|
|
|
#include "AnnotsHandler.h"
|
|
|
|
#include "RotationHandler.h"
|
|
|
|
#include "FlateDecode.h"
|
|
|
|
#include "Utils.h"
|
|
|
|
#include "Rectangle.h"
|
|
|
|
#include "Filter.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include <string.h>
|
|
|
|
#include "Parser.h"
|
|
|
|
|
|
|
|
#include "core/memcheck.h"
|
|
|
|
|
|
|
|
using namespace merge_lib;
|
|
|
|
|
|
|
|
|
|
|
|
Page::Page(unsigned int pageNumber): _root(NULL),_pageNumber(pageNumber), _rotation(0)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Page::~Page()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string & Page::getPageContent()
|
|
|
|
{
|
|
|
|
return _root->getObjectContent();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Object::Children & Page::getPageRefs()
|
|
|
|
{
|
|
|
|
return _root->getChildren();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Page::recalculateObjectNumbers(unsigned int & newNumber)
|
|
|
|
{
|
|
|
|
_root->recalculateObjectNumbers(newNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
Object * Page::pageToXObject(std::vector<Object *> & allObjects, std::vector<Object *> & annots, bool isCloneNeeded)
|
|
|
|
{
|
|
|
|
Object * xObject = (isCloneNeeded) ? _root->getClone(allObjects) : _root;
|
|
|
|
return _pageToXObject(xObject, annots);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Object * Page::_pageToXObject(Object *& page, std::vector<Object *> & annots)
|
|
|
|
{
|
|
|
|
RemoveHimselfHandler * removeParent = new RemoveHimselfHandler(page, "/Parent");
|
|
|
|
RemoveHimselfHandler * removeBleedBox = new RemoveHimselfHandler(page, "/BleedBox");
|
|
|
|
RemoveHimselfHandler * removeTrimBox = new RemoveHimselfHandler(page, "/TrimBox");
|
|
|
|
RemoveHimselfHandler * removeArtBox = new RemoveHimselfHandler(page, "/ArtBox");
|
|
|
|
RemoveHimselfHandler * removeBoxColorInfo = new RemoveHimselfHandler(page, "/BoxColorInfo");
|
|
|
|
RemoveHimselfHandler * removeRotate = new RemoveHimselfHandler(page, "/Rotate");
|
|
|
|
RemoveHimselfHandler * removeThumb = new RemoveHimselfHandler(page, "/Thumb");
|
|
|
|
RemoveHimselfHandler * removeB = new RemoveHimselfHandler(page, "/B");
|
|
|
|
RemoveHimselfHandler * removeDur = new RemoveHimselfHandler(page, "/Dur");
|
|
|
|
RemoveHimselfHandler * removeTrans = new RemoveHimselfHandler(page, "/Trans");
|
|
|
|
AnnotsHandler * removeAnnots = new AnnotsHandler(page, "/Annots", annots);
|
|
|
|
RemoveHimselfHandler * removeAA = new RemoveHimselfHandler(page, "/AA");
|
|
|
|
RemoveHimselfHandler * removeID = new RemoveHimselfHandler(page, "/ID");
|
|
|
|
RemoveHimselfHandler * removePZ = new RemoveHimselfHandler(page, "/PZ");
|
|
|
|
RemoveHimselfHandler * removeSeparationInfo = new RemoveHimselfHandler(page, "/SeparationInfo");
|
|
|
|
RemoveHimselfHandler * removeTabs = new RemoveHimselfHandler(page, "/Tabs");
|
|
|
|
RemoveHimselfHandler * removeTemplateInstantiated = new RemoveHimselfHandler(page, "/TemplateInstantiated");
|
|
|
|
RemoveHimselfHandler * removePresSteps = new RemoveHimselfHandler(page, "/PresSteps");
|
|
|
|
RemoveHimselfHandler * removeUserUnit = new RemoveHimselfHandler(page, "/UserUnit");
|
|
|
|
RemoveHimselfHandler * removeVP = new RemoveHimselfHandler(page, "/VP");
|
|
|
|
ContentHandler * contentHandler = new ContentHandler(page, "/Contents");
|
|
|
|
CropBoxElementHandler * cropBoxElementHandler = new CropBoxElementHandler(page);
|
|
|
|
MediaBoxElementHandler * mediaBoxElementHandler = new MediaBoxElementHandler(page);
|
|
|
|
TypeElementHandler * typeElementHandler = new TypeElementHandler(page);
|
|
|
|
|
|
|
|
cropBoxElementHandler->addNextHandler(mediaBoxElementHandler);
|
|
|
|
mediaBoxElementHandler->addNextHandler(removeParent);
|
|
|
|
removeParent->addNextHandler(removeBleedBox);
|
|
|
|
removeBleedBox->addNextHandler(removeTrimBox);
|
|
|
|
removeTrimBox->addNextHandler(removeArtBox);
|
|
|
|
removeArtBox->addNextHandler(removeBoxColorInfo);
|
|
|
|
removeBoxColorInfo->addNextHandler(removeRotate);
|
|
|
|
removeRotate->addNextHandler(removeThumb);
|
|
|
|
removeThumb->addNextHandler(removeB);
|
|
|
|
removeB->addNextHandler(removeDur);
|
|
|
|
removeDur->addNextHandler(removeTrans);
|
|
|
|
removeTrans->addNextHandler(removeAnnots);
|
|
|
|
removeAnnots->addNextHandler(removeAA);
|
|
|
|
removeAA->addNextHandler(removeID);
|
|
|
|
removeID->addNextHandler(removePZ);
|
|
|
|
removePZ->addNextHandler(removeSeparationInfo);
|
|
|
|
removeSeparationInfo->addNextHandler(removeTabs);
|
|
|
|
removeTabs->addNextHandler(removeTemplateInstantiated);
|
|
|
|
removeTemplateInstantiated->addNextHandler(removePresSteps);
|
|
|
|
removePresSteps->addNextHandler(removeUserUnit);
|
|
|
|
removeUserUnit->addNextHandler(removeVP);
|
|
|
|
removeVP->addNextHandler(typeElementHandler);
|
|
|
|
typeElementHandler->addNextHandler(contentHandler);
|
|
|
|
|
|
|
|
cropBoxElementHandler->processObjectContent();
|
|
|
|
cropBoxElementHandler->changeObjectContent();
|
|
|
|
|
|
|
|
delete cropBoxElementHandler;
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string _getContentOfContentObject(MergePageDescription & description)
|
|
|
|
{
|
|
|
|
std::string content("<<\n/Length ");
|
|
|
|
std::string stream = "";
|
|
|
|
if(!description.skipBasePage)
|
|
|
|
{
|
|
|
|
stream.append("1.000000 0 0 1.000000 0 0 cm\n"
|
|
|
|
"q\n");
|
|
|
|
stream.append(description.basePageTransformation.getCMT());
|
|
|
|
stream.append("/OriginalPage2 Do\n"
|
|
|
|
"Q\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
if(!description.skipOverlayPage)
|
|
|
|
{
|
|
|
|
stream.append("1.000000 0 0 1.000000 0 0 cm\n"
|
|
|
|
"q\n");
|
|
|
|
stream.append(description.overlayPageTransformation.getCMT());
|
|
|
|
stream.append("/OriginalPage1 Do\n"
|
|
|
|
"Q\n");
|
|
|
|
}
|
|
|
|
FlateDecode encoder;
|
|
|
|
encoder.encode(stream);
|
|
|
|
content.append(Utils::uIntToStr(stream.size()));
|
|
|
|
content.append("\n/Filter /FlateDecode\n>>\nstream\n");
|
|
|
|
content.append(stream);
|
|
|
|
content.append("endstream\n");
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _recalculateAnnotsCoordinates(Object * annotation,
|
|
|
|
const Rectangle & basePagesRectangle,
|
|
|
|
const Rectangle & outputPagesRectangle,
|
|
|
|
const MergePageDescription & description)
|
|
|
|
{
|
|
|
|
Q_UNUSED(outputPagesRectangle);
|
|
|
|
Q_UNUSED(basePagesRectangle);
|
|
|
|
std::string annotsRectangleName("/Rect");
|
|
|
|
Object * objectWithRectangle;
|
|
|
|
unsigned int fake;
|
|
|
|
annotation->findObject(annotsRectangleName, objectWithRectangle, fake);
|
|
|
|
std::string annotContent = objectWithRectangle->getObjectContent();
|
|
|
|
|
|
|
|
Rectangle annotsRectangle(annotsRectangleName.c_str(), annotContent);
|
|
|
|
|
|
|
|
//we move annotation from base page to output page
|
|
|
|
//that's way annotation should be scaled before all transformations.
|
|
|
|
//Annotation's coordinates should be recalculated according to new
|
|
|
|
//page width and height
|
|
|
|
|
|
|
|
annotsRectangle.recalculateInternalRectangleCoordinates(description.basePageTransformation.getAnnotsTransformations());
|
|
|
|
annotsRectangle.updateRectangle(annotation, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
// function updates parent reference of annotation with new page object
|
|
|
|
static void _updateAnnotParentPage(Object *annotation,Object *newParentPage)
|
|
|
|
{
|
|
|
|
if( annotation )
|
|
|
|
{
|
|
|
|
std::string strP = "/P";
|
|
|
|
std::string &annotContent = annotation->getObjectContent();
|
|
|
|
|
|
|
|
size_t startOfP = Parser::findTokenName(annotContent,strP);
|
|
|
|
if((int) startOfP == -1 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
size_t endOfP = Parser::findEndOfElementContent(annotContent,startOfP + strP.size());
|
|
|
|
|
|
|
|
// lets find object with reference to parent
|
|
|
|
std::vector<Object *> children = annotation->getChildrenByBounds(startOfP,endOfP);
|
|
|
|
if( children.size() == 0 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object *childWithP = children[0];
|
|
|
|
if( childWithP )
|
|
|
|
{
|
|
|
|
Object::ReferencePositionsInContent pagePosition = annotation->removeChild(childWithP);
|
|
|
|
annotation->eraseContent(startOfP,endOfP-startOfP);
|
|
|
|
std::stringstream strout;
|
|
|
|
strout <<"/P "<<newParentPage->getObjectNumber()<<" "<<newParentPage->getgenerationNumber()<<" R\n";
|
|
|
|
// to compensate posible deviation
|
|
|
|
for(size_t i =strout.str().size();i<endOfP-startOfP;i++)
|
|
|
|
{
|
|
|
|
strout<<" ";
|
|
|
|
}
|
|
|
|
annotation->insertToContent(startOfP,strout.str());
|
|
|
|
annotation->addChild(newParentPage,pagePosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// function performs adjusting of some color parameters of annotation
|
|
|
|
// to avoid interference with overlay content
|
|
|
|
static void _updateAnnotFormColor(Object *annotation )
|
|
|
|
{
|
|
|
|
std::string &objectContent = annotation->getObjectContent();
|
|
|
|
if((int) objectContent.find("/Widget") == -1 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
size_t startOfAP = Parser::findTokenName(objectContent,"/AP");
|
|
|
|
if((int) startOfAP == -1 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
size_t endOfAP = objectContent.find(">>", startOfAP);
|
|
|
|
std::vector<Object *> aps = annotation->getChildrenByBounds(startOfAP, endOfAP);
|
|
|
|
for(size_t i = 0; i < aps.size(); ++i)
|
|
|
|
{
|
|
|
|
Object * childWithAP = aps[i];
|
|
|
|
if( !childWithAP->hasStream() )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// first lets obtain and decode stream of Annotation appearrence stream
|
|
|
|
std::string & content = childWithAP->getObjectContent();
|
|
|
|
|
|
|
|
Filter filter(childWithAP);
|
|
|
|
std::string decodedStream;
|
|
|
|
filter.getDecodedStream(decodedStream);
|
|
|
|
|
|
|
|
// lets iterate over stream and find operator f and remove it!
|
|
|
|
size_t beg = 0;
|
|
|
|
size_t found = 0;
|
|
|
|
std::string token;
|
|
|
|
while(Parser::getNextWord(token,decodedStream,beg,&found))
|
|
|
|
{
|
|
|
|
if( token == "f" || token == "F" )
|
|
|
|
{
|
|
|
|
if((int) found != -1 )
|
|
|
|
{
|
|
|
|
decodedStream[found] = ' ';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Then we need to update Filter section (if any)
|
|
|
|
std::string filterStr = "/Filter";
|
|
|
|
size_t startOfFlate = Parser::findTokenName(content,filterStr);
|
|
|
|
if((int) startOfFlate != -1 )
|
|
|
|
{
|
|
|
|
size_t endOfFlate = Parser::findEndOfElementContent(content,startOfFlate+filterStr.size());
|
|
|
|
childWithAP->eraseContent(startOfFlate,endOfFlate-startOfFlate);
|
|
|
|
//encode and put new stream to object content
|
|
|
|
childWithAP->insertToContent(startOfFlate,"/Filter /FlateDecode ");
|
|
|
|
FlateDecode flate;
|
|
|
|
flate.encode(decodedStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
// update the length field
|
|
|
|
std::string lengthStr = "/Length";
|
|
|
|
size_t startOfLength = Parser::findTokenName(content,lengthStr,0);
|
|
|
|
if((int) startOfLength != -1 )
|
|
|
|
{
|
|
|
|
size_t endOfLength = Parser::findEndOfElementContent(content,startOfLength + lengthStr.size());
|
|
|
|
childWithAP->eraseContent(startOfLength,endOfLength-startOfLength);
|
|
|
|
std::stringstream ostr;
|
|
|
|
ostr<<"/Length "<< decodedStream.size()<<"\n";
|
|
|
|
childWithAP->insertToContent(startOfLength,ostr.str());
|
|
|
|
|
|
|
|
// update the stream of object with new content
|
|
|
|
std::string stream("stream");
|
|
|
|
size_t leftBoundOfContentStream = content.find(stream);
|
|
|
|
if((int) leftBoundOfContentStream != -1 )
|
|
|
|
{
|
|
|
|
size_t rightBoundOfContentStream = content.find("endstream", leftBoundOfContentStream);
|
|
|
|
if((int) rightBoundOfContentStream == -1 )
|
|
|
|
{
|
|
|
|
rightBoundOfContentStream = content.size() - 1;
|
|
|
|
}
|
|
|
|
childWithAP->eraseContent(leftBoundOfContentStream, rightBoundOfContentStream - leftBoundOfContentStream);
|
|
|
|
decodedStream.insert(0,"\nstream\n");
|
|
|
|
childWithAP->insertToContent(leftBoundOfContentStream,decodedStream);
|
|
|
|
childWithAP->appendContent("endstream\n");
|
|
|
|
childWithAP->forgetStreamInFile();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// sometimes page object does not have resources,
|
|
|
|
// they are inherited from parent object
|
|
|
|
// this method processes such cases and insert resources from parent to page
|
|
|
|
// for correct X-Object transformation
|
|
|
|
static void processBasePageResources(Object *basePage)
|
|
|
|
{
|
|
|
|
if( basePage == NULL )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::string resourceToken = "/Resources";
|
|
|
|
if((int) Parser::findTokenName(basePage->getObjectContent(),resourceToken) == -1 )
|
|
|
|
{
|
|
|
|
// it seems base page does not have resources, they can be located in parent!
|
|
|
|
Object *resource = basePage->findPatternInObjOrParents(resourceToken);
|
|
|
|
if( resource )
|
|
|
|
{
|
|
|
|
std::string &resContStr = resource->getObjectContent();
|
|
|
|
size_t startOfRes = Parser::findTokenName(resContStr,resourceToken);
|
|
|
|
if((int) startOfRes == -1 )
|
|
|
|
{
|
|
|
|
// no resources at all
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
size_t endOfRes = Parser::findEndOfElementContent(resContStr,startOfRes + resourceToken.size());
|
|
|
|
if((int) endOfRes == -1 )
|
|
|
|
{
|
|
|
|
return; // broken resources
|
|
|
|
}
|
|
|
|
std::string resourceContent = resContStr.substr(startOfRes,endOfRes-startOfRes);
|
|
|
|
|
|
|
|
size_t positionToInsert = basePage->getObjectContent().find("<<");
|
|
|
|
if((int) positionToInsert == -1 )
|
|
|
|
{
|
|
|
|
positionToInsert = 0;
|
|
|
|
resourceContent.insert(0,"<<");
|
|
|
|
resourceContent.append(">>");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
positionToInsert += strlen("<<");
|
|
|
|
}
|
|
|
|
// insert obtained resources to base page
|
|
|
|
basePage->insertToContent(positionToInsert,resourceContent);
|
|
|
|
|
|
|
|
// if resource contains childs, then we need to add reference to them to current object
|
|
|
|
std::vector<Object *> resChilds = resource->getChildrenByBounds(startOfRes, endOfRes);
|
|
|
|
std::vector<Object *>::const_iterator objectIt(resChilds.begin());
|
|
|
|
|
|
|
|
const Object::Children & children = resource->getChildren();
|
|
|
|
|
|
|
|
for(; objectIt != resChilds.end(); objectIt++ )
|
|
|
|
{
|
|
|
|
Object::Children::const_iterator childrenIt = children.find( (*objectIt)->getObjectNumber());
|
|
|
|
if( childrenIt != children.end() )
|
|
|
|
{
|
|
|
|
Object::ReferencePositionsInContent refPositionInCont = (*childrenIt).second.second;
|
|
|
|
|
|
|
|
Object::ReferencePositionsInContent::iterator positionIt(refPositionInCont.begin());
|
|
|
|
|
|
|
|
Object::ReferencePositionsInContent newPositions;
|
|
|
|
for( ;positionIt != refPositionInCont.end(); positionIt++ )
|
|
|
|
{
|
|
|
|
newPositions.push_back( (*positionIt) - startOfRes + positionToInsert );
|
|
|
|
}
|
|
|
|
|
|
|
|
basePage->addChild( (*objectIt),newPositions );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Page::_getMergedPageContent( unsigned int & contentPosition,
|
|
|
|
unsigned int & parentPosition,
|
|
|
|
unsigned int & originalPage1Position,
|
|
|
|
unsigned int & originalPage2Position,
|
|
|
|
std::pair<unsigned int, unsigned int> originalPageNumbers,
|
|
|
|
const MergePageDescription & description,
|
|
|
|
Object * basePage,
|
|
|
|
const std::vector<Object *> & annots,
|
|
|
|
std::vector <Object::ChildAndItPositionInContent> & annotsPositions
|
|
|
|
)
|
|
|
|
{
|
|
|
|
std::string content("<<\n/Type /Page\n");
|
|
|
|
content.append("/Contents ");
|
|
|
|
contentPosition = content.size();
|
|
|
|
//object number 1 will be recalculated during serialization
|
|
|
|
content.append("1 0 R\n");
|
|
|
|
Rectangle mediaBox("/MediaBox");
|
|
|
|
mediaBox.x2 = description.outPageWidth;
|
|
|
|
mediaBox.y2 = description.outPageHeight;
|
|
|
|
mediaBox.appendRectangleToString(content, " ");
|
|
|
|
content.append("/Parent ");
|
|
|
|
parentPosition = content.size();
|
|
|
|
//object number 1 will be recalculated during serialization
|
|
|
|
content.append("1 0 R\n");
|
|
|
|
content.append("/Resources <<\n"
|
|
|
|
"/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]\n"
|
|
|
|
"/XObject <<\n");
|
|
|
|
if(!description.skipOverlayPage)
|
|
|
|
{
|
|
|
|
content.append("/OriginalPage1 ");
|
|
|
|
originalPage1Position = content.size();
|
|
|
|
content.append(Utils::uIntToStr(originalPageNumbers.first));
|
|
|
|
content.append(" 0 R\n");
|
|
|
|
}
|
|
|
|
if(!description.skipBasePage)
|
|
|
|
{
|
|
|
|
content.append("/OriginalPage2 ");
|
|
|
|
originalPage2Position = content.size();
|
|
|
|
content.append(Utils::uIntToStr(originalPageNumbers.second));
|
|
|
|
content.append(" ");
|
|
|
|
content.append(Utils::uIntToStr(basePage->getgenerationNumber()));
|
|
|
|
content.append(" R\n");
|
|
|
|
}
|
|
|
|
content.append(">>\n>>\n");
|
|
|
|
|
|
|
|
content.append("/Annots [ ");
|
|
|
|
if(!description.skipBasePage)
|
|
|
|
{
|
|
|
|
Rectangle basePageRectangle("/BBox", basePage->getObjectContent());
|
|
|
|
for(size_t i = 0; i < annots.size(); ++i)
|
|
|
|
{
|
|
|
|
_updateAnnotFormColor(annots[i]);
|
|
|
|
_recalculateAnnotsCoordinates(annots[i], basePageRectangle, mediaBox, description);
|
|
|
|
Object::ReferencePositionsInContent annotationPosition;
|
|
|
|
annotationPosition.push_back(content.size());
|
|
|
|
Object::ChildAndItPositionInContent annotAndItPosition(annots[i], annotationPosition);
|
|
|
|
annotsPositions.push_back(annotAndItPosition);
|
|
|
|
content.append(Utils::uIntToStr(annots[i]->getObjectNumber()));
|
|
|
|
content.append(" ");
|
|
|
|
content.append(Utils::uIntToStr(annots[i]->getgenerationNumber()));
|
|
|
|
content.append(" R ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content.append("] \n>>\n");
|
|
|
|
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Page::merge(Page * sourcePage, Document * parentDocument, MergePageDescription & description, bool isPageDuplicated)
|
|
|
|
{
|
|
|
|
if( sourcePage == NULL )
|
|
|
|
{
|
|
|
|
description.skipBasePage = true;
|
|
|
|
}
|
|
|
|
if(!description.skipOverlayPage)
|
|
|
|
{
|
|
|
|
// Lets recalculate final transformation of overlay page
|
|
|
|
// before it will be places into XObject
|
|
|
|
Rectangle mediaBox("/MediaBox",_root->getObjectContent());
|
|
|
|
description.overlayPageTransformation.recalculateTranslation(mediaBox.getWidth(),mediaBox.getHeight());
|
|
|
|
|
|
|
|
std::vector<Object *> fake;
|
|
|
|
_pageToXObject(_root, fake);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Object *> toAllObjects;
|
|
|
|
std::vector<Object *> annotations;
|
|
|
|
Object * sourcePageToXObject = 0;
|
|
|
|
if(!description.skipBasePage)
|
|
|
|
{
|
|
|
|
RotationHandler rotationHandler(sourcePage->_root, "/Rotate", *this);
|
|
|
|
rotationHandler.processObjectContent();
|
|
|
|
description.basePageTransformation.addRotation(_rotation);
|
|
|
|
|
|
|
|
if((int) sourcePage->_root->getObjectContent().find("/Annots") != -1 )
|
|
|
|
{
|
|
|
|
Object *crop = sourcePage->_root->findPatternInObjOrParents("/CropBox");
|
|
|
|
if( crop )
|
|
|
|
{
|
|
|
|
// we need to calculate special compensational shifting
|
|
|
|
// for annotations if cropbox is starting not from 0,0
|
|
|
|
Rectangle mediaBox("/CropBox", crop->getObjectContent());
|
|
|
|
if( !Utils::doubleEquals(mediaBox.x1,0) || !Utils::doubleEquals(mediaBox.y1,0) )
|
|
|
|
{
|
|
|
|
double shiftX = Utils::doubleEquals(mediaBox.x1,0)?0:-mediaBox.x1;
|
|
|
|
double shiftY = Utils::doubleEquals(mediaBox.y1,0)?0:-mediaBox.y1;
|
|
|
|
Translation compensation(shiftX,shiftY);
|
|
|
|
description.basePageTransformation.addAnnotsTransformation(compensation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
processBasePageResources(sourcePage->_root);
|
|
|
|
sourcePageToXObject = sourcePage->pageToXObject(toAllObjects, annotations, isPageDuplicated);
|
|
|
|
Rectangle mediaBox("/BBox", sourcePageToXObject->getObjectContent());
|
|
|
|
description.basePageTransformation.recalculateTranslation(mediaBox.getWidth(),mediaBox.getHeight());
|
|
|
|
}
|
|
|
|
|
|
|
|
Object * catalog = 0;
|
|
|
|
unsigned int fake;
|
|
|
|
if(!parentDocument->getDocumentObject()->findObject(std::string("/Kids"), catalog, fake))
|
|
|
|
{
|
|
|
|
std::string error("Wrong document ");
|
|
|
|
error.append("There is no object with Kids field");
|
|
|
|
throw Exception(error);
|
|
|
|
}
|
|
|
|
Object::ReferencePositionsInContent pagePosition = catalog->removeChild(_root);
|
|
|
|
//create merged Page
|
|
|
|
unsigned int contentPosition, parentPosition, originalPage1Position, originalPage2Position;
|
|
|
|
std::pair<unsigned int, unsigned int> originalPageNumbers(_root->getObjectNumber(), 0);
|
|
|
|
if(!description.skipBasePage)
|
|
|
|
originalPageNumbers.second = sourcePageToXObject->getObjectNumber();
|
|
|
|
std::vector <Object::ChildAndItPositionInContent> annotsAndItPositions;
|
|
|
|
std::string mergedPageContent = _getMergedPageContent(contentPosition,
|
|
|
|
parentPosition,
|
|
|
|
originalPage1Position,
|
|
|
|
originalPage2Position,
|
|
|
|
originalPageNumbers,
|
|
|
|
description,
|
|
|
|
sourcePageToXObject,
|
|
|
|
annotations,
|
|
|
|
annotsAndItPositions
|
|
|
|
);
|
|
|
|
Object * mergedPage = new Object(_root->getObjectNumber(), _root->getgenerationNumber(), mergedPageContent);
|
|
|
|
toAllObjects.push_back(mergedPage);
|
|
|
|
std::vector < unsigned int > contentPositionVec, parentPositionVec, originalPage1PositionVec, originalPage2PositionVec;
|
|
|
|
contentPositionVec.push_back(contentPosition);
|
|
|
|
parentPositionVec.push_back(parentPosition);
|
|
|
|
originalPage1PositionVec.push_back(originalPage1Position);
|
|
|
|
originalPage2PositionVec.push_back(originalPage2Position);
|
|
|
|
Object * contentOfMergedPage = new Object(1, 0, _getContentOfContentObject(description));
|
|
|
|
toAllObjects.push_back(contentOfMergedPage);
|
|
|
|
parentDocument->addToAllObjects(toAllObjects);
|
|
|
|
mergedPage->addChild(contentOfMergedPage, contentPositionVec);
|
|
|
|
mergedPage->addChild(catalog, parentPositionVec);
|
|
|
|
if(!description.skipOverlayPage)
|
|
|
|
mergedPage->addChild(_root, originalPage1PositionVec);
|
|
|
|
if(!description.skipBasePage)
|
|
|
|
mergedPage->addChild(sourcePageToXObject, originalPage2PositionVec);
|
|
|
|
// Annotation parent page should be changed, since we moved old page
|
|
|
|
// to Xobject
|
|
|
|
if( !description.skipBasePage )
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i< annotations.size();i++)
|
|
|
|
{
|
|
|
|
_updateAnnotParentPage(annotations[i],mergedPage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(size_t i = 0; i < annotsAndItPositions.size(); ++i)
|
|
|
|
{
|
|
|
|
mergedPage->addChild(annotsAndItPositions[i].first, annotsAndItPositions[i].second);
|
|
|
|
}
|
|
|
|
catalog->addChild(mergedPage, pagePosition);
|
|
|
|
_root = mergedPage;
|
|
|
|
}
|
|
|
|
|
|
|
|
|