/*************************************************************************** * Copyright (C) 2012 by Pierre Marchand * * pierre@oep-h.com * * * * 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "ReaderJSONCPP.h" #include #include #include #include #include #include #include #ifndef WITHOUT_BOOST_FS3 #define BOOST_FILESYSTEM_VERSION 3 namespace filesystem = boost::filesystem3; #else namespace filesystem = boost::filesystem; #endif #include //#define WITH_CURL #ifdef WITH_CURL extern "C"{ int ospi_curl_get_file(const char * url, const char * filename); } #endif #define OSPI_BUFFER_CHUNK_SIZE 256 namespace ospi { const std::string ReaderJSONCPP::K_OutputFile = std::string("output_file_name"); const std::string ReaderJSONCPP::K_Plan = std::string("plan"); const std::string ReaderJSONCPP::K_TargetWidth = std::string("page_width"); const std::string ReaderJSONCPP::K_TargetHeight = std::string("page_height"); const std::string ReaderJSONCPP::K_Slots = std::string("slots"); const std::string ReaderJSONCPP::K_SlotWidth = std::string("width"); const std::string ReaderJSONCPP::K_SlotHeight = std::string("height"); const std::string ReaderJSONCPP::K_SlotLeft = std::string("left"); const std::string ReaderJSONCPP::K_SlotTop = std::string("top"); const std::string ReaderJSONCPP::K_SlotFile = std::string("file"); const std::string ReaderJSONCPP::K_SlotRemoteFile = std::string("remote_file"); const std::string ReaderJSONCPP::K_SlotRemoteFileURL = std::string("url"); const std::string ReaderJSONCPP::K_SlotRemoteFileType = std::string("type"); const std::string ReaderJSONCPP::K_SlotPage = std::string("page"); const std::string ReaderJSONCPP::K_CropWidth = std::string("crop_width"); const std::string ReaderJSONCPP::K_CropHeight = std::string("crop_height"); const std::string ReaderJSONCPP::K_CropLeft = std::string("crop_left"); const std::string ReaderJSONCPP::K_CropTop = std::string("crop_top"); const std::string ReaderJSONCPP::K_Rotation = std::string("rotation"); const std::string ReaderJSONCPP::K_PageDimMod = std::string("page_dim_mod"); const std::string ReaderJSONCPP::K_CropDimMod = std::string("crop_dim_mod"); const std::string ReaderJSONCPP::K_SlotDimMod = std::string("slot_dim_mod"); const std::string ReaderJSONCPP::V_DimModAbsolute = std::string("absolute"); const std::string ReaderJSONCPP::V_DimModRelative = std::string("relative"); const std::string ReaderJSONCPP::V_DimModPercent = std::string("percent"); bool ReaderJSONCPP::SourcePage_Key::operator< (const ReaderJSONCPP::SourcePage_Key& o) const { if(doc < o.doc) return true; else if(doc == o.doc) { if(pnumber < o.pnumber) return true; else if(pnumber == o.pnumber) { if(bbox.ToString() < o.bbox.ToString()) return true; else return false; } return false; } return false; } ReaderJSONCPP::ReaderJSONCPP(const std::string& plan, const PlanParams& params, bool isData) :planPath(plan), params(params), optIsData(isData) { } void ReaderJSONCPP::readPage(const Json::Value &page, unsigned int tpidx) { double tpagewidth(0); double tpageheight(0); std::string pageDimMod(page.get(K_PageDimMod, V_DimModAbsolute).asString()); if(pageDimMod == V_DimModAbsolute) { tpagewidth = page.get(K_TargetWidth, tpagewidth).asDouble(); tpageheight = page.get(K_TargetHeight, tpageheight).asDouble(); } else if(pageDimMod == V_DimModRelative) { Rectangle r; templatePage(page, r); tpagewidth = r.width() + page.get(K_TargetWidth, 0).asDouble(); tpageheight = r.height() + page.get(K_TargetHeight, 0).asDouble(); } else if(pageDimMod == V_DimModPercent) { Rectangle r; templatePage(page, r); tpagewidth = r.width() * (page.get(K_TargetWidth, 100.0).asDouble() / 100.0); tpageheight = r.height() + (page.get(K_TargetHeight, 100.0).asDouble() / 100.0); } if(tpagewidth <= 0.0 || tpageheight <= 0.0) { Rectangle r; templatePage(page, r); tpagewidth = r.width(); tpageheight = r.height(); // std::cerr<<"Computed target page: w = "<CreatePage(PoDoFo::PdfRect(0,0,tpagewidth,tpageheight)); const Json::Value slots_(page[K_Slots]); std::map pDict; for (unsigned int index(0); index < slots_.size(); ++index ) { Json::Value rec(slots_[index]); readSlot(rec, tpidx, pDict); } } void ReaderJSONCPP::templatePage(const Json::Value &page, Rectangle &rect) { const Json::Value slots_(page[K_Slots]); for (unsigned int index(0); index < slots_.size(); ++index ) { Json::Value slot(slots_[index]); std::string sdoc; unsigned int spagenumber; double left, top, width, height; double cleft, ctop, cwidth, cheight, cbottom; double rotation(slot.get(K_Rotation, 0).asDouble()); std::string slotDimMod(slot.get(K_SlotDimMod, V_DimModAbsolute).asString()); std::string cropDimMod(slot.get(K_CropDimMod, V_DimModAbsolute).asString()); sdoc = slot.get(K_SlotFile, sdoc).asString(); spagenumber = slot.get(K_SlotPage, 1).asInt() - 1; DocumentPtr sdocptr(new PoDoFo::PdfMemDocument(sdoc.c_str())); PoDoFo::PdfPage * sourcepage(sdocptr->GetPage(std::min(int(spagenumber), sdocptr->GetPageCount() - 1))); PoDoFo::PdfRect srect(sourcepage->GetMediaBox()); PoDoFo::PdfRect crect(sourcepage->GetCropBox()); if(cropDimMod == V_DimModAbsolute) { cleft = slot.get(K_CropLeft,crect.GetLeft()).asDouble(); ctop = slot.get(K_CropTop, srect.GetHeight() -(crect.GetBottom() + crect.GetHeight())).asDouble(); cwidth = slot.get(K_CropWidth,crect.GetWidth()).asDouble(); cheight = slot.get(K_CropHeight, crect.GetHeight()).asDouble(); } else if(cropDimMod == V_DimModRelative) { cleft = crect.GetLeft() + slot.get(K_CropLeft, 0).asDouble(); ctop = (srect.GetHeight() - (crect.GetBottom() + crect.GetHeight())) + slot.get(K_CropTop, 0).asDouble(); cwidth = crect.GetWidth() + slot.get(K_CropWidth,0).asDouble(); cheight = crect.GetHeight() + slot.get(K_CropHeight, 0).asDouble(); } else if(cropDimMod == V_DimModPercent) { cleft = srect.GetLeft() + (srect.GetWidth() * (slot.get(K_CropLeft,100.0).asDouble() / 100.0)); ctop = (srect.GetBottom() + srect.GetHeight()) - (srect.GetHeight() * (slot.get(K_CropTop, 100.0).asDouble() / 100.0)); cwidth = srect.GetWidth() * (slot.get(K_CropWidth,100.0).asDouble() / 100.0); cheight = srect.GetHeight() * (slot.get(K_CropHeight, 100.0).asDouble() / 100.0); } left = slot.get(K_SlotLeft, 0).asDouble(); top = slot.get(K_SlotTop, 0).asDouble(); if(slotDimMod == V_DimModAbsolute) { width = slot.get(K_SlotWidth,cwidth).asDouble(); height = slot.get(K_SlotHeight, cheight).asDouble(); } else if(slotDimMod == V_DimModRelative) { width = cwidth + slot.get(K_SlotWidth,0).asDouble(); height = cheight + slot.get(K_SlotHeight,0).asDouble(); } else if(slotDimMod == V_DimModPercent) { width = cwidth *( slot.get(K_SlotWidth,100.0).asDouble() / 100.0); height = cheight *( slot.get(K_SlotHeight,100.0).asDouble() / 100.0); } if(rotation == 1 || rotation == 3) { double tmpW = width; width = height; height = tmpW; } Rectangle slotRect(Point(left, top), Point(left+width, top+height)); rect.united(slotRect); } } void ReaderJSONCPP::readSlot(const Json::Value &slot, unsigned int tpidx, std::map& pDict) { // here we are! std::string sdoc; unsigned int spagenumber; double left, top, width, height, bottom; double cleft, ctop, cwidth, cheight, cbottom; double rotation; PoDoFo::PdfPage * tpage(tdocument->GetPage(tpidx)); std::string slotDimMod(slot.get(K_SlotDimMod, V_DimModAbsolute).asString()); std::string cropDimMod(slot.get(K_CropDimMod, V_DimModAbsolute).asString()); // IMPORTANT: the slot is already rotated. It does mean that width and height of the source document have to be compared to de-rotated slot dimensions // Source PDF file sdoc = slot.get(K_SlotFile, sdoc).asString(); // bool fileIsRemote(false); if(sdoc.empty()) { Json::Value remote(slot.get(K_SlotRemoteFile, sdoc)); std::string rsdocurl(remote.get(K_SlotRemoteFileURL, std::string()).asString()); std::vector urlvec; boost::algorithm::split( urlvec, rsdocurl, boost::algorithm::is_any_of("/"), boost::algorithm::token_compress_on ); bool first(true); BOOST_FOREACH(const std::string& urlpart, urlvec) { if(first) first = false; else sdoc.append(urlpart); } // sdoc = urlvec.back(); // fileIsRemote = true; } // Source page spagenumber = slot.get(K_SlotPage, 1).asInt() - 1; // to discuss with json providers whether we go natural counting or not. std::string origineName(sdoc); origineName.append("#"); origineName.append(boost::lexical_cast(spagenumber)); filesystem::path fp(sdoc.c_str()); if(!filesystem::exists(fp) /*|| fileIsRemote*/) { #ifdef WITH_CURL // try to get it from internet Json::Value remote(slot.get(K_SlotRemoteFile, sdoc)); std::string rsdocurl(remote.get(K_SlotRemoteFileURL, std::string()).asString()); std::string rsdoctype(remote.get(K_SlotRemoteFileType, std::string("pdf")).asString()); if(rsdoctype != std::string("pdf")) throw std::runtime_error("type of remote file is not PDF (JSONCPP)"); if(ospi_curl_get_file(rsdocurl.c_str(), sdoc.c_str()) > 0) throw std::runtime_error("Can't fetch file from internet (JSONCPP/CURL)"); #else throw std::runtime_error("Can't find source PDF file (JSONCPP/CURL)"); #endif } if(sdocuments.find(sdoc) == sdocuments.end()) { DocumentPtr d(new PoDoFo::PdfMemDocument(sdoc.c_str())); sdocuments[sdoc] = d; } DocumentPtr sdocptr(sdocuments[sdoc]); if(sdocptr->GetPageCount() <= spagenumber) { spagenumber = sdocptr->GetPageCount() - 1; // std::cerr<<"Try to get non-existing page, take the last page of the document"<GetPage(spagenumber)); // GEOMETRY // NOTE: rotation point is the center of the cropped page rotation = slot.get(K_Rotation, 0).asDouble(); PoDoFo::PdfRect targetPageRect(tpage->GetMediaBox()); PoDoFo::PdfRect srect(sourcepage->GetMediaBox()); PoDoFo::PdfRect crect(sourcepage->GetCropBox()); if(cropDimMod == V_DimModAbsolute) { cleft = slot.get(K_CropLeft,crect.GetLeft()).asDouble(); ctop = slot.get(K_CropTop, srect.GetHeight() -(crect.GetBottom() + crect.GetHeight())).asDouble(); cwidth = slot.get(K_CropWidth,crect.GetWidth()).asDouble(); cheight = slot.get(K_CropHeight, crect.GetHeight()).asDouble(); } else if(cropDimMod == V_DimModRelative) { cleft = crect.GetLeft() + slot.get(K_CropLeft, 0).asDouble(); ctop = (srect.GetHeight() - (crect.GetBottom() + crect.GetHeight())) + slot.get(K_CropTop, 0).asDouble(); cwidth = crect.GetWidth() + slot.get(K_CropWidth,0).asDouble(); cheight = crect.GetHeight() + slot.get(K_CropHeight, 0).asDouble(); } else if(cropDimMod == V_DimModPercent) { // here it makes it too much convoluted to use the given BleedBox, let's assume the effect is to crop to some percentage of the MediaBox cleft = srect.GetLeft() + (srect.GetWidth() * (slot.get(K_CropLeft,100.0).asDouble() / 100.0)); ctop = (srect.GetBottom() + srect.GetHeight()) - (srect.GetHeight() * (slot.get(K_CropTop, 100.0).asDouble() / 100.0)); cwidth = srect.GetWidth() * (slot.get(K_CropWidth,100.0).asDouble() / 100.0); cheight = srect.GetHeight() * (slot.get(K_CropHeight, 100.0).asDouble() / 100.0); } // cbottom = srect.GetHeight() - (ctop + cheight); left = slot.get(K_SlotLeft, 0).asDouble(); top = slot.get(K_SlotTop, 0).asDouble(); if(slotDimMod == V_DimModAbsolute) { width = slot.get(K_SlotWidth, /*cwidth*/ 0).asDouble(); height = slot.get(K_SlotHeight, /*cheight*/ 0).asDouble(); if(width == double(0) || height == double(0)) { if(rotation == 1 || rotation == 3) { height = cwidth; width = cheight; } else { width = cwidth; height = cheight; } } } else { if(rotation == 1 || rotation == 3) { if(slotDimMod == V_DimModRelative) { height = cwidth + slot.get(K_SlotWidth, 0).asDouble(); width = cheight + slot.get(K_SlotHeight, 0).asDouble(); } else if(slotDimMod == V_DimModPercent) { height = cwidth * (slot.get(K_SlotWidth, 100.0).asDouble() / 100.0); width = cheight * (slot.get(K_SlotHeight, 100.0).asDouble() / 100.0); } } else { if(slotDimMod == V_DimModRelative) { width = cwidth + slot.get(K_SlotWidth, 0).asDouble(); height = cheight + slot.get(K_SlotHeight, 0).asDouble(); } else if(slotDimMod == V_DimModPercent) { width = cwidth * (slot.get(K_SlotWidth, 100.0).asDouble() / 100.0); height = cheight * (slot.get(K_SlotHeight, 100.0).asDouble() / 100.0); } } } bottom = targetPageRect.GetHeight() - (top + height); ospi::Point slotCenter(left + (width / 2.0), (targetPageRect.GetHeight() - top) - (height / 2.0)); PoDoFo::PdfRect crop(cleft, srect.GetHeight() -( ctop + cheight), cwidth, cheight); trx_double_t rotate(-rotation * 90.0); trx_double_t scaleX(width / cwidth); trx_double_t scaleY(height / cheight); if(rotation == 1 || rotation == 3) { scaleX = width / cheight; scaleY = height / cwidth; } ospi::Point cropCenter((cleft + (cwidth / 2.0)) * 1.0, ((srect.GetHeight() - ctop) - (cheight / 2.0)) * 1.0); trx_double_t transX(slotCenter.getX() - cropCenter.getX()); trx_double_t transY(slotCenter.getY() - cropCenter.getY()); Transform t; t.translate(transX, transY); t.rotate(rotate, slotCenter); t.scale(scaleX, scaleY, slotCenter); SourcePage_Key spk(sdoc, spagenumber, crop); SourcePagePtr sp; if(pDict.find(spk) == pDict.end()) { sp = SourcePagePtr(new SourcePage); sp->setSourceDoc(sdocptr.get()); sp->setSourcePage(spagenumber); sp->setTargetDoc(tdocument.get()); sp->setTargetPage(tpidx); sp->setCrop(crop); pDict[spk] = sp; } else sp = pDict.find(spk)->second; sp->addTransform(t); if(std::find(spages.begin(), spages.end(), sp) == spages.end()) spages.push_back(sp); } std::string ReaderJSONCPP::getPlanData(std::istream &in) { std::vector< boost::shared_ptr > bufVec; int tlen(0); int chunkCount(0); while(in.good()) { boost::shared_ptr pbuf(new char[OSPI_BUFFER_CHUNK_SIZE]); in.read(pbuf.get(), OSPI_BUFFER_CHUNK_SIZE); bufVec.push_back(pbuf); tlen += in.gcount(); chunkCount++; } if(tlen == 0) throw std::runtime_error ( "Failed to read plan data" ); int bufSize((chunkCount * OSPI_BUFFER_CHUNK_SIZE) + 1); boost::shared_ptr buffer(new char[bufSize]); memset(buffer.get(), '\0', bufSize); unsigned int bufVecSize(bufVec.size()); for(unsigned int i(0); i < bufVecSize; i++) { memcpy(buffer.get() + (i * OSPI_BUFFER_CHUNK_SIZE), bufVec[i].get(), OSPI_BUFFER_CHUNK_SIZE); } return std::string(buffer.get(), tlen); } int ReaderJSONCPP::Impose() { std::string jsondata; if(!optIsData) { if(planPath == std::string("-")) jsondata = getPlanData(std::cin); else { std::ifstream in( planPath.c_str(), std::ifstream::in ); jsondata = getPlanData(in); } } else jsondata = planPath; Json::Value root; Json::Reader reader; bool parsingSuccessful = reader.parse( jsondata, root ); if ( !parsingSuccessful ) { throw std::runtime_error(reader.getFormattedErrorMessages()); } std::string outputName; outputName = root.get(K_OutputFile,outputName).asString(); if(outputName.empty()) { if(params.Has(K_OutputFile)) { outputName = params.Get(K_OutputFile, std::string()); } if(outputName.empty()) throw std::runtime_error("Can't get output_file_name (JSONCPP)"); } PoDoFo::PdfMemDocument * tdoc(new PoDoFo::PdfMemDocument); tdocument = DocumentPtr(tdoc); const Json::Value plan(root[K_Plan]); for (unsigned int index(0); index < plan.size(); ++index ) { Json::Value rec(plan[index]); readPage(rec, index); } BOOST_FOREACH(SourcePagePtr spp, spages) { spp->commit(); } tdocument->SetWriteMode(PoDoFo::ePdfWriteMode_Clean); tdocument->Write(outputName.c_str()); } } // namespace ospi