/* * PDFedit - free program for PDF document manipulation. * Copyright (C) 2006, 2007 PDFedit team: Michal Hocko, * Miroslav Jahoda, * Jozef Misutka, * Martin Petricek * * Project is hosted on http://sourceforge.net/projects/pdfedit */ // vim:tabstop=4:shiftwidth=4:noexpandtab:textwidth=80 /* * $RCSfile: pdfwriter.cc,v $ * * $Log: pdfwriter.cc,v $ * Revision 1.15 2007/05/04 09:26:00 bilboq * * fixed bad formating strings on 64bit machines (%u printing size_t ...) * * Revision 1.14 2007/02/04 20:17:02 mstsxfx * Common Licence comment for all cc and h files available in doc/licence_header * file and its content to all cc and h files in src/{gui,kernel,utils} * directories. * Xpdf code, QSA and QOutputDevice code are not touched * * Revision 1.13 2006/06/29 20:00:08 hockm0bm * doc updated * * Revision 1.12 2006/06/18 12:04:42 hockm0bm * obsevers code clean up and consolidation * * Revision 1.11 2006/06/13 20:44:14 hockm0bm * * ChangeContextType enum removed from class IChangeContext to observer namespace * * pdfwriter.cc synced with ChangeContextType change * * Revision 1.10 2006/06/05 22:28:29 hockm0bm * * IProgressBar interface added * * ProgressObserver implemented * * Revision 1.9 2006/05/29 16:34:16 hockm0bm * * writeContent * - uses writeIndirectObject method for object storing * * writeTrailer * - uses writeDirectObject method for trailer storing * * writeIndirectObject * writeDirectObject methods added * - writes xpdf objects correctly - strings are escaped and can contain * 0 bytes, stream objects are also ok * - Streams are used directly * - other types objects are transformed to IProperty and use * getStringRepresentation * * Revision 1.8 2006/05/29 10:30:04 hockm0bm * writeContent handles string with CharBuffer too * * Revision 1.7 2006/05/23 19:04:03 hockm0bm * OldStylePdfWriter::writeTrailer * - signature changed returns position after stored xref section * - trims averything behind stored data * * Revision 1.6 2006/05/16 17:57:27 hockm0bm * * infrastructure for obserevers usable for IPdfWriter * - OperationStep, OperationScope structures * - PdfWriterObserver and PdfWriterObserverContext types * * OldStylePdfWriter uses observers to provide progress information * * writeTrailer method changed * - lastXref parameter replaced by PrevSecInfo structure * - Trailer Prev field removed if PrevSecInfo::xrefPos==0 * - Trailer Size field is set to proper value * * Revision 1.5 2006/05/14 14:02:00 hockm0bm * quick fix * - CharBuffer handling changed (uses CharBuffer.get rather than * operator and casting) * * Revision 1.4 2006/05/14 12:34:01 hockm0bm * support for special writing of stream objects * * Revision 1.3 2006/05/13 22:19:29 hockm0bm * isInValidPdf refactored to hasValidPdf or isPdfValid functions * * Revision 1.2 2006/05/09 20:10:55 hockm0bm * * doc update * * writeContent some checking added * - duplicated entries are ignored * - NULL entries are ignored * * Revision 1.1 2006/05/08 20:12:18 hockm0bm * * abstract IPdfWriter class for pdf content writers * * OldStylePdfWriter implementation of IPdfWriter * * pdf key words defined here now * * */ #include "xpdf.h" #include "static.h" #include "pdfwriter.h" #include "cobject.h" /** Size of buffer for xref table row. * This includes also 1 byte for trailing '\0' (end of string marker). */ #define XREFROWLENGHT 21 const char * PDFHEADER="%PDF-"; const char * TRAILER_KEYWORD="trailer"; const char * XREF_KEYWORD="xref"; const char * STARTXREF_KEYWORD="startxref"; const char * EOFMARKER="%%EOF"; namespace pdfobjects { namespace utils { void ProgressObserver::notify(boost::shared_ptr newValue, boost::shared_ptr > context)const throw() { using namespace boost; using namespace observer; // if no progress visualizator is set or no context is provided, // immediatelly returns if(!progressBar || !context.get()) return; // gets current step and context - which has to have // ScopedChangeContextType size_t currStep=newValue->currStep; if(context->getType()!=ScopedChangeContextType) { printf("Unsupported context.\n"); return; } shared_ptr progressContext= dynamic_pointer_cast(context); size_t total=progressContext->getScope()->total; if(!started) { // progress has just started // starts progress, sets maximum steps and mark progress as started progressBar->start(); progressBar->setMaxStep(total); started=true; } // updates progress bar progressBar->update(currStep); // handles last step if(currStep==total) { started=false; progressBar->finish(); } } /** Helper method for xpdf direct object writing to the stream. * @param obj Xpdf object to write. * @param stream Stream where to write. * * Creates correct pdf string representation of given object and writes * everything to the given stream. Given xpdf object data (like stream * or string) can contain unprintable or 0 bytes. */ void writeDirectObject(::Object & obj, StreamWriter & stream) { using namespace boost; using namespace std; // stream requires special handling, because it may // contain binary data if(obj.isStream()) { CharBuffer charBuffer; ::Ref ref={0,0}; size_t size=streamToCharBuffer(obj, ref, charBuffer, false); stream.putLine(charBuffer.get(), size); }else { // converts xpdf object to cobject and gets correct string // representation scoped_ptr cobj_ptr(createObjFromXpdfObj(obj)); string objPdfFormat; cobj_ptr->getStringRepresentation(objPdfFormat); // objPdfFormat may contain 0 bytes, so we can't use c_str() // method and have to copy all bytes to the CharBuffer (which also // handles correct deallocation) size_t bufSize=objPdfFormat.length(); char * buf=char_buffer_new(bufSize); CharBuffer charBuffer(buf, char_buffer_delete()); for(size_t i=0; i cobj_ptr(createObjFromXpdfObj(obj)); string objPdfFormat; cobj_ptr->getStringRepresentation(objPdfFormat); // we have to add some more information to write indirect // object (this includes header and footer) std::string indirectFormat; IndiRef indiRef(ref); createIndirectObjectStringFromString(indiRef, objPdfFormat, indirectFormat); // indirectFormat may contain 0 bytes, so we can't use c_str() // method and have to copy all bytes to the CharBuffer (which also // handles correct deallocation) size_t bufSize=indirectFormat.length(); char * buf=char_buffer_new(bufSize); CharBuffer charBuffer(buf, char_buffer_delete()); for(size_t i=0; imaxObjNum) maxObjNum=ref.num; // associate given reference with current position. size_t objPos=stream.getPos(); offTable.insert(OffsetTab::value_type(ref, objPos)); writeIndirectObject(*obj, ref, stream); utilsPrintDbg(DBG_DBG, "Object with "< EntryType; typedef vector EntriesType; typedef map< int , EntriesType> SubSectionTab; // creates subsection offTable from offset offTable: // Starts with first element from offset offTable - inserts num as key and gen // and offset as entry pair. SubSectionTab subSectionTable; SubSectionTab::iterator sub=subSectionTable.begin(); utilsPrintDbg(DBG_DBG, "Creating subsection offTable"); // goes through rest entries of offset offTable for(OffsetTab::iterator i=offTable.begin(); i!=offTable.end(); i++) { int num=(i->first).num; int gen=(i->first).gen; size_t off=i->second; // skips not assigned subsection if(sub!=subSectionTable.end()) { // if num can be added to current sub, appends entries array // NOTE: num can be added if sub's key+entries size == num, which // means that entries array won't destroy sequence without holes // condition after addition if((size_t)num == sub->first + (sub->second).size()) { utilsPrintDbg(DBG_DBG, "Appending num="< scope(new OperationScope()); scope->total=subSectionTable.size(); scope->task=TRAILER; shared_ptr context(new ChangeContext(scope)); // writes all subsection size_t index=1; for(SubSectionTab::iterator i=subSectionTable.begin(); i!=subSectionTable.end(); i++, index++) { // at first writes head object number and number of elements in the // subsection EntriesType & entries=i->second; int startNum=i->first; utilsPrintDbg(DBG_DBG, "Starting subsection with startPos="<first, entry->second); stream.putLine(xrefRow); } // notifies observers shared_ptr newValue(new OperationStep()); newValue->currStep=index; notifyObservers(newValue, context); } // updates Prev field - to say where previous xref table starts // if 0, removes Prev entry if present if(!prevSection.xrefPos) { Object * prev=trailer.getDict()->del("Prev"); if(prev) freeXpdfObject(prev); utilsPrintDbg(DBG_DBG, "No previous xref section. Removing Trailer::Prev."); }else { Object newPrev; newPrev.initInt(prevSection.xrefPos); char * key=strdup("Prev"); Object * originalPrev=trailer.getDict()->update(key, &newPrev); if(originalPrev) { // value has been set to something different, we have to deallocate it // and to free key, because it is not stored in update utilsPrintDbg(DBG_DBG, "Removing old Trailer::Prev="<getInt()); free(key); freeXpdfObject(originalPrev); } utilsPrintDbg(DBG_DBG, "Linking to previous xref section. Trailer::Prev="<update(key, &newSize); if(originalSize) { // value has been set to something different, we have to deallocate it // and to free key, because it is not stored in update utilsPrintDbg(DBG_DBG, "Removing old Trailer::Size="<getInt()); free(key); freeXpdfObject(originalSize); } utilsPrintDbg(DBG_DBG, "Setting Trailer::Size="<currPos) { size_t size=eofPos-currPos; kernelPrintDbg(DBG_DBG, "Cleaning pending ("<